use Moo;
is equivalent to
use strict; # strict vars, refs, subs
use warnings; # in Moo v2+
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
new()
to just create an object.is called when the object is destroyed
package Alien;
use Moo;
sub DEMOLISH {
my ($self, $in_global_destruction) = @_;
$self->dbh->disconnect;
}
my $alien = Alien->new(@args)
$alien->DESTROY()
undef $alien
$alien->DESTROY()
$alien->DESTROY()
calls $alien->DEMOLISH()
at every level of inheritance
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
ro
attributesrw
when you need that
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 Types::URI -all;
has uri => (is => 'ro', isa => Uri);
use Types::Standard -all;
has is_from_mars => (is => 'ro', isa => Bool);
has tentacles => (is => 'ro', isa => ArrayRef[InstanceOf["Tentacle"]]);
package MyCompany::Types;
use Type::Library -base;
use Type::Utils -all;
BEGIN { extends "Types::Standard" };
declare "PurpleTentacles",
as ArrayRef[InstanceOf["Tentacles"]],
where {
my $tentacles = $_;
for my $tentacle (@$tentacles) {
return 0 if $tentacle->color ne "purple";
}
return 1;
};
use MyCompany::Types -all;
has tentacles => (is => 'ro', isa => PurpleTentacles);
Unlike Moose, coercions are separate from type checking
No solution that worked with both Moose and Moo until recently
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
Type::Tiny coercions come packaged with types (like in Moose)
package Client;
use Moo;
use Types::URI -all;
has uri => (is => 'ro', isa => URI, coerce => 1);
my $client = Client->new( uri => 'http://example.com' );
$client->uri->host; # returns example.com
package MyCompany::Types;
use Type::Library -base;
use Type::Utils -all;
BEGIN { extends "Types::Standard" };
declare "Greeting",
as "Str",
where { $_ =~ /Take me to your leader.$/ };
coerce "Greeting",
from "Str",
via { $_ .= ' Take me to your leader.' };
Too many for me to cover here. See also:
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;
use Moo;
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;
use Moo;
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');