Introduction to Moose
        Italian Perl Workshop 2009
  Mike Whitaker - BBC / EnlightenedPerl.org
About me
About me

• BBC iPlayer team
About me

• BBC iPlayer team
• ex-Yahoo Europe (News team)
About me

• BBC iPlayer team
• ex-Yahoo Europe (News team)
• ex-CricInfo.com
About me

• BBC iPlayer team
• ex-Yahoo Europe (News team)
• ex-CricInfo.com
• Board member of EnlightenedPerl.org
Why Moose?
Why Moose?


• For that, we need a bit of history:
In the Beginning...
In the Beginning...
• ...there was Perl 5
In the Beginning...
• ...there was Perl 5
• DIY OO
In the Beginning...
• ...there was Perl 5
• DIY OO
•   perldoc perltoot
In the Beginning...
• ...there was Perl 5
• DIY OO
•   perldoc perltoot


      package Foo;

      sub new {
        my $self = {};
        return bless $self;
      }
In the Beginning...
                              package Foo;

•   ...there was Perl 5       sub new {
                                my $self = {};

• DIY OO                      }
                                return bless $self;


•   perldoc perltoot          sub bar {
                                my $self = shift;
                                my $val = shift;
      package Foo;              if (defined $val) {
                                    $self->{bar} = $val;
      sub new {                 }
        my $self = {};          else {
        return bless $self;         return $self->{bar};
      }                         }
                              }
Perl 5 Native OO
Perl 5 Native OO

• A bit of a hack
Perl 5 Native OO

• A bit of a hack
• Not really a first-class part of the language
Perl 5 Native OO

• A bit of a hack
• Not really a first-class part of the language
• Have to roll your own
So...
People started writing classes to make it
                easier....
Class::Accessor::Classy         Class::Accessor
Object::InsideOut         fields
                                  Hash::FieldHash
       Badger::Class Rubyish
  OP                                    Object::Declare
           Class::Maker UR         Class::Gomor
 Object::Tiny::XS Class::XSAccessor
                                         Class::Framework
       Class::Accessor::Fast Class::Simple
 Rose::Object MOP::MOP                         Coat
    Object::InsideOut         Class::STD
                                          Class::Object
   Class::Base     Class::ArrayObjects
                                                 Spiffy
     Class::Accessor::Grouped Class::Frame
 Object::Accessor Object::Tiny           Object::Simple
Class::Generate       Class::Builder   accessors
     Fukurama::Class                      Class::Define
                     Abstract::Meta::Class
Moose
What is Moose?
What is Moose?
• (Post)modern Perl OO framework
What is Moose?
• (Post)modern Perl OO framework
• Deals with the overhead of implementing
  OO, allows you to get on with coding
What is Moose?
• (Post)modern Perl OO framework
• Deals with the overhead of implementing
  OO, allows you to get on with coding
• Meta-object implementation - allows you to
  introspect your classes and objects
What is Moose?
• (Post)modern Perl OO framework
• Deals with the overhead of implementing
  OO, allows you to get on with coding
• Meta-object implementation - allows you to
  introspect your classes and objects
• Already used in production software
My First Class
package Person;
use Moose;

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

no Moose;
Using the class
use Person;
my $person = Person->new();

$person->name('Mike');

print $person->name();
> Mike
Adding a method
package Person;
use Moose;

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

sub introduce {
    print "I'm " . $self->name;
}
Using it
use Person;
my $person = Person->new();

$person->name('Mike');

$person->introduce();
> I'm Mike
Read-only attributes
package Person;
use Moose;

has name => ( is => 'ro' );

sub introduce {
    print "I'm " . $self->name;
}
Read-only attributes (2)

use Person;
my $person = Person->new();

$person->name('Mike');
# boom!
Read-only attributes (3)

use Person;
my $person = Person->new(
    { name => 'Mike' },
);

# no boom today!
Changing defaults
has 'name' => (
    isa => 'Str',
    reader => 'get_name',
    writer => 'set_name',
    init_arg => 'name',
    required => 1,
);
Types
package Person;
use Moose;

has name => (
    is => 'rw',
    isa => 'Str',
);
Types (2)
use Person;
my $person = Person->new();

$person->name('Mike');
# shiny!

