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',
);