Post on 16-Apr-2017
Moose
Sawyer X
Sawyer X
blogs.perl.org/users/sawyer_x
search.cpan.org/~xsawyerx
metacpan.org/author/XSAWYERX
github.com/xsawyerx/
The Dancer guy!
Projects: Dancer, Module::Starter, MetaCPAN::API, MooseX::Role::Loggable, etc.
Moose
Object system
Metaclass-based
Advanced
Sophisticated
Extensible
Production-ready
but why Moose?
package Person;
use strict;use warnings;
use Carp qw( confess );use DateTime;use DateTime::Format::Natural;
sub new { my $class = shift; my %p = ref $_[0] ? %{ $_[0] } : @_;
exists $p{name} or confess 'name is a required attribute'; $class->_validate_name( $p{name} );
exists $p{birth_date} or confess 'birth_date is a required attribute';
$p{birth_date} = $class->_coerce_birth_date( $p{birth_date} ); $class->_validate_birth_date( $p{birth_date} );
$p{shirt_size} = 'l' unless exists $p{shirt_size}:
$class->_validate_shirt_size( $p{shirt_size} );
return bless \%p, $class;}
sub _validate_name { shift; my $name = shift;
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
defined $name or confess 'name must be a string';}
package User;
use Email::Valid;use Moose;use Moose::Util::TypeConstraints;
extends 'Person';
subtype 'Email' => as 'Str' => where { Email::Valid->address($_) } => message { "$_ is not a valid email address" };
has email_address => ( is => 'rw', isa => 'Email', required => 1,);
Get it?
Writing objects in basic Perl 5
Create a reference (usually to a hash)
Connect it to a package (using bless)
Provide subroutines that access the hash keys
Error check the hell out of it
Writing objects in basic Perl 5, e.g.
package Dog;use strict; use warnings;sub new { my $class = shift; my $self = bless {}, $class; return $self;}1;
Issues
Defining the same new() concept every time
No parameters for new() yet
Will have to do all error-checking manually
Moose
package Dog;
use Moose;
1;
You get
use strict;
use warnings;
new() method
To hell with ponies, you get a moose!(roll picture of a moose beating a pony in soccer)
Full affordance accessors (attributes)
has name => ( is => 'rw' );ro is available for read-only attributes
You can manually change setter/getter via writer/reader
Attributes can have defaults
Attributes can have type constraints
Attributes can have traits
Attributes can be lazy, have builders, clearers, predicates...
Type constraints
has name => ( is => 'ro', isa => 'Str' );Str, Int, Bool, ArrayRef, HashRef, CodeRef, Regex, Classes
Combine: ArrayRef|HashRef, ArrayRef[Str]
Derivatives (Int is a Num)
Create your own using subtype, available at Moose::Util::TypeConstraints
Methods
Same as beforesub run { my $self = shift; say qq{I make it a habit only to run when being chased!};}
Inheritance easy as...
package Punkuse Moose;extends 'Person';Multiple inheritance also possible (extends accepts array)package Child;use Moose;extends qw/Father Mother/;
Roles are even better
package Punk;use Moose;with qw/Tattoos Piercings Squatter/;
Roles are things you do, instead of things you are.
More hooks than a coat rack!
package User::WinterAware;use Moose;extends 'User';before leaving => sub { my $self = shift; $self->cold and $self->take_jacket;};
More hooks than a coat rack!
package User::Secure;use Moose;extends 'User';around login => sub { my $orig = shift; my $self = shift; $self->security_check and $self->$orig(@_);};
More hooks than a coat rack!
Before
After
Around
Inner
Augment
Back to attributes...
has set => ( is => 'rw', isa => 'Set::Object', default => sub { Set::Object->new }, required => 1, lazy => 1, predicate => 'has_set', clearer => 'clear_set', builder => 'build_set',);
Attribute options: default
default => 'kitteh' # stringdefault => 3 # numberdefault => sub { {} } # HashRefdefault => sub { [] } # ArrayRefdefault => sub { Object->new } # an Object
(if you need a more elaborate sub, use builder)
Attribute options: required
required => 1 # requiredrequired => 0 # not required
Attribute options: lazy
lazy => 1 # make it lazy
Class will not create the slot for this attribute unless it absolutely has to, defined by whether it is accessed at all.
No access? No penalty!Lazy == good
Attribute options: builder
builder => 'build_it' # subroutine name
sub build_it { my $self = shift; # not a problem! return Some::Object->new( $self->more_opts );}
# and, obviously...after build_it => sub { they will come };
Attribute options: clearer
clearer => 'clear_it' # subroutine name
# you don't need to create the subroutinesub time_machine { my $self = shift; $self->clear_it; # 'it' never happened :)}
- Clear the value (not back to default)
Attribute options: predicate
predicate => 'has_it' # subroutine name
# you don't need to create the subroutinesub try_to_do_it { my $self = shift; $self->has_it && $self->do_it();}
Checks an attribute value existsEven false valuesWhich is good
Attribute options: lazy_build
lazy_build => 1 # 1,builder => '_build_it', # privateclearer => 'clear_it',predicate => 'has_it',
Example: Comican
A hub for various comics strips
Allow you to fetch comic strips
Standard uniformed interface to add more comics
We'll be using:Roles
Lazy attributes
Overriding attributes options
Attribute predicates
Comican
Comican.pmComican::Comic::xkcdComican::Comic::PennyArcadeComican::Comic::DilbertComican::Role::Comicuser
main module
comic modules
interface role
Comican: overall
Comican.pm is the user's interface
It's a hub for Comican::Comic::* modules
They are objects that fetch specific comic strips
They have a common interface
Defined by Comican::Role::Comic
SHOW ME THE CODE!
/^M(?:o(?:o|[ou]se)?)?$/
Moose: standart
Mouse: subset, uses XS, faster
(Any::Moose: use Mouse unless Moose already loaded)
Moo: Pure-Perl, subset, blazing fast
Mo: As little as possible (incl. character count)
M: Really as little as possible
Hummus (HuMoose): Incompatible chickpeas paste
Thank you.