$person->name( [] );
# boom!
# Not a Str
Types (3)
package Person;
use Moose;

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

has dob => (
    is => 'rw',
    isa => 'DateTime',
);
Types (4)
use Person;
my $person = Person->new(
   { name => 'Mike' }
);
my $dob = DateTime->new (
   year => 1963, month => 8, day => 5,
);
$person->dob($dob);
print $person->dob->year;
> 1963
Subclassing
package Student;
use Moose;
extends qw/Person/;

has overdraft => (
    isa => 'Bool',
    is => 'rw',
);
Compound Types
package Student;
use Moose;
extends qw/Person/;

has classes => (
    isa => 'HashRef[Str]',
    is => 'rw',
);
Required attributes
package Student;
use Moose;
extends qw/Person/;
# ...
has course => (
    isa => 'Str',
    is => 'ro',
    required => 1,
);
Setting defaults
package Student;
use Moose;

has 'overdraft' => (
    isa => 'Bool',
    default => 1,
);
But, what if...

package BankAccount;

sub balance {
    # yadda yadda
}
And then...
package Student;
use Moose;
extends qw/Person/;

has 'account' => (
    isa => 'BankAccount',
    is => 'rw',
    required => 1,
);
Lazy default
has 'overdraft' => (
    isa => 'Bool',
    is => 'ro',
    lazy => 1,
    default => sub {
      shift->account->balance < 0;
    }
);
Lazy builder
has 'overdraft' => (
    isa => 'Bool',
    is => 'ro',
    lazy_build => 1,
    init_arg => undef,
);
sub _build_overdraft {
   return shift->account->balance < 0;
}
Method modifiers
package Student;
# yadda yadda

after 'introduce' => sub {
    my $self = shift;
    print ', studying ' . $self->course;
}
Using it
use Student;
my $person = Student->new(
    name => 'Mike',
    course => 'Computer Science',
);

$person->introduce();
> I'm Mike, studying Computer Science
Method modifiers (2)
package Student;
# yadda yadda

around 'introduce' => sub {
    my ($next, $self, @args) = @_;
    print "Hi, ";
    $self->$next(@args);
    print ', studying ' . $self->course;
}
Using around
use Student;
my $person = Student->new(
    name => 'Mike',
    course => 'Computer Science',
);

$person->introduce();
> Hi, I'm Mike, studying Computer Science
Roles
Roles
• Code fragments that define and provide a
  small, reusable behaviour
Roles
• Code fragments that define and provide a
  small, reusable behaviour
• Not inherited - methods become part of
  consuming class
Roles
• Code fragments that define and provide a
  small, reusable behaviour
• Not inherited - methods become part of
  consuming class
 • can be overriden by comsuming class
Roles
• Code fragments that define and provide a
  small, reusable behaviour
• Not inherited - methods become part of
  consuming class
 • can be overriden by comsuming class
 • like MI but better!
Example role
package Age;
use Moose::Role;
has dob => (
    isa => 'DateTime', is =>'ro'
);
sub age {
    return DateTime->now
     ->subtract( shift->dob() )
     ->years;
}
Using a role
package Person;
use Moose;
with qw/Age/;
has name => ( isa => 'Str', is => 'ro');

sub introduce {
    print "I'm " . $self->name .
       ', age ' . $self->age;
}
Using a role (2)
use Student;
my $person = Student->new(
    name => 'Mike',
    dob => ... # yadda yadda
    course => 'CS',
);

$person->introduce();
> Hi, I'm Mike, age 45, studying CS
What we'd really like
use Student;
my $person = Student->new(
    name => 'Mike',
    dob => '05-08-1963',
    course => 'CS',
);
Redefine our attr...
package Age;
use Moose::Role;
has dob => (
    isa => 'DateStr',
    is =>'ro',
);
Types to the rescue

use Moose::Util::TypeConstraints;

subtype 'DateStr'
    => as 'Str'
    => where {
        /^dd-dd-dddd$/
    };
Types to the rescue (2)
use Moose::Util::TypeConstraints;
class_type 'DateTime';

coerce 'DateTime' => from 'Str'
   => via {
      my ($d, $m, $y) = split /-/, $_;
      return DateTime->new(
        year => $y, month => $m, day => $d
     );
  };
