the same state - we say they share an interface ✦ Other classes interacting with the interface don’t “know” what exactly is there on the other side Interfaces Thursday, December 29, 2011
A method is just a subroutine ✦ An attribute is... we’ll get back to that use strict; use warnings; package Person; use Moose; has 'name', is => 'ro', isa => 'Str'; has 'age', is => 'rw', isa => 'Int'; package main; use feature ':5.10'; my $p = Person->new(name => "James"); say $p->name; Class Def Class Use Thursday, December 29, 2011
named Class.pm (capital first) ✦ Use a package declaration at the head of each file ✦ Use ->new to create a new object ✦ Use .t files to test your class Thursday, December 29, 2011
'ro', isa => 'Num', required => 1; sub go { my $self = shift; print "Vroom Vroom [speed: ", $self->speed, "]\n"; } package main; my $c = Car->new(speed => 10); $c->go; A Method takes the object as its first argument Calling is performed automatically by perl Thursday, December 29, 2011
attribute ✦ has takes attribute name and a list of options ✦ Can use parens or no parens. Usually use parens with multi line option lists Thursday, December 29, 2011
✦ Use optional ‘writer’ to specify writer name ✦ Use optional ‘reader’ to specify reader name package Product; use Moose; has 'name' => ( is => 'rw', reader => 'get_name', writer => '_set_name', ); has 'price' => ( is => 'rw', reader => 'get_price', writer => 'set_price', ); Thursday, December 29, 2011
isa => 'Str'; has 'clients', is => 'rw', isa => 'ArrayRef[Client]'; has 'products', is => 'rw', isa => 'ArrayRef[Product]'; has 'revenue', is => 'rw', isa => 'Num'; 1; Thursday, December 29, 2011
that as extra constraints use Moose::Util::TypeConstraints; subtype 'age', as 'Int', where { $_ >= 0 && $_ <= 120 }, message { "Invalid Age: $_ "}; Thursday, December 29, 2011
build logic ✦ A builder takes a method name as its value package Person; use Moose; has 'pet', is => 'ro', builder => '_build_pet'; has 'age', is => 'rw', required => 1; sub _build_pet { my $self = shift; if ( $self->age < 13 ) { return "None"; } else { return "Dog"; } } package main; my $p = Person->new(age => 10); print $p->pet; Thursday, December 29, 2011
has a builder, it will get called the first time the attribute is used. ✦ Lazy is highly recommended package Person; use Moose; has 'pet', is => 'ro', lazy_build => 1; has 'age', is => 'rw', required => 1; sub _build_pet { my $self = shift; if ( $self->age < 13 ) { return "None"; } else { return "Dog"; } } package main; my $p = Person->new(age => 10); print $p->pet; Thursday, December 29, 2011
can write to a file or the screen ✦ Use them as log destinations, for example: my $fw = FileWriter->new(file => ‘app.log’); my $sw = Screen->new; my $logger = Logger->new(writers => [$fw, $sw]); Thursday, December 29, 2011
'rw', isa => 'Bool'; sub break { my $self = shift; print "Ouch\n" if ! $self->is_broken; $self->is_broken(1); } sub fix { my $self = shift; print "Works now\n" if $self->is_broken; $self->is_broken(0); } package Glass; use Moose; with 'Breakable'; package main; my $g = Glass->new; $g->break; $g->fix; Thursday, December 29, 2011
Use ‘with’ to consume a role ✦ Inside a role, define methods and attributes ✦ Each class has a method ‘does’ that states if it consumes a given role Thursday, December 29, 2011
When a class consumes the role, it must provide full implementations of the missing methods package Writer; use Moose::Role; requires 'write'; package ScreenWriter; use Moose; with 'Writer'; sub write { print @_; } Thursday, December 29, 2011
✦ Can use a role as a type constraint ✦ Now only classes which consume the Writer role are allowed package Logger; use Moose; has 'writer' => ( is => 'ro', does => 'Writer', required => 1, ); package main; my $w = ScreenWriter->new; my $l = Logger->new(writer => $w); Thursday, December 29, 2011
numbers and detects if the list forms a series ✦ Use scenario on the right package main; use SeriesDetector; use SeriesDetector::Arithmetic; my $arith = SeriesDetector::Arithmetic- >new; my $detector = SeriesDetector->new(plugins => [$arith]); # success - returns 'Arithmetic' $detector->check(qw/1 2 3 4/); $detector->check(qw/10 20 30/); #fail - returns undef $detector->check(qw/5 7 11); Thursday, December 29, 2011
an attribute of another object ✦ When that happens, it’s possible to redirect all calls on containing object to the attribute Thursday, December 29, 2011
on an attribute ✦ Moose will create the proxy methods for you package Contact; use Moose; has 'email' => ( is => 'ro', handles => [ qw/send_mail/ ] ); Thursday, December 29, 2011
✦ Each method matches the regexp will be delegated to the attribute object has 'uri' => ( is => 'ro', isa => 'URI', handles => qr/^query.*)/, ); Thursday, December 29, 2011
✦ handles will automatically create methods for each method in the role has 'uri' => ( is => 'ro', isa => 'URI', handles => 'HasURI', ); Thursday, December 29, 2011
feel by creating proxy methods that just work for native data types ✦ Currently supported: Array, Hash, Number, String, Bool, Counter ✦ Useful when you need an object to “work like an array” Thursday, December 29, 2011
unshift ✦ Here we take push and shift, and build proxy methods for them automatically by moose ✦ Can use $q->add_item and $q->next_item has 'q' => ( is => 'ro', isa => 'ArrayRef[Int]', default => sub { [] }, traits => [qw/Array/], handles => { add_item => 'push', next_item => 'shift', }, ); package main; my $q = Queue->new; $q->add_item(10, 20); Thursday, December 29, 2011
ColoredCircle ✦ There can be multiple circles on a canvas, some of them may be colored. ✦ A ColoredCircle is a Circle with an extra color attribute ✦ See delegation/lab_starter.pl for usage Thursday, December 29, 2011
a method by injecting code before or after it is executed ✦ Can modify method from roles, or even from the class itself ✦ Works best in mixins Thursday, December 29, 2011
executed ✦ Takes two arguments: method name and the code to add package Secret; use Mouse; has 'message', is => 'ro', required => 1, clearer => 'reset'; has 'counter', is => 'rw', default => 3; after 'message' => sub { my $self = shift; $self->counter( $self->counter - 1 ); if ( $self->counter <= 0 ) { $self->reset; } }; package main; my $secret = Secret->new( message => 'This message will self destroy'); print $secret->message, "\n" for (1..5); Thursday, December 29, 2011
a method is executed ✦ The program on the right will log everything to standard error ✦ Can you spot the bug ? package Logger; use Mouse; sub log { my $self = shift; my ($msg) = @_; print $msg; } before 'log' => sub { select *STDERR }; after 'log' => sub { select *STDOUT }; package main; my $log = Logger->new; $log->log("hello\n"); Thursday, December 29, 2011
the code ✦ around modifier is far more powerful ✦ With around, you can manipulate function arguments and control the invocation of the original function Thursday, December 29, 2011
original method as a CODE ref ✦ The object ✦ around code must call: $self->$orig(@_) inside the wrapping (otherwise original method won’t get called) package AroundExample; use Mouse; use feature ':5.10'; sub foo { print "In Foo\n" } around 'foo' => sub { my $orig = shift; my $self = shift; say "Around: before calling method"; $self->$orig(@_); say "Around: after calling method"; }; Thursday, December 29, 2011
even called ✦ decision is based on environmental conditions package User; use Mouse; use DateTime; sub login { warn 'Welcome' } around 'login' => sub { my $now = DateTime->now; if ( $now->hour < 12 ) { my $orig = shift; my $self = shift; $self->$orig(@_); } }; Thursday, December 29, 2011
Be careful with this one not to wrap Moose’s functions ✦ Remember to call original function package Test; use Mouse; sub foo { warn 'In foo' } sub bar { warn 'In bar' } sub buz { warn 'In buz' } around qr/^(foo|bar|buz)$/ => sub { warn 'Before call'; my $orig = shift; my $self = shift; $self->$orig(@_); warn 'After call'; }; Thursday, December 29, 2011
methods behavior ✦ Use before and after to add some code “on top of” existing functionality ✦ Use around to completely alter functionality ✦ Q & A Thursday, December 29, 2011
in a file. Provide get($line), set ($line, $content), save() ✦ Write a PersistentDocument class that saves the file after each change ✦ Use the starter on the right my $d = Document->new('file1.txt'); my $p = PersistentDocument->new ('file2.txt'); # This one does not change file1.txt $d->set_line(2, 'This is the new line 2'); # This one changes file2.txt $p->set_line(2, 'This is the new line 2'); # Finally, this one changes file1.txt $d->save; Thursday, December 29, 2011