introduction to moose
DESCRIPTION
Moose is a framework for Perl 5 build on top of Class::MOP, that brings all features of object-oriented programming.TRANSCRIPT
.OOP for PerlMoose
● ( , , , ...)Classes methods types attributes open recursion
● Encapsulation
● Aggregation
● Message passing and dynamic dispatch
● Inheritance and polymorphism
● Abstraction
What we expect from OOPWhat we expect from OOP
Skip definitions
ClassesClasses
● Templates for defining objects state and
behavior
EncapsulationEncapsulation
● Specifying which classes may use the
. :members of an object e g
, , public protected or private determining
, whether they are available to all classes
- .sub classes or only the defining class
AggregationAggregation
● Object containing another object
Message passing and dynamic dispatchMessage passing and dynamic dispatch
● Message passing is the process by which an object sends data to
another object or asks the other object to invoke a method
● , when a method is invoked on an object the object itself determines
what code gets executed by looking up the method at run time in a
( table associated with the object needed when multiple classes
)contain different implementations of the same method
Inheritance and polymorphism Inheritance and polymorphism
● allows the programmer to treat derived class members just like
' (" " their parent class s members Subclasses are more specialized
, versions of a class which inherit attributes and behaviors from
, .)their parent classes and can introduce their own
● - a class has all the state and behavior of another class
polymorphism
What OOP features offers Perl?What OOP features offers Perl?
● 5:MOP in Perl
● Class - package
● Object - ( blessed reference a scalar type holding a reference to object
, , .)data – object may be a hash array or reference to subroutine
● Method - a subroutine within a package
● Constructor – a method that returns reference to blessed variable
What OOP features offers Perl?What OOP features offers Perl?
package Car; sub new { my $class = shift; my $brand = shift; my $self = {}; $self->{'brand'} = $brand; bless ($self, $class); return $self; }
What OOP features offers Perl?What OOP features offers Perl?
● .Encapsulation – in general only by convention
● : , , Some tricks private variable private method private function
package Mom;
sub new{ my $class = shift; my $self = { 'CARD_PIN' => '0000', };
my $closure = sub { my $arg = shift; if ($arg and $arg eq 'nice'){ return $self->{'CARD_PIN'}; } else { return 'forget!'; } }; bless($closure, $class); return $closure; };
package Kido;
use base qw(Mom);
sub ask_for_pin{ my $self = shift; my $how = shift; return &{$self}($how);}
package main;my $mom = Mom->new();my $kido = Kido->new();
print $kido->ask_for_pin('not nice');print $kido->ask_for_pin('nice');
●“ - ... ...Inside out” classes not handy “There is only one way to do it ”.
What OOP features offers Perl?What OOP features offers Perl?
{package Mom; (...)
my $_prepare_pastry = sub{ print "makin' pastry 2 \n"; };
sub make_cookies{ &{$_pastry}; print "cookies ready\n"; }}
package main;(...)package main;
my $mom = Mom->new();$mom->make_cookies();
{package Mom; (...)
sub _prepare_pastry{ if (caller ne __PACKAGE__){ die; } else { print "makin' pastry \n"; } }
sub make_cookies{ _prepare_pastry(); print "cookies ready\n"; }}
package main;(...)my $mom = Mom->new();$mom->_prepare_pastry(); #thiscompiles!$mom->make_cookies();
What OOP features offers Perl?What OOP features offers Perl?
● Aggregation
my $eva = Person->new();my $car1 = Car->new();$car1->brand( 'Fiat' );
$eva->cars->[0]->brand(); #returns 'Fiat'
package Person;
sub new { my $class = shift; my $self = {}; $self->{'cars'} = (); bless( $self, $class ); return $self; }
sub cars { my $self = shift; my $car = shift; if ( ref( $car ) eq 'Car' ) { push @{ $self->{'cars'} }, $car; } return $self->cars; }
What OOP features offers Perl?What OOP features offers Perl?
● ( - ) : ()Inheritance multi object and polymorphism use base
package Person;
sub new { my $class = shift; my $self = {}; bless( $self, $class ); return $self;}
sub name { my $self = shift; if ( @_ ) { $self->{'name'} = shift } return $self->{'name'};}
sub say_name { my $self = shift; printf( "My name is %s. \n", $self->{'name'} );}
package Employee;
use base qw(Person);
sub new { my $class = shift; my $job = shift; my $self = $class->SUPER::new(); $self->{'job'} = $job;
bless( $self, $class ); return $self;}
#overridingsub say_name { my $self = shift; $self->SUPER::say_name(); printf( "My job is %s.\n", $self->{'job'} ); }
#inheritance and polymorphismmy $adam = Employee->new( 'Developer' );$adam->name( 'Adam' );$adam->say_name
What OOP features offers Perl?What OOP features offers Perl?
● Abstraction – only objects
package File; use Carp; sub new { my $class = shift; my $self = {}; bless ($self, $class); return $self; }
sub load{ croak('not implemented'); }
package BMP; use base qw(File);
package JPG; use base qw(File); sub load{ my $self = shift; print "File loaded. \n"; }
my $bmp = BMP->new();$bmp->load(); my $jpg = JPG->new();$jpg->load();
What OOP features offers Perl?What OOP features offers Perl?
?What is missing
● - .The in language support for OO features is minimal
● , , ... No type checking no horizontal inheritance no encapsulation
!There is more than one way to do it
MooseMoose
, There is more than one way to do it but sometimes !consistency is not a bad thing either
Moose is Perl.Moose is Perl.
=Moose:: + Class MOP semantic
Moose is Perl.Moose is Perl.
● common system for building classes
● , new levels of code reuse allowing you to improve your
code with features that would otherwise be too complex or
expensive to implement
Moose is Perl. Moose is Perl.
+ Everything missing in OO Perl you can find in Moose some
!important bonuses
● Type matching
● Coercion - the ability to convert types of one type to another when
necessary
● Encapsulation
● Method Modifiers - , the ability to add code that runs before after or
around an existing method
● Horizontal inheritance - - roles the ability to add predefined
-functionality to classes without sub classing
… .and much more
Moose – first example.Moose – first example.
package Vehicle;use Moose;
has 'RegNum' => ( 'is' => 'rw', 'isa' => 'Str');
sub goto{ my $self = shift; my $place_name = shift; print "Going to $place_name \n"; }
__PACKAGE__->meta->make_immutable;
no Moose;1;
use Vehicle;my $avehicle = Vehicle->new();
$acar->RegNum('123456');print $acar->RegNum;
$acar->goto('Oriente');
MOO
S E C
LASS
Sets strict and warnings
.Class attributes
.Class methods
() We get new for free
We get also default .accessors for free
Moose – first example.Moose – first example.
package Vehicle;use strict;
sub new { my $class = shift; my %args = @_;
my $self = { '_RegNum' => undef };
if ( exists( $args{'RegNum'} ) && defined( $args{'RegNum'} ) ) { $self->{'_RegNum'} = $args{'RegNum'}; }
return bless($self, $class);}
sub RegNum { my $self = shift; my $regnum = shift; if ( $regnum ) { $self->{'_RegNum'} = $regnum; } return $self->{'_RegNum'};}
sub goto{ my $self = shift; my $place_name = shift; print "Going to $place_name \n";}
1;
package Vehicle;use Moose;
has 'RegNum' => ( 'is' => 'rw', 'isa' => 'Str');
sub goto{ my $self = shift; my $place_name = shift; print "Going to $place_name \n"; }
no Moose;1;
12 :lines of code from previous slide give us all this
Lets have a closer look...Lets have a closer look...
(...)
has 'RegNum' => ( 'is' => 'rw', 'isa' => 'Str');
(...)
' ' - -is – defines if argument is read write or read only
' ' ( )isa – defines type of the attribute and validate it during assignment
● Arguments
' ' ' ' rw or ro – tells which accessors will be created
:Moose types , , , , , ,Bool Str Num Int ArrayRef HashRef
and more
Lets have a closer look...Lets have a closer look...
has 'RegNum' => ( 'is' => 'ro', 'isa' => 'Str');
$avehicle->RegNum('123456');
my $avehicle = Vehicle->new(
'RegNum'=>'1234'
);
print $avehicle->RegNum."\n";
● - Read only arguments
...If we restrict access to the attribute ' .the default accessor won t be created
' This method doesn t exist – it results in compilation error
The only right moment to set up the .attribute is during initialization
.We can still read it wherever we want
What more we can do with attributes?What more we can do with attributes?
has 'RegNum' => ( 'is' => 'ro', 'isa' => 'Str', 'required' => 1, );
#$acar = Vehicule->new();
my $avehicle = Vehicle->new('RegNum'=>'123456');
my $avehicle = Vehicle->new('RegNum'=>'');
.Required attribute must be set ...It means that
… this will not compile
... ..but we still can have illegal vehicle .RegNum can be set to empty string
has 'RegNum' => ( 'is' => 'ro', 'required' => 1, );
my $avehicle = Vehicle->new('RegNum'=>undef);BUT
If attribute has no type it can beset to undefined
● Required arguments
What more we can do with attributes?What more we can do with attributes?
has 'RegNum' => ( 'is' => 'ro', 'isa' => 'Str', 'required'=> 1, 'clearer' => 'unregister', 'predicate'=>'is_registered', );
my $avehicle = Vehicule->new('RegNum'=>'1234');
print $avehicle->RegNum;
$avehicle->unregister();
if (!$avehicle->is_registered()){ print "Vehicle illegal!.\n"; }
' .You can t use accessors because it is “ro”
... , .Imagine the vehicle is a broken and is waiting to be scraped but first you must unregister it
... but you can set clearer to clean the value
and predicate to check if the value exists
.The method you get for free
● Clearer and predicate
What more we can do with attributes?What more we can do with attributes?
...Not every string can be the registration number
my $avehicle = Vehicle->new('RegNum'=>'11-xx-22');
#my $avehicle = Vehicle->new('RegNum'=>'111-xx-22');
package Vehicle;use Moose;use Moose::Util::TypeConstraints;
subtype 'RegistrationNo' => as 'Str', => where { /^(\d\d)(-)(\w\w)(-)(\d\d)$/ }, => message {"$_ is not valid registration number."};
has 'RegNum' => ( 'is' => 'ro', 'isa' => 'RegistrationNo', 'required'=> 1, 'clearer' => 'unregister', 'predicate'=>'is_registered', );
use TypeConstraints and create subtype
parent type
$_ compare to something
and use it
Subtypes can be more complex more examples here
● Subtypes
One Step Further - MooseX.One Step Further - MooseX.
.... 2 - : Lets define where is our vehicle a D coordinate two points x and y that
. must have positive value
. .This is very useful type We can pack it in separate package and reuse later
package MyTypes;
use Moose;use MooseX::Types -declare => [qw(Coordinate Point)];use MooseX::Types::Moose qw(Int HashRef);
subtype Point, as Int, where { $_>= 0}, message {'Must be positive number.'};
subtype Coordinate, as HashRef, where { Point->check( $_->{'X'} ) and Point->check( $_->{'Y'} )}, message {'Incorect coordinate.'.$_ };
what we want to create
what we use to create
a point
A coordinate that is a has of two valid points
● Custom types
.Lets use the coordinate to define where is the vehicle
use MyTypes qw( Coordinate );
(...)has 'place' => ( 'is' => 'rw', 'isa' => Coordinate,);(...)
my $place = {'X'=>1, 'Y'=>3 };
$avehicle->place( $place );
modify the vehicle class ( . )Vehicle pm
use it where ever you need
import custom type
● Custom types
One Step Further - MooseX.One Step Further - MooseX.
...?But maybe it would be easier to use it this way
$avehicle->place( [4,5] );
package MyTypes;use Moose;use MooseX::Types -declare => [qw(Coordinate Point)];use MooseX::Types::Moose qw(Int HashRef ArrayRef);
subtype Point, as Int, where { $_> 0}, message {'Must be positive number.'};
subtype Coordinate, as HashRef, where { Point->check( $_->{'X'} ) and Point->check( $_->{'Y'} )}, message {'Incorect coordinate.'.$_ };
coerce Coordinate, from ArrayRef, via { {'X'=>$_->[0], 'Y'=>$_->[1]} };
1;
(...)has 'place' => ( 'is' => 'rw', 'isa' => Coordinate, 'coerce' => 1,);
(...)
One Step Further - MooseX.One Step Further - MooseX.
● Custom types and coercion
We define how we can .transform one type to another
, ,In the end in class definition' Don t forget to turn on the
coercion on the argument
Private attributes – only Private attributes – only convention.convention.
?The vehicle may change its place only when it moves
(...)#an attribute to be publicly readable, but only privately settable.has '_place' => ( 'is' => 'ro', 'isa' => Coordinate, 'coerce' => 1, 'writer' => '_set_place', );
sub goto{ my $self = shift; my $coordinate = shift; $self->_set_place($coordinate);}
(...)
$avehicle->goto([7,8]);print Dumper($avehicle);print 'The vehicle is at'.$avehicle->_place->{'X'}.','.$avehicle->_place->{'Y'}.'.';
#my $place = {'X'=>1, 'Y'=>3 };#my $other_place = [4,5];#$avehicle->_set_place( $place );
This is what the manual says – .but this is only CONVENTION
.Now we can “move” the vehicle
.However this still would work MOOSE is PERL
reader and writer redefine the names of custom accessors
has '_mileage' => ( 'is' => 'ro', 'isa' => 'Num', 'required' => 1, 'default' => 0, 'writer' => '_set_mileage', );
around 'goto' => sub { my $orig = shift; my $self = shift;
my $coordinate1 = $self->_place; my $x1 = $coordinate1->{'X'}; my $y1 = $coordinate1->{'Y'};
#@_ stores new coordinate $self->$orig(@_);
my $coordinate2 = $self->_place; my $x2 = $coordinate2->{'X'}; my $y2 = $coordinate2->{'Y'};
my $distance = sqrt(($x1-$x2)**2+($y1-$y2)**2); $self->_set_mileage($self->_mileage + $distance);};
Method modifiersMethod modifiers
.Mileage – every time the vehicle moves its mileage increase
One more private .attribute
Method modifier
,Has access to attributes before they are modified by
method
Executing main method
Accessing modifiedattributes
other actions
.Mileage – every time the vehicle moves its mileage increase
has '_place' => ( 'is' => 'ro', 'isa' => Coordinate, 'coerce' => 1, 'writer' => '_set_place', 'default' => sub {[0,0]}, );
print $avehicle->_mileage;
$avehicle->goto([7,8]);$avehicle->goto([1,1]);$avehicle->goto([4,5]);
print $avehicle->_mileage
Define default value
Every time we move vehicle .the mileage increase
Method modifiersMethod modifiers
Lazy, lazy_build and trigger.Lazy, lazy_build and trigger.
. .Imagine we want to sell the vehicle The price depends on the mileage
has 'price' => ( 'is' => 'ro', 'isa' => 'Num', 'predicate'=>'has_price', 'lazy_build'=>1, 'builder'=>'calculate_price', 'init_arg'=>undef, );
sub calculate_price { my $self = shift; my $price = 100000; if ($self->_mileage != 0){ $price = $price / $self->_mileage; } return $price;}
$avehicle->goto([1,1]);
print $avehicle->price;
$avehicle->goto([4,5]);
print $avehicle->price;
has 'price' => ( 'is' => 'ro', 'isa' => 'Num', 'predicate'=>'has_price', 'lazy_build'=>1,
+ = _lazy clearer lazy build
- Lazy value is calculated only when it is accessed and is unset.
Lazy needs default value or builder
.Method building the value
... , ' .despite we move the price didn t change Lazy build calculates the value only if it is unset.
Therefore we need to clean it every time .we want to get updated value
Lazy, lazy_build and trigger.Lazy, lazy_build and trigger.
' .Let s run the clearer automatically
has '_mileage' => ( 'is' => 'ro', 'isa' => 'Num', 'required' => 1, 'default' => 0, 'writer' => '_set_mileage', 'trigger' => sub { my $self=shift; $self->clear_price; } );
sub sell_vehicle { my $self = shift; $self->clear_price; return $self->price; }
$avehicle->goto([1,1]);print $avehicle->price;
$avehicle->goto([4,5]);print $avehicle->price;
print "The final price: ".$avehicle->sell_vehicle. "\n";
Every time the mileage changes the .clearer for price attribute is triggered
Lazy build is very useful for attributes are expensive to calculate – .we calculate it only while reading
We can also use method instead .the trigger The price is updated every time we read it
.using the method .Useful for expensive calculation
After this change the previous example shows updated value every time we access it
Code reuse - inheritance in Moose.Code reuse - inheritance in Moose.
?Is the vehicle car or bike
package Car;use Moose;
extends 'Vehicle';
has 'fuel' =>( 'is' => 'rw', 'isa' => 'Int', 'default'=>0,);
sub refuel { my $self = shift; $self->fuel($self->fuel + 10); }
override goto => sub{ my $self = shift; if($self->fuel eq 0){ print 'No fuel'; return; } return super;};
no Moose
Parent class
New attribute
New method
Overwritten method
Calling super
Code reuse - inheritance in Moose.Code reuse - inheritance in Moose.
package Bike;use Moose;
extends 'Vehicle';
has '+RegNum' => ( 'required'=> 0, );
no Moose;1;
my $abike = Bike->new();
changing inherited attribute
registration number is not linger required
Better code reuse – inheritance and roles.Better code reuse – inheritance and roles.
:Short explanation
● .Role is a “state or behavior that can be shared between classes ”
● ' , ' … ' .Role is not a class – can t be subclassed can t be extended we can t create an object of a role
● , .But we can use a role in a class and methods of the role in an object
' ' .We can create a truck role and use it in our vehicle
package Truck;use Moose::Role;
has 'capacity' => ( 'is' => 'ro', 'isa' => 'Int', 'default'=>10, 'required'=>1,);
has 'load' => ( 'is' => 'rw', 'isa' => 'Bool', 'default'=>0,);
sub do_load { my $self = shift; my $load = shift;
if(($self->capacity >= $load) and !$self->load){ $self->load(1);
}else{ print 'Load unsuccessful,'; }}
sub do_unload { my $self = shift; my $load = shift;
if($self->load){ $self->load(0); }else{ print 'Nothing to unload,'; }}no Moose;1;
We create a piece of functionality exactly the same way as we were
.creating a moose class
The only difference
Better code reuse – inheritance and roles.Better code reuse – inheritance and roles.
package Lorry;use Moose;
extends 'Car';with 'Truck';
no Moose;1;
(…)
use Lorry;
(…)
my $alorry = Lorry->new('RegNum'=>'11-xx-23', 'capacity'=>50);#$alorry->do_load(100); - more than capacity
$alorry->do_load(40);#$alorry->do_load(40); #unsecessfull - is loaded
$alorry->do_unload();$alorry->do_load(30);
#$alorry->goto([2,4]); - still can't go without the fuel
- .We want to have a car with a “truck functionality” a lorry
= Lorry Car with Truck role
?How to use Lorry .Lorry is a vehicle – mast have registration number
.Lorry has a Truck role – mast have capacity
.We can use all truck functions
But Lorry is still a car ' .so won t go without fuel
Better code reuse – inheritance and roles.Better code reuse – inheritance and roles.
package Truck;use Moose::Role;requires 'fuel';
(…)
use Moose;
extends 'Bike';with 'Truck';
no Moose;
'Truck' requires the method 'fuel' to be implemented by 'Lorrybike' at/usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi/Moose/Meta/Role/Application.pm line 51
Moose::Meta::Role::Application::apply('Moose::Meta::Role::Application::ToClass=HASH(0xa5faa2c)','Moose::Meta::Role=HASH(0xa5f6a6c)', 'Moose::Meta::Class=HASH(0xa5f8754)') called at/usr/local/lib/perl5/site_perl/5.10.0/i386-linux-thread-multi/Moose/Meta/Role/Application/ToClass.pmline 31 (...)
In the end we want to assure that the truck role will be only used with anything that uses fuel
( - ?)Have you ever seen a lorry bike
' ' We require that a method fuel is present in a class where we
.use the Truck role
' Since bike doesn t use fuel .this code will not compile
Better code reuse – inheritance and roles.Better code reuse – inheritance and roles.
BUILDARGS and BUILD BUILDARGS and BUILD
' . :Let s go back to Vehicle It would be awesome to create a vehicle this way
$acar = Vehicule->new('11-xx-11');
around BUILDARGS => sub { my $orig = shift; my $class = shift;
my %a = @_; my %args = (); if ( @_ == 1 ) { $args{ 'RegNum' } = $_[0]; return $class->$orig( %args ); } return $class->$orig( %a );};
BUILDARGS method is called as a class method . before an object is created It has direct access .to arguments passed in method call
BUILDARGS and BUILD BUILDARGS and BUILD
. ...There is a possibility to have a bike without registration But we want to be warn about it
sub BUILD { my $self = shift; if(!$self->RegNum){ if($self->isa('Bike')){ print "Vehicle without registration number.\n"; } } }
my $abike = Bike->new();my $abike2 = Bike->new('22-yy-66');
.BUILD method is called after an object is created
We will be warn about creating a .bike without a registration number
Aggregation and Native TraitsAggregation and Native Traits
' .Let s create a garage for our vehicles
package Garage;use Moose;use Moose::Util::TypeConstraints;
has 'vehicles' => ( 'isa'=> 'ArrayRef[ Vehicle ]', 'default' => sub {[]}, 'traits' => ['Array'], 'handles'=> { 'add_vehicle' => 'push', 'get_vehicle' => 'shift', },);
no Moose;1;
Trait .– a role added to attribute Here we make the vehicles attribute
.work as an array
.Defining accessors( ).these accessors use type checking
: Notice there is no' ' =>'is ro|rw'
:More native traits
: , , , , , , …Array push pop shift unshit map sort grep
: , , , , , , _ …Hash get set delete exists defined clear is empty
: , , ...String append substr length
: , , ...Bool set unset toggle
: , _ ...Code execute execute method
Aggregation and Native TraitsAggregation and Native Traits
' ...Let s park some vehicles
my $garage = Garage->new();
$garage->add_vehicle($avehicle);my $v = $garage->get_vehicle();
$garage->add_vehicle($acar);my $acar2 = $garage->get_vehicle();
$ 2acar and $acar is the same( )reference to the same object
What more you can do in Moose?What more you can do in Moose?
:For example
● - " " Delegation proxy methods call some other method on an
.attribute
● - Custom traits MooseX
● - Custom OO system – sub classing methaclasses
● ...And much more
Some tipsSome tips
● ' :If you don t know what is wrong try to load package from command line
perl -Ilib/ -e 'use Coordinate'
● $ -> object dump for debugging
More information:More information:
● :// . . / 2010/ / / /13673http www oscon com oscon public schedule detail : Moose is Perl A Guide to the New Revolution
● :// . . / / -527243http www slideshare net dtreder moose , .Awesome presentation with huge dose of humor but also very helpful
● :// . . /~ / -1.21/http search cpan org drolsky Moose :: ::Moose Manual and Moose Cookbook