And then...
package Age;
use Moose::Role;
has dob => (
    isa => 'DateTime',
    is =>'ro',
    coerce => 1, # very important!
);
Et voila...
use Student;
my $person = Student->new(
    name => 'Mike',
    dob => '05-08-1963',
    course => 'CS',
);

print $person->age();
> 45
Roles as interfaces
package Earnings;
use Moose::Role;

requires qw/annual_income/;

sub monthly_income {
    return shift->annual_income / 12;
}
Roles as interfaces (2)
package Person;
with qw/Earnings/;

package Student;
extends qw/Person/;

sub annual_income {
   my $self = shift;
   return $self->grant_amount;
}
Role gotchas
Role gotchas

• Roles cannot use extends
Role gotchas

• Roles cannot use extends
• requires only works with actual methods :(
Role gotchas

• Roles cannot use extends
• requires only works with actual methods :(
 • for now, at least
Role gotchas

• Roles cannot use extends
• requires only works with actual methods :(
 • for now, at least
• watch out for method and attribute
  conflicts
Method Delegation
package Degree;
use Moose;
use Moose::Utils::TypeConstraints;
enum 'Grade' => qw/I IIi IIii III/;


has 'grade' => ( isa => 'Grade' );
has 'awarded' => (
    isa => 'DateTime',
    coerce => 1,
);
Method Delegation (2)
package Graduate;
use Moose;
extends qw/Student/;

has 'degree' => (
   isa => 'Degree',
   is => 'rw',
);
Method Delegation (3)
my $g = Graduate->new(name => 'Mike');
my $d = Degree->new(
    awarded => '06-06-1985',
    grade => 'IIii',
);
$g->degree($d);

print $g->degree->awarded->year;
> 1985
Method Delegation (4)
has 'degree' => (
   isa => 'Degree',
   is => 'rw',
   handles => {
       graduated => 'awarded'
   },
);
Method Delegation (5)
my $g = Graduate->new(name => 'Mike');
my $d = Degree->new(
    awarded => '06-06-1985',
    grade => 'IIii',
);
$g->degree($d);

print $g->graduated->year;
> 1985
More on attributes
package Student;
use MooseX::Util::TypeConstraints;
use Moose;
extends qw/Person/;
enum 'Result' => qw/pass fail/;
has classes => (
    isa => 'HashRef[Result]',
    is => 'rw',
    predicate => 'has_classes',
);
Attribute Helpers
my $st = Student->new( name => 'Mike' );

$st->classes(
    { "Compilers" => 'fail',
     "Numerical Methods" => 'pass' }
);

%{$st->classes}->{"Lisp"} = "pass";
Attribute Helpers (2)
use MooseX::AttributeHelpers;
has classes => (
    metaclass => 'Collection::Hash',
    isa => 'HashRef[Result]',
    is => 'rw',
    provides => {
        set => 'add_class',
        keys => 'list_classes',
    }
)
Attribute Helpers (3)
my $st = Student->new( name => 'Mike' );

$st->classes(
    { "Compilers" => 'fail',
      "Numerical Methods" => 'pass' }
);

$st->add_class("Lisp" => 'pass');
print join ", ", $st->list_classes();
> Compilers, Numerical Methods, Lisp
Introspecting Moose
Introspecting Moose
• my $metaclass = $self->meta;
Introspecting Moose
• my $metaclass = $self->meta;
Introspecting Moose
• my $metaclass = $self->meta;

• $metaclass->superclasses;
Introspecting Moose
• my $metaclass = $self->meta;

• $metaclass->superclasses;
• $metaclass->linearized_isa;
Introspecting Moose
• my $metaclass = $self->meta;

• $metaclass->superclasses;
• $metaclass->linearized_isa;
• $metaclass->has_method("foo");
Introspecting Moose
• my $metaclass = $self->meta;

• $metaclass->superclasses;
• $metaclass->linearized_isa;
• $metaclass->has_method("foo");
• $metaclass->get_all_attributes;
Method Signatures
Method Signatures

• We have types...
Method Signatures

• We have types...
• Wouldn't it be nice if types weren't
  restricted to attrs + accessors?
Method Signatures

• We have types...
• Wouldn't it be nice if types weren't
  restricted to attrs + accessors?
• Enter: MooseX::Method::Signature
Method Signatures
package Student;
use Moose;
use MooseX::Method::Signatures;

method attend (Str $class, DateTime
$time) {
     # ...
}
Method Signatures (2)
method attend
  (Str $class, DateTime $time) {
    if (grep $class,
        $self->list_classes) {
        $self->schedule($time, $class);
    }
}
Even more Java-like?
use MooseX::Declare;

class Student extends Person with
Earnings {
   has 'id' => ( isa => 'StudentID' );

    method attend ( Str $class, ... ) {
        # yadda yadda
    }
}
How is this
implemented?
Lasciate ogne
  speranza,
voi ch'intrate
But seriously...
But seriously...

• Devel::Declare sticks the interpreter on
  pause while it plays with your source
But seriously...

• Devel::Declare sticks the interpreter on
  pause while it plays with your source
• NOT a source filter
But seriously...

• Devel::Declare sticks the interpreter on
  pause while it plays with your source
• NOT a source filter
• You don't need to know what's going on...
Why Moose?
Why Moose?
• Less code = fewer errors
Why Moose?
• Less code = fewer errors
• Don't waste time on class implementation
Why Moose?
• Less code = fewer errors
• Don't waste time on class implementation
 • Better object model
Why Moose?
• Less code = fewer errors
• Don't waste time on class implementation
 • Better object model
 • "We do this so you don't have to"
Why Moose?
• Less code = fewer errors
• Don't waste time on class implementation
 • Better object model
 • "We do this so you don't have to"
 • Less need for low level tests
Why Moose?
• Less code = fewer errors
• Don't waste time on class implementation
 • Better object model
 • "We do this so you don't have to"
 • Less need for low level tests
• More descriptive code
Further Reading
Further Reading

• http://iinteractive.com/moose/
Further Reading

• http://iinteractive.com/moose/
• Moose::Cookbook (CPAN)
Further Reading

• http://iinteractive.com/moose/
• Moose::Cookbook (CPAN)
• http://www.stonehenge.com/merlyn/
  LinuxMag/col94.html
Complex Example
package Student;
use Moose;

# yadda

has 'id' => (
     isa => 'StudentID',
     is => 'rw',
);
Complex Example (2)
use MooseX::ClassAttribute;

class_has 'db' => (
    isa => 'Str', is => 'rw',
    default => 'dbi:SQLite:dbname=s.db'
);
class_has _schema => (
    isa => 'Person::Schema',
    lazy_build => 1, is => 'ro',
);
Complex Example (3)

sub _build__schema {
    my $self = shift;
    return Person::Schema->connect(
        $self->db
    );
}
Complex Example (4)
use Config::General;

override BUILDARGS => sub {
    my $args = super;

     my %conf = Config::General
         ->new( 'db.conf' )->getall();
     $args->{db} = $conf{db}
         if exists $conf{db};
};
Complex Example (5)
has _row => {
    isa => 'DBIx::Class::Row',
    lazy_build => 1,
}
sub _build__row {
    return shift->_schema
        ->resultset('Student')
        ->find($self->id);
}
Complex Example (6)

has _row => {
    isa => 'DBIx::Class::Row',
    lazy_build => 1,
    handles => [qw/address gender/],
}
Complex Example (7)
my $student = Student->new(
     name => 'Mike', id => '3056',
);

print $st->address();

# What happens here?
What happens?
What happens?
•   address method is handled by _row
What happens?
•   address method is handled by _row

•   _row is lazy_build, so calls _build__row
What happens?
•   address method is handled by _row

•   _row is lazy_build, so calls _build__row

• which requires _schema, ALSO
    lazy_build
What happens?
•   address method is handled by _row

•   _row is lazy_build, so calls _build__row

• which requires _schema, ALSO
    lazy_build

• which uses id to find the right row in db
What happens?
•   address method is handled by _row

•   _row is lazy_build, so calls _build__row

• which requires _schema, ALSO
    lazy_build

• which uses id to find the right row in db
•   Tadaa!
But...
# What if the student's ID changes?

has 'id' => (
    isa => 'StudentID',
    is => 'rw',
    trigger => '_build__row',
);

Introduction To Moose

  • 1.
    Introduction to Moose Italian Perl Workshop 2009 Mike Whitaker - BBC / EnlightenedPerl.org
  • 2.
  • 3.
    About me • BBCiPlayer team
  • 4.
    About me • BBCiPlayer team • ex-Yahoo Europe (News team)
  • 5.
    About me • BBCiPlayer team • ex-Yahoo Europe (News team) • ex-CricInfo.com
  • 6.
    About me • BBCiPlayer team • ex-Yahoo Europe (News team) • ex-CricInfo.com • Board member of EnlightenedPerl.org
  • 7.
  • 8.
    Why Moose? • Forthat, we need a bit of history:
  • 9.
  • 10.
    In the Beginning... •...there was Perl 5
  • 11.
    In the Beginning... •...there was Perl 5 • DIY OO
  • 12.
    In the Beginning... •...there was Perl 5 • DIY OO • perldoc perltoot
  • 13.
    In the Beginning... •...there was Perl 5 • DIY OO • perldoc perltoot package Foo; sub new { my $self = {}; return bless $self; }
  • 14.
    In the Beginning... package Foo; • ...there was Perl 5 sub new { my $self = {}; • DIY OO } return bless $self; • perldoc perltoot sub bar { my $self = shift; my $val = shift; package Foo; if (defined $val) { $self->{bar} = $val; sub new { } my $self = {}; else { return bless $self; return $self->{bar}; } } }
  • 15.
  • 16.
    Perl 5 NativeOO • A bit of a hack
  • 17.
    Perl 5 NativeOO • A bit of a hack • Not really a first-class part of the language
  • 18.
    Perl 5 NativeOO • A bit of a hack • Not really a first-class part of the language • Have to roll your own
  • 19.
    So... People started writingclasses to make it easier....
  • 21.
    Class::Accessor::Classy Class::Accessor Object::InsideOut fields Hash::FieldHash Badger::Class Rubyish OP Object::Declare Class::Maker UR Class::Gomor Object::Tiny::XS Class::XSAccessor Class::Framework Class::Accessor::Fast Class::Simple Rose::Object MOP::MOP Coat Object::InsideOut Class::STD Class::Object Class::Base Class::ArrayObjects Spiffy Class::Accessor::Grouped Class::Frame Object::Accessor Object::Tiny Object::Simple Class::Generate Class::Builder accessors Fukurama::Class Class::Define Abstract::Meta::Class
  • 22.
  • 23.
  • 24.
    What is Moose? •(Post)modern Perl OO framework
  • 25.
    What is Moose? •(Post)modern Perl OO framework • Deals with the overhead of implementing OO, allows you to get on with coding
  • 26.
    What is Moose? •(Post)modern Perl OO framework • Deals with the overhead of implementing OO, allows you to get on with coding • Meta-object implementation - allows you to introspect your classes and objects
  • 27.
    What is Moose? •(Post)modern Perl OO framework • Deals with the overhead of implementing OO, allows you to get on with coding • Meta-object implementation - allows you to introspect your classes and objects • Already used in production software
  • 28.
    My First Class packagePerson; use Moose; has name => ( is => 'rw', ); no Moose;
  • 29.
    Using the class usePerson; my $person = Person->new(); $person->name('Mike'); print $person->name(); > Mike
  • 30.
    Adding a method packagePerson; use Moose; has name => ( is => 'rw' ); sub introduce { print "I'm " . $self->name; }
  • 31.
    Using it use Person; my$person = Person->new(); $person->name('Mike'); $person->introduce(); > I'm Mike
  • 32.
    Read-only attributes package Person; useMoose; has name => ( is => 'ro' ); sub introduce { print "I'm " . $self->name; }
  • 33.
    Read-only attributes (2) usePerson; my $person = Person->new(); $person->name('Mike'); # boom!
  • 34.
    Read-only attributes (3) usePerson; my $person = Person->new( { name => 'Mike' }, ); # no boom today!
  • 35.
    Changing defaults has 'name'=> ( isa => 'Str', reader => 'get_name', writer => 'set_name', init_arg => 'name', required => 1, );
  • 36.
    Types package Person; use Moose; hasname => ( is => 'rw', isa => 'Str', );
  • 37.
    Types (2) use Person; my$person = Person->new(); $person->name('Mike'); # shiny! $person->name( [] ); # boom! # Not a Str
  • 38.
    Types (3) package Person; useMoose; has name => ( is => 'rw', isa => 'Str' ); has dob => ( is => 'rw', isa => 'DateTime', );
  • 39.
    Types (4) use Person; my$person = Person->new( { name => 'Mike' } ); my $dob = DateTime->new ( year => 1963, month => 8, day => 5, ); $person->dob($dob); print $person->dob->year; > 1963
  • 40.
    Subclassing package Student; use Moose; extendsqw/Person/; has overdraft => ( isa => 'Bool', is => 'rw', );
  • 41.
    Compound Types package Student; useMoose; extends qw/Person/; has classes => ( isa => 'HashRef[Str]', is => 'rw', );
  • 42.
    Required attributes package Student; useMoose; extends qw/Person/; # ... has course => ( isa => 'Str', is => 'ro', required => 1, );
  • 43.
    Setting defaults package Student; useMoose; has 'overdraft' => ( isa => 'Bool', default => 1, );
  • 44.
    But, what if... packageBankAccount; sub balance { # yadda yadda }
  • 45.
    And then... package Student; useMoose; extends qw/Person/; has 'account' => ( isa => 'BankAccount', is => 'rw', required => 1, );
  • 46.
    Lazy default has 'overdraft'=> ( isa => 'Bool', is => 'ro', lazy => 1, default => sub { shift->account->balance < 0; } );
  • 47.
    Lazy builder has 'overdraft'=> ( isa => 'Bool', is => 'ro', lazy_build => 1, init_arg => undef, ); sub _build_overdraft { return shift->account->balance < 0; }
  • 48.
    Method modifiers package Student; #yadda yadda after 'introduce' => sub { my $self = shift; print ', studying ' . $self->course; }
  • 49.
    Using it use Student; my$person = Student->new( name => 'Mike', course => 'Computer Science', ); $person->introduce(); > I'm Mike, studying Computer Science
  • 50.
    Method modifiers (2) packageStudent; # yadda yadda around 'introduce' => sub { my ($next, $self, @args) = @_; print "Hi, "; $self->$next(@args); print ', studying ' . $self->course; }
  • 51.
    Using around use Student; my$person = Student->new( name => 'Mike', course => 'Computer Science', ); $person->introduce(); > Hi, I'm Mike, studying Computer Science
  • 52.
  • 53.
    Roles • Code fragmentsthat define and provide a small, reusable behaviour
  • 54.
    Roles • Code fragmentsthat define and provide a small, reusable behaviour • Not inherited - methods become part of consuming class
  • 55.
    Roles • Code fragmentsthat define and provide a small, reusable behaviour • Not inherited - methods become part of consuming class • can be overriden by comsuming class
  • 56.
    Roles • Code fragmentsthat define and provide a small, reusable behaviour • Not inherited - methods become part of consuming class • can be overriden by comsuming class • like MI but better!
  • 57.
    Example role package Age; useMoose::Role; has dob => ( isa => 'DateTime', is =>'ro' ); sub age { return DateTime->now ->subtract( shift->dob() ) ->years; }
  • 58.
    Using a role packagePerson; use Moose; with qw/Age/; has name => ( isa => 'Str', is => 'ro'); sub introduce { print "I'm " . $self->name . ', age ' . $self->age; }
  • 59.
    Using a role(2) use Student; my $person = Student->new( name => 'Mike', dob => ... # yadda yadda course => 'CS', ); $person->introduce(); > Hi, I'm Mike, age 45, studying CS
  • 60.
    What we'd reallylike use Student; my $person = Student->new( name => 'Mike', dob => '05-08-1963', course => 'CS', );
  • 61.
    Redefine our attr... packageAge; use Moose::Role; has dob => ( isa => 'DateStr', is =>'ro', );
  • 62.
    Types to therescue use Moose::Util::TypeConstraints; subtype 'DateStr' => as 'Str' => where { /^dd-dd-dddd$/ };
  • 63.
    Types to therescue (2) use Moose::Util::TypeConstraints; class_type 'DateTime'; coerce 'DateTime' => from 'Str' => via { my ($d, $m, $y) = split /-/, $_; return DateTime->new( year => $y, month => $m, day => $d ); };
  • 64.
    And then... package Age; useMoose::Role; has dob => ( isa => 'DateTime', is =>'ro', coerce => 1, # very important! );
  • 65.
    Et voila... use Student; my$person = Student->new( name => 'Mike', dob => '05-08-1963', course => 'CS', ); print $person->age(); > 45
  • 66.
    Roles as interfaces packageEarnings; use Moose::Role; requires qw/annual_income/; sub monthly_income { return shift->annual_income / 12; }
  • 67.
    Roles as interfaces(2) package Person; with qw/Earnings/; package Student; extends qw/Person/; sub annual_income { my $self = shift; return $self->grant_amount; }
  • 68.
  • 69.
    Role gotchas • Rolescannot use extends
  • 70.
    Role gotchas • Rolescannot use extends • requires only works with actual methods :(
  • 71.
    Role gotchas • Rolescannot use extends • requires only works with actual methods :( • for now, at least
  • 72.
    Role gotchas • Rolescannot use extends • requires only works with actual methods :( • for now, at least • watch out for method and attribute conflicts
  • 73.
    Method Delegation package Degree; useMoose; use Moose::Utils::TypeConstraints; enum 'Grade' => qw/I IIi IIii III/; has 'grade' => ( isa => 'Grade' ); has 'awarded' => ( isa => 'DateTime', coerce => 1, );
  • 74.
    Method Delegation (2) packageGraduate; use Moose; extends qw/Student/; has 'degree' => ( isa => 'Degree', is => 'rw', );
  • 75.
    Method Delegation (3) my$g = Graduate->new(name => 'Mike'); my $d = Degree->new( awarded => '06-06-1985', grade => 'IIii', ); $g->degree($d); print $g->degree->awarded->year; > 1985
  • 76.
    Method Delegation (4) has'degree' => ( isa => 'Degree', is => 'rw', handles => { graduated => 'awarded' }, );
  • 77.
    Method Delegation (5) my$g = Graduate->new(name => 'Mike'); my $d = Degree->new( awarded => '06-06-1985', grade => 'IIii', ); $g->degree($d); print $g->graduated->year; > 1985
  • 78.
    More on attributes packageStudent; use MooseX::Util::TypeConstraints; use Moose; extends qw/Person/; enum 'Result' => qw/pass fail/; has classes => ( isa => 'HashRef[Result]', is => 'rw', predicate => 'has_classes', );
  • 79.
    Attribute Helpers my $st= Student->new( name => 'Mike' ); $st->classes( { "Compilers" => 'fail', "Numerical Methods" => 'pass' } ); %{$st->classes}->{"Lisp"} = "pass";
  • 80.
    Attribute Helpers (2) useMooseX::AttributeHelpers; has classes => ( metaclass => 'Collection::Hash', isa => 'HashRef[Result]', is => 'rw', provides => { set => 'add_class', keys => 'list_classes', } )
  • 81.
    Attribute Helpers (3) my$st = Student->new( name => 'Mike' ); $st->classes( { "Compilers" => 'fail', "Numerical Methods" => 'pass' } ); $st->add_class("Lisp" => 'pass'); print join ", ", $st->list_classes(); > Compilers, Numerical Methods, Lisp
  • 82.
  • 83.
    Introspecting Moose • my$metaclass = $self->meta;
  • 84.
    Introspecting Moose • my$metaclass = $self->meta;
  • 85.
    Introspecting Moose • my$metaclass = $self->meta; • $metaclass->superclasses;
  • 86.
    Introspecting Moose • my$metaclass = $self->meta; • $metaclass->superclasses; • $metaclass->linearized_isa;
  • 87.
    Introspecting Moose • my$metaclass = $self->meta; • $metaclass->superclasses; • $metaclass->linearized_isa; • $metaclass->has_method("foo");
  • 88.
    Introspecting Moose • my$metaclass = $self->meta; • $metaclass->superclasses; • $metaclass->linearized_isa; • $metaclass->has_method("foo"); • $metaclass->get_all_attributes;
  • 89.
  • 90.
  • 91.
    Method Signatures • Wehave types... • Wouldn't it be nice if types weren't restricted to attrs + accessors?
  • 92.
    Method Signatures • Wehave types... • Wouldn't it be nice if types weren't restricted to attrs + accessors? • Enter: MooseX::Method::Signature
  • 93.
    Method Signatures package Student; useMoose; use MooseX::Method::Signatures; method attend (Str $class, DateTime $time) { # ... }
  • 94.
    Method Signatures (2) methodattend (Str $class, DateTime $time) { if (grep $class, $self->list_classes) { $self->schedule($time, $class); } }
  • 95.
    Even more Java-like? useMooseX::Declare; class Student extends Person with Earnings { has 'id' => ( isa => 'StudentID' ); method attend ( Str $class, ... ) { # yadda yadda } }
  • 96.
  • 98.
    Lasciate ogne speranza, voi ch'intrate
  • 100.
  • 101.
    But seriously... • Devel::Declaresticks the interpreter on pause while it plays with your source
  • 102.
    But seriously... • Devel::Declaresticks the interpreter on pause while it plays with your source • NOT a source filter
  • 103.
    But seriously... • Devel::Declaresticks the interpreter on pause while it plays with your source • NOT a source filter • You don't need to know what's going on...
  • 104.
  • 105.
    Why Moose? • Lesscode = fewer errors
  • 106.
    Why Moose? • Lesscode = fewer errors • Don't waste time on class implementation
  • 107.
    Why Moose? • Lesscode = fewer errors • Don't waste time on class implementation • Better object model
  • 108.
    Why Moose? • Lesscode = fewer errors • Don't waste time on class implementation • Better object model • "We do this so you don't have to"
  • 109.
    Why Moose? • Lesscode = fewer errors • Don't waste time on class implementation • Better object model • "We do this so you don't have to" • Less need for low level tests
  • 110.
    Why Moose? • Lesscode = fewer errors • Don't waste time on class implementation • Better object model • "We do this so you don't have to" • Less need for low level tests • More descriptive code
  • 111.
  • 112.
  • 113.
  • 114.
    Further Reading • http://iinteractive.com/moose/ •Moose::Cookbook (CPAN) • http://www.stonehenge.com/merlyn/ LinuxMag/col94.html
  • 116.
    Complex Example package Student; useMoose; # yadda has 'id' => ( isa => 'StudentID', is => 'rw', );
  • 117.
    Complex Example (2) useMooseX::ClassAttribute; class_has 'db' => ( isa => 'Str', is => 'rw', default => 'dbi:SQLite:dbname=s.db' ); class_has _schema => ( isa => 'Person::Schema', lazy_build => 1, is => 'ro', );
  • 118.
    Complex Example (3) sub_build__schema { my $self = shift; return Person::Schema->connect( $self->db ); }
  • 119.
    Complex Example (4) useConfig::General; override BUILDARGS => sub { my $args = super; my %conf = Config::General ->new( 'db.conf' )->getall(); $args->{db} = $conf{db} if exists $conf{db}; };
  • 120.
    Complex Example (5) has_row => { isa => 'DBIx::Class::Row', lazy_build => 1, } sub _build__row { return shift->_schema ->resultset('Student') ->find($self->id); }
  • 121.
    Complex Example (6) has_row => { isa => 'DBIx::Class::Row', lazy_build => 1, handles => [qw/address gender/], }
  • 122.
    Complex Example (7) my$student = Student->new( name => 'Mike', id => '3056', ); print $st->address(); # What happens here?
  • 123.
  • 124.
    What happens? • address method is handled by _row
  • 125.
    What happens? • address method is handled by _row • _row is lazy_build, so calls _build__row
  • 126.
    What happens? • address method is handled by _row • _row is lazy_build, so calls _build__row • which requires _schema, ALSO lazy_build
  • 127.
    What happens? • address method is handled by _row • _row is lazy_build, so calls _build__row • which requires _schema, ALSO lazy_build • which uses id to find the right row in db
  • 128.
    What happens? • address method is handled by _row • _row is lazy_build, so calls _build__row • which requires _schema, ALSO lazy_build • which uses id to find the right row in db • Tadaa!
  • 129.
    But... # What ifthe student's ID changes? has 'id' => ( isa => 'StudentID', is => 'rw', trigger => '_build__row', );