Title: An Intro to Moose Location: Minneapolis Perl Mongers Presenter: Chris Prather Organization: Infinity Interactive Date: September 12, 2007 Theme: moose What Moose Is Not ======= * An experiment or prototype * A toy * Just another accessor builder * A source filter or some other kind of Perl black magic * A implementation of Perl 6 in Perl 5 ✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------ What Moose Is ============= * A complete modern object framework for Perl * Syntactic Sugar for Class::MOP * Written by Stevan Little * Based on the work done by Stevan and others for Pugs and Perl 6 * Influenced by CLOS or Common Lisp Object System * Stable and used in Production systems * Stevan declared Moose as ready for prime time on April 3, 2007 at version 0.18 * We use Moose in large applications for household name clients * Currently at version 0.25 with 0.26 in SVN ✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------ A Simple Example ================
package Person;
use strict;
use warnings;
sub new {
my ($class) = @_;
return bless {
name => '',
age => undef,
}, $class;
}
sub name {
my ($self, $name) = @_;
$self->{'name'} = $name if $name;
return $self->{'name'};
}
sub age {
my ($self, $age) = @_;
$self->{'age'} = $age if $age;
return $self->{'age'};
}
1;
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
A Simple Moose Example
======================
package Person;
use Moose;
has name => (is => 'rw');
has age => (is => 'rw');
1;
* `use Moose;`
* imports `has, extends, with, before, after, around, super, override, inner, augment`
* turns on `use strict;` and `use warnings;`
* `Carp::confess` and `Scalar::Util::blessed`
* sets `@ISA` to `Moose::Object` (unless it's already set)
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
A Simple Moose Example (cont.)
==============================
package Person;
use Moose;
has name => (is => 'rw');
has age => (is => 'rw');
1;
* `has` declares attributes
* Moose will automatically create the proper accessors
* `is => 'rw'` create read/write accessor
* `is => 'ro'` create read only accessor
* Also note no constructor
* `Moose::Object` provides one for us
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Variations on a Moose Example
=============================
package Person;
use Moose;
has name => (
is => 'rw',
isa => 'Str'
default => 'Bob'
);
has age => (
is => 'rw',
isa => 'Int',
default => '0'
);
1;
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Variations on a Moose Example (cont.)
=====================================
has name => (
is => 'rw',
isa => 'Str'
default => 'Bob'
);
* `isa` sets up Type checking
* Types are defined in `Moose::Util::TypeConstraints`.
* Standard type constraints: `Any, Item, Bool, Undef, Defined, Value, Num, Int, Str, Ref, ScalarRef, ArrayRef, HashRef, CodeRef, RegexpRef, GlobRef, FileHandle, Object and Role`
* Type constraints are subtype-able
* Unknown types are assumed to be a Class and an anonymous subtype will be created
has 'date' => (isa => 'DateTime'); # This will DWIM
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Variations on a Moose Example (cont.)
=====================================
has name => (
is => 'rw',
isa => 'Str'
default => 'Bob'
);
has staff => (
is => 'ro',
isa => 'ArrayRef',
default => sub { [qw(Bob Alice Tim)] },
);
* `default` values are either ...
* non-references (numbers, strings)
* subroutine references
* `default` sub is passed `$self`
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Extending the Example
=====================
package Person;
use Moose;
has name => (is => 'rw');
has age => (is => 'rw');
package Employee;
use Moose;
extends qw(Person);
has manager => (
is => 'ro',
isa => 'Manager',
required => 1, # Everybody has to have a manager
);
package Manager;
use Moose;
extends qw(Employee);
has staff => (
is => 'rw',
isa => 'ArrayRef',
default => sub { [] },
);
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Extending the Example (cont.)
=============================
package Employee;
use Moose;
extends qw(Person);
has manager => (
is => 'ro',
isa => 'Manager',
required => 1, # Everybody has to have a manager
);
* `extends` declares the parent classes
* `extends` is preferred over `use base` because it fully replaces the `@ISA` rather than extending it
* it defines _the_ inheritance of the class, not an inheritance of the class
* this gurantees no hidden surprises, like classes not properly inheriting from `Moose::Object`
* it also happens to work better with the coming Perl 5.10 release
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Some Type of Coercion
=====================
package Employee;
use Moose;
use Moose::Util::TypeConstraints;
extends qw(Person);
subtype 'Manager'
=> as 'Object'
=> where { $_[0]->isa('Manager') };
coerce 'Manager'
=> from 'Str'
=> via { Manager->new( name => $_) };
has manager => (
is => 'ro',
isa => 'Manager',
required => 1,
coerce => 1,
);
* Moose can convert from one type to another ... with a little help.
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Some Type of Coercion (cont.)
=============================
use Moose::Util::TypeConstraints;
* Make sure you use the TypeConstraints utilities
subtype 'Manager'
=> as 'Object'
=> where { $_[0]->isa('Manager') };
* You have to define your subtype(s)
* Types are global so you only need to do this once
coerce 'Manager'
=> from 'Str'
=> via { Manager->new( name => $_) };
* Then you tell Moose how to convert from one type to another
has manager => (
...
coerce => 1,
);
* Finally you tell Moose which attributes to coerce
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Role of the Moose
=================
package TeamMember;
use Moose;
extends qw(Employee);
with qw(ProjectTeamRole);
has 'weekly_hours' => (
is => 'ro',
isa => 'Int',
default => 40,
);
1;
* `with` adds a Role (`ProjectTeamRole`) to the Class (`TeamMember`)
* A Role is a part of a Class that is composed into the class during Class creation ... basically a Mix-In
* Similar in concept to Java's Interfaces
* In fact Interfaces are a simplified case of Roles
* Roles are much more powerful
* `Moose::Object` provides a `does()` method to check objects for Roles
$emp->does('ProjectTeamRole'); # true for TeamMembers
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Role of the Moose (cont.)
=========================
package ProjectTeamRole;
use Moose::Role;
requires qw('weekly_hours');
has project => (
is => 'ro',
isa => 'Str',
required => 1,
)
has project_hours => (
is => 'rw',
isa => 'Int',
default => 0,
)
sub log_hours {
my ($self, $hours) = @_;
my $total = $self->project_hours + $hours;
$self->project_hours($total);
}
1;
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Role of the Moose (cont.)
=========================
package ProjectTeamRole;
use Moose::Role;
requires qw('weekly_hours');
* `use Moose::Role`
* imports everything from Moose plus `requires` and `excludes`
* `requires` says that the composing class must have a `weekly_hours` method
* Note that Roles are only part of a Class
* They cannot inherit from parent classes ... `extends` throws an exception
* All other Moose keywords are deferred until the Class creation
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Role of the Moose (cont.)
=========================
has project => (
is => 'ro',
isa => 'Str',
required => 1,
)
has project_hours => (
is => 'rw',
isa => 'Int',
default => 0,
)
sub log_hours {
my ($self, $hours) = @_;
my $total = $self->project_hours + $hours;
$self->project_hours($total);
}
* Roles can provide state
* We add two more attributes to our class `project` and `project_hours`
* Roles also provide mix-in methods
* `log_hours` basically increments the `project_hours` by a given amount
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Role of the Moose (cont.)
=========================
* There are several pre-baked Roles on the CPAN that provide various features
* `MooseX::Storage` - Serialization to JSON or YAML
* `MooseX::Daemonize` - Daemonization and handling of pid files
* `MooseX::Getopt`- Command Line Parsing
* `MooseX::IOC` - Support for Inversion of Control paradigm
* `MooseX::Param` - Provide a `param()` method similar to CGI.pm's
* And Several more in the Moose Repository waiting to be released
* `MooseX::Workers` - forked process management for concurrently running tasks
* `MooseX::Service` - Turns your class into a lazy service locator
* `MooseX::LogDispatch` - Provide a standard Logging object
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Benefits of Moose
=================
* Code is less tedious
* no need to worry about the basic mechanics of OO like:
* object initialization order
* object destruction order
* attribute storage, access and initialization
* less tedium means many (sloppy) typo errors are all but eliminated
* Code is shorter
* Moose's declarative style allows you say more with less
* less code == less bugs
* Less low-level testing needed
* no need to verify things which are already checked by the Moose test suite
* tests can focus on what your class does, not that it is "assembled" correctly
* Code becomes more descriptive
* Moose's declarative style requires you to provide more descriptive information
* Code describes your intentions more and OO mechanics less
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Drawbacks of Moose
==================
* Moose has a fairly heavy compile-time cost.
* Not appropriate for non-persistent environments (vanilla-CGI, etc.)
* We are looking into using .pmc files to reduce this burden.
* Some Moose features can be slow at times.
* However speed is directly proportional to the amount of features used.
* Nothing in life is free.
* Extending non-Hash based classes is tricky.
* The most common example is the IO::* family of class.
* Stevan recommends Class::InsideOut or Object::InsideOut for this (or delegation, however ...).
* Some delegation features do not play well with AUTOLOAD
* AUTOLOAD is evil anyway :)
✂------✂------✂------✂------✂------✂------✂------✂------✂------✂------
Le Fin
======
* Special thanks to
* Stevan and Robert Boone who's previous talks formed the foundation of this one
* Matt Trout (mst), Yuval Kogman (nothingmuch), Robert Sedlacek (phaylon) and the rest of #moose
And you for coming