On Github kablamo / slides-2014-ddg-intro-to-moo
NOTE: This presentation is now out of date. Please see http://kablamo.org/slides-intro-to-moofor the most up to date version.
use Moo;
is equivalent to
use strict; # strict vars, refs, subs use warnings FATAL => 'all'; no indirect; # can't do `my $alien = new Alien;` no multidimensional; # can't do `$hash{1,2}` no bareword::filehandles; # can't do `open FH, $filename` use base 'Moo::Object'; # provides new(), etc
also use Moo imports some methods:
extends(), with(), has(), before(), around(), and after()
It inflates your Moo class to a Moose class
my $alien = Alien->new(@args)
new() calls Alien->BUILDARGS(@args) new() instantiates object, creating $self new() calls BUILD($self) new() returns $self
is called before instantiation
package Alien; use Moo; sub BUILDARGS { my ($class, @args) = @_; # -- modify @args here -- return { @args }; }
In Moose you need to call SUPER at the end
is called after instantiation
package Alien; use Moo; sub BUILD { my ($self, $args) = @_; # -- additional validation or logging here -- }
The return value is ignored
is called when the object is destroyed
package Alien; use Moo; sub DEMOLISH { my ($self, $in_global_destruction) = @_; $self->dbh->disconnect; }
package Alien; use Moo; has eyes => (is => 'rw'); has nostrils => (is => 'ro');
my $alien = Alien->new( nostrils => 20 ); $alien->eyes(10); # succeeds $alien->nostrils(10); # dies
package Alien; use Moo; has eyes => (is => 'ro', default => sub { 5 }); has nostrils => (is => 'ro', builder => '_build_nostrils'); # Perlism: methods that start with _ are private sub _build_nostrils { 5 }
has tentacles => (is => 'lazy');
is equivalent to
has tentacles => (is => 'ro', lazy => 1, builder => '_build_tentacles');
Use MooseX::AttributeShortcuts to get support for is => 'lazy' in Moose
is unpredictable
package Alien; use Moo; use Tentacle; has tentacle_count => (is => 'ro', default => sub { 5 }); has tentacles => (is => 'ro', builder => '_build_tentacles'); sub _build_tentacles { my $self = shift; my @tentacles; push @tentacles, Tentacle->new() for (1..$self->tentacle_count); return \@tentacles; }
package Alien; use Moo; use Tentacle; has tentacles => (is => 'lazy'); has tentacle_count => (is => 'ro', default => sub { 5 }); sub _build_tentacles { my $self = shift; my @tentacles; push @tentacles, Tentacle->new() for (1..$self->tentacle_count); return \@tentacles; }
use Scalar::Util qw/looks_like_number/; has antenna => ( is => 'ro', isa => sub { ref(shift) eq 'Antenna'}, ); has antenna_count => ( is => 'ro', isa => sub { looks_like_number(shift) }, );
use MooX::Types::MooseLike qw/:all/; has eyes => (is => 'ro', isa => Int);
use Types::URI -all; has uri => (is => 'ro', isa => Uri); use Types::Standard -all; has is_from_mars => (is => 'ro', isa => Bool);
use Scalar::Util qw(looks_like_number); use Type::Tiny; my $NUM = "Type::Tiny"->new( name => "Number", constraint => sub { looks_like_number($_) }, message => sub { "$_ ain't a number" }, ); has eyes => (is => 'ro', isa => $NUM);
Coercions are also a bit up in the air atm
Unlike Moose, coercions are separate from type checking
package Client; use Moo; use URI; has uri => (is => 'ro', coerce => \&string_to_uri } ); sub string_to_uri { my $value = shift; return $value if ref $value eq 'URI' return URI->new($value); }
my $client = Client->new( uri => 'http://example.com' ); $client->uri->host; # returns example.com
package ConfigFile; use Moo; use Path::Class; has _file => ( is => 'ro', handles => [qw/spew slurp/], default => sub { Path::Class::file('.configuration') }, );
my $config_file = ConfigFile->new(); $config_file->slurp(); # read in file contents
package ConfigFile; use Moo; use Path::Class; has _file => ( is => 'ro', handles => { write => 'spew', read => 'slurp'}, default => sub { Path::Class::file('.configuration') }, );
my $config_file = ConfigFile->new(); $config_file->read(); # slurp in file contents
A code reference run after an attribute is set
has cowboy => ( is => 'ro', trigger => sub { warn "cowboy attr set"; } );
package Brontosaurus; extends 'Dinosaur'; before eat => sub { my ($self, $food) = @_; die "bad params" if $food eq 'meat'; };
Receives same params as the original method
Return value is ignored
Good for adding extra validation
package Brontosaurus; extends 'Dinosaur'; after eat => sub { my ($self, $food) = @_; $self->log->warning("Brontosaurus does not like to eat $food") if $food eq 'meat'; };
Receives same params as the original method
Return value is ignored
Good for logging/debugging
package Brontosaurus; extends 'Dinosaur'; around eat => sub { my ($orig, $self, $food) = @_; uc $orig->($self, $food); };
Called instead of the original method
package Searchable; use Moo::Role; # state has 'number_of_results' => (is => 'ro', default => sub { 5 }); # behavior sub search { ... }
package Thing; use Moo; with qw/Searchable/;
my $thing = Thing->new(); $thing->search();
package Searchable; use Moo::Role; requires 'search_engine'; sub search { my ($self, $query) = @_; my $json = $self->search_engine->search($query); return JSON->new->utf8->decode($json); }
package Thing; use Moo; with qw/Searchable/; sub search_engine { return Bing->new(); }
print "hi" if Thing->does('Searchable');