Moose talk at FOSDEM 2011 (Perl devroom)

  • Upload
    xsawyer

  • View
    8.432

  • Download
    1

Embed Size (px)

Citation preview

Moose

A postmodernmetaclass-basedobject systemfor Perl 5

Moose

Sawyer X

* blogs.perl.org/users/sawyer_x* search.cpan.org/~xsawyerx/* search.metacpan.org/#/author/XSAWYERX* github.com/xsawyerx

Objects in Perl 5
(a short intro)

A blessed hashref

Manual new() method

No real attributes

bless {}, __PACKAGE__;

sub new {my ( $class, @args ) = @_;

my $self = { @args }; # paramsbless $self, $class;return $self;}

sub name {my ( $self, $name ) = @_;$name and$self->{'name'} = $name;

return $self->{'name'};}

Why would you want to use Moose?

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';}

Plain old Perl 5

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?

Defining an object in Moose

use strict

use warnings

An object!

new() method

A pon-.. err.. a moose!

package User;

use Moose;

1;

You get:

Full-Affordance accessors

'ro' is also available for read-only attributes!

You can set the getter or setter manually via reader/writer

I'll show more attribute options later on

has name => (is => 'rw',);

An attribute with type constraint

Lots of types: Str, Int, ArrayRef, HashRef, CodeRef, Regexp

You can combine: ArrayRef[Str], HashRef[ArrayRef[Int]]

They have inheritance: Int is a Num

Roll your own using subtype

package User;

use Moose;

has name => (is => 'rw',isa => 'Str',);

1;

Methods are the same as before

sub method {my $self = shift;

...

$self->more();}

Inheritance is as easy as...

Multiple inheritance is also possible, extends accepts an array

package Punk;

use Moose;extends 'Person';

1;

package Child;

use Moose;

extends qw/ Father Mother /;

1;

Roles are even easier!

Multiple roles are recommend! with accepts an array too

package Punk;

use Moose;extends 'Person';with 'Piercings';

1;

package Punk;

use Moose;extends 'Person';

with qw/PiercingsTattoos/;

1;

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;};

1;

before

after

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(@_);};

1;

before

after

around

inner

augment

Back to attributes options...

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 => 'kitteh', # stringdefault => 3, # numberdefault => sub { {} }, # HashRefdefault => sub { [] }, # ArrayRefdefault => sub { Object->new }, # an Object

etc. etc.

(if you need a more elaborate sub, use builder)

default

Attribute options

required => 1, # requiredrequired => 0, # not required

required

Attribute options

lazy => 1, # make it lazy

Class will not create the slot for this attribute unless it absolutely has to. That is defined by whether it is accessed at all.

Wasn't accessed? You don't pay the penalty! :)

lazy = good

lazy

Attribute options

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 };

(a builder sets the value of the attribute)

builder

Attribute options

clearer => 'clear_it', # subroutine name

# you don't need to create the subroutinesub time_machine {my $self = shift;$self->clear_it; # 'it' never happened :)}

(a clearer clears the value, as if it never existed)(does not go back to default)

clearer

Attribute options

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();}

(a predicate checks an attribute value exists)(even false values)(which is good!)

predicate

Attribute options

lazy_build => 1, # 1,builder => '_build_it', # privateclearer => 'clear_it',predicate => 'has_it',

(lazy_build is recommended)

lazy_build

Moose

It's beautiful

It's clean

It's stable

It's here to stay

Check it out!

A ton more options

More control

Roles / Traits