=> 'ro', isa => 'Str', default => 'Lassy'; has 'past_owners', is => 'ro', isa => 'ArrayRef[Str]'; package main; my $dog = Pet->new( past_owners => ['James', 'Mike'] ); # show dog's info. No need to import Data::Dumper warn $dog->dump; # DOES returns true for objects of the class, subclasses or # implementors of the role print "Good boy" if $dog->DOES('Pet'); Friday, July 6, 12
object is created • If inheritance is in effect, parent’s BUILD is called before child BUILD is called automatically • Used for: • Object state validation (whole object) • Tracking objects creation Friday, July 6, 12
=> 'ro', isa => 'Str', required => 1; has 'crew', is => 'rw', isa => 'ArrayRef[Str]', required => 1; sub BUILD { my $self = shift; if ( $self->captain ~~ $self->crew ) { my $captain = $self->captain; die "Validation Error: Cannot use $captain for both Captain and Crew"; } } package main; # Validation error my $enterprise = Starship->new( { captain => 'James T Kirk', crew => ['Dr. McCoy', 'Scott', 'Lt. Uhura', 'James T Kirk'], }); Friday, July 6, 12
'ro', isa => 'Str', required => 1; around BUILDARGS => sub { my $orig = shift; my $class = shift; my @params = @_; # Sole parameter that is not a ref # is considered the name if ( ( @params == 1 ) && ( ! ref $params[0] ) ) { return $class->$orig( name => $params[0] ); } else { return $class->$orig ( @params ); } }; # Watch the semicolon Friday, July 6, 12
Receives: method name and subroutine 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); Friday, July 6, 12
is called • Spot the bug on the right 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"); Friday, July 6, 12
Second parameter is the object • Can call $self->$orig to get requested functionality 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"; }; Friday, July 6, 12
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(@_); } }; Friday, July 6, 12
my $g = Glass->new; $g->break; $g->fix; package Breakable; use Moose::Role; has 'is_broken', is => '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); } Friday, July 6, 12
Use ‘with’ to consume a role • Inside a role, define methods, attributes and modifiers • Use ‘does’ to find out if an object implements a role Friday, July 6, 12
names results in a conflict • Class must then implement the conflicted method on its own • Can call role implementation using their namespace Friday, July 6, 12
method: compare($other) - returns -1 if $other is greater than $self; 0 if they are equal and +1 if $self is greater. • Use compare to implement the following: greater_than, greater_or_equal, less_than, less_or_equal • Implement a class that consumes the role Friday, July 6, 12
• Use ‘writer’ to specify writer’s name • Use ‘reader’ to specify reader’s 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', ); Friday, July 6, 12
Available Types include: Bool, Str, Num, Int, ScalarRef, ArrayRef, HashRef, CodeRef • Can use another object as type constraint • Many more type constraints with option to extend the list yourself Friday, July 6, 12
'owner', is => 'ro', isa => 'Str'; has 'clients', is => 'rw', isa => 'ArrayRef[Client]'; has 'products', is => 'rw', isa => 'ArrayRef[Product]'; has 'revenue', is => 'rw', isa => 'Num'; 1; Friday, July 6, 12
builder works better than default for inheritance 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; Friday, July 6, 12
• Use lazy_build to type less 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; Friday, July 6, 12
In the ctor, logger can take a file name • If no arguments passed, create a screen logger (write all output to screen) • If a file name was provided, write to that file • Use dependency injection to test your Logger Solution: https://gist.github.com/3029901 Friday, July 6, 12
an object of a different class • Can then redirect all calls on containing object to the attribute - thus delegating specific methods Friday, July 6, 12
standard data type methods • Currently supported: Array, Hash, Number, String, Bool, Counter • Useful for: Fields that should “work like” the native data type Friday, July 6, 12
and more • Can now use: $q->add_item to add an item to the queue 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); Friday, July 6, 12
can “like” • Every photo starts with 0 likes • How many “likes” do you have before the image is online ? Uploading Image No likes yet Image Online 0 Likes. Go find more friends Friday, July 6, 12
unpublish and is_published • Setting value to undef does not affect predicate package Photo; use Moose; has 'likes' => ( is => 'rw', clearer => 'unpublish', predicate => 'is_published', ); sub publish { my $self = shift; $self->likes ( 0 ); } Friday, July 6, 12
not the same as the name of the constructor param • A possible workaround is BUILDARGS, but that’s too tedious • A better way: Use init_arg • Usage: modify constructor param name, prevent dependency injection Friday, July 6, 12
Attribute name is size, while object creation is performed with: Cls->new( bigness => 7 ) has 'bigness' => ( is => 'ro', init_arg => 'size', ); Friday, July 6, 12
scope - it will now be deleted • When Course object leaves scope, Moose will automatically clear all “learns_at” attributes of students package Student; use Moose; has 'name', is => 'ro', required => 1; has 'learns_at', is => 'rw', weak_ref => 1; Full Example: https://gist.github.com/3031636 Friday, July 6, 12
when set from new or explicitly • Is not called when set from default or builder has 'size' => ( is => 'rw', trigger => \&_size_set, ); sub _size_set { my ( $self, $size, $old_size ) = @_; } Friday, July 6, 12
or simply put - an API to build an object system • Moose is one object system built upon Class::MOP • Understanding Class::MOP and Moose’s use of it reveals new features in Moose Friday, July 6, 12
Introspection • Modify objects and classes dynamically (add/remove methods, attributes, roles) • Add more information to attributes (label, persistent) Friday, July 6, 12
$attr ( $z->meta->get_all_attributes ) { say $attr->name; } for my $method ( $z->meta->get_all_methods ) { say $method->fully_qualified_name; } if ( $z->meta->has_method( 'eat_brain' ) ) { $z->eat_brain; } Full Source: https://gist.github.com/3032056 Friday, July 6, 12
of type constraints • $constraint->check( $value ) • $constraint->validate( $value ) or die $constraint->get_message( $value ); • $constraint->assert_valid( $value ) Friday, July 6, 12
of a certain type • Types have names, so they can be reused • Type checking is just sugar for method arguments validation. Perl does not associate types with variables • Earlier error detection Friday, July 6, 12
'Person::Types::EyeColor', [qw/gray brown green blue/]; package Person; use Moose; use Moose::Util::TypeConstraints; has 'eyecolor' => ( is => 'ro', isa => 'Person::Types::EyeColor', ); Friday, July 6, 12
subtype • role_type ‘barks’, { role => ‘Some::Library::Role::Barks’ } • union ‘UnionName’, [qw/Str ArrayRef/]; - Create a new subtype that can hold either string or an array Friday, July 6, 12
‘where’ to add constraint on the base type • Provide your own error message with ‘message’ subtype 'NaturalLessThanTen', as 'Natural', where { $_ < 10 }, message { "This number ($_) is not less than ten!" }; Friday, July 6, 12
extend • Writing extensions can take some time and effort BUT • There are tons of Moose Extensions on CPAN • Prefixed MooseX, they provide extra or modified functionality Friday, July 6, 12
argument package Contact; use Moose; use MooseX::StrictConstructor; has 'email', is => 'ro'; has 'name', is => 'ro'; package main; # Throw an exception Contact->new( name => 'Bob', emial => 'bob@gmail.com'); Friday, July 6, 12
Has initialization method to pass arguments if needed package App; use MooseX::Singleton; package main; { my $app = App->instance; } { # same one my $app = App->instance; } Friday, July 6, 12
use v5.14; package Contact; use Moose; use MooseX::SingleArg; single_arg 'name'; has 'name', is => 'ro'; package main; my $c = Contact->new('Mike'); say $c->name; Friday, July 6, 12
be called from within the class • protected can only be called from within the class or any of its subclasses • Doesn’t work for roles use MooseX::Privacy; has config => ( is => 'rw', isa => 'Some::Config', traits => [qw/Private/], ); private_method foo => sub { return 23; }; protected_method bar => sub { return 42; }; Friday, July 6, 12
class • New attributes: log and logger package MyApp; use Moose; with 'MooseX::Log::Log4perl'; sub go { my $self = shift; $self->log->debug('Starting method go'); $self->log->info('Go go go'); $self->log('IO')->info('reading data'); } Friday, July 6, 12
• all subroutines become ‘requires’ • Easy to use and very powerful package Logger; use Moose; use MooseX::APIRole; sub info { } sub error { } make_api_role 'Logger::API'; package Test; use Moose; # Fails - Test does not implement # required methods with 'Logger::API'; Friday, July 6, 12
Rock; use Moose; package Game; use Moose; use MooseX::MultiMethods; multi method play (Paper $x, Rock $y) { 1 } multi method play (Scissors $x, Paper $y) { 1 } multi method play (Rock $x, Scissors $y) { 1 } multi method play (Any $x, Any $y) { 0 } my $game = Game->new; # 1, Paper covers Rock print $game->play(Paper->new, Rock->new); Friday, July 6, 12