This document provides an introduction and overview of Moose, a modern object framework for Perl 5. It begins with background on the author and a brief history of object oriented programming in Perl 5. It then explains what Moose is, including that it handles object overhead, allows for introspection, and is used in production software. Examples are provided of basic class creation and usage with Moose as well as more advanced features like attributes, types, subclassing, roles, method modifiers, and introspection. The benefits of Moose are summarized as writing less code and avoiding implementation details to have a better object model.
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};
} }
}
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
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)
package Student;
# 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
54. Roles
• Code fragments that define and provide a
small, reusable behaviour
• Not inherited - methods become part of
consuming class
55. 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
56. 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!
57. Example role
package Age;
use Moose::Role;
has dob => (
isa => 'DateTime', is =>'ro'
);
sub age {
return DateTime->now
->subtract( shift->dob() )
->years;
}
58. 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;
}
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 really like
use Student;
my $person = Student->new(
name => 'Mike',
dob => '05-08-1963',
course => 'CS',
);
62. Types to the rescue
use Moose::Util::TypeConstraints;
subtype 'DateStr'
=> as 'Str'
=> where {
/^dd-dd-dddd$/
};
63. 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
);
};
64. And then...
package Age;
use Moose::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
package Earnings;
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;
}
70. Role gotchas
• Roles cannot use extends
• requires only works with actual methods :(
71. Role gotchas
• Roles cannot use extends
• requires only works with actual methods :(
• for now, at least
72. Role gotchas
• Roles cannot use extends
• requires only works with actual methods :(
• for now, at least
• watch out for method and attribute
conflicts
73. 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,
);
74. Method Delegation (2)
package Graduate;
use Moose;
extends qw/Student/;
has 'degree' => (
isa => 'Degree',
is => 'rw',
);
78. 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',
);
95. Even more Java-like?
use MooseX::Declare;
class Student extends Person with
Earnings {
has 'id' => ( isa => 'StudentID' );
method attend ( Str $class, ... ) {
# yadda yadda
}
}
103. 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...
106. Why Moose?
• Less code = fewer errors
• Don't waste time on class implementation
107. Why Moose?
• Less code = fewer errors
• Don't waste time on class implementation
• Better object model
108. 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"
109. 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
110. 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
117. 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',
);
118. Complex Example (3)
sub _build__schema {
my $self = shift;
return Person::Schema->connect(
$self->db
);
}
119. 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};
};
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?
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 if the student's ID changes?
has 'id' => (
isa => 'StudentID',
is => 'rw',
trigger => '_build__row',
);