Class::MOP
package Person;
use strict;
use warnings;
sub new {
my ( $class, @args ) = @_;
@args = %{$args[0]} if @args == 1;
return bless {
@args,
}, $class;
}
sub name {
my ($self, @args) = @_;
$self->{name} = $args[0] if @args;
return $self->{name};
}
sub age {
my ($self, @args) = @_;
$self->{age} = $args[0] if @args;
return $self->{age};
}
1;
package Person;
use Moose;
has name => (is => 'rw');
has age => (is => 'rw');
1;
use Moose;
use strict; use warnings;
@ISA = qw(Moose::Object) unless @ISA
has
declares attributes
is => 'rw'
→ read/write accessoris => 'ro'
→ read only accessorwriter
, reader
new
inherited from Moose::Object
Now we’re going to discuss more features of the attributes
package Manager;
use Moose;
has name => (
is => 'rw',
isa => 'Str',
default => 'Bob'
);
has staff => (
is => 'ro',
isa => 'ArrayRef',
lazy => 1,
default => sub { [qw(Bob Alice Tim)] },
);
Adds default, isa
default
is a
new
lazy
delays default
$object->staff
new
discusses default
non refs make accidental sharing hard
isa
specifies a type
Moose::Util::TypeConstraints
Any, Item, Bool, Undef, Defined, Value, Num, Int, Str, Ref, ScalarRef, ArrayRef, HashRef, CodeRef, RegexpRef, GlobRef, FileHandle, Object and Role
has 'date' => (isa => 'DateTime'); # DWIM
isa, type constraints
Item
⊃ Defined
⊃ Ref
⊃ Object
subtype 'Ref'
=> as 'Defined'
=> where { ref($_) };
subtype 'Object'
=> as 'Ref'
=> where { blessed($_) }
type hierarchy
package Employee;
use Moose;
extends qw(Person);
has manager => (
is => 'ro',
isa => 'Manager',
handles => {
manager_name => 'name',
coworkers => 'staff',
}
);
handles
certain methods for Employee
$emp->coworkers
== $emp->manager->staff
has phone => (
...
handles => [qw(number extension)],
);
has phone => (
...
isa => "Phone",
handles => qr/^[a-z]w+$/,
);
has phone => (
...
handles => "Dialing", # a role
);
package Company;
use Moose;
use MooseX::AttributeHelpers;
has employees => (
metaclass => 'Collection::Array',
isa => 'ArrayRef[Employees]',
is => 'rw',
provides => {
push => 'add_employee',
pop => 'remove_employee',
count => 'number_of_employees',
empty => 'any_employees',
},
);
before 'employees' => sub { warn 'calling employees' };
after 'employees' => sub { warn 'finished calling employees' };
@_
around 'employees' => sub {
my ($next, $self, @args) = @_;
...
my @return = $self->$next(@args);
...
return @return;
};
package Employee;
use Moose;
sub do_work {
my $self = shift;
$self->punch_in;
inner(); # call subclass here
$self->punch_out;
}
package Employee::Chef;
use Moose;
extends qw(Employee);
augment do_work => sub {
my $self = shift;
while ( @burgers ) {
$self->flip_burger(shift @burgers);
}
};
$chef->do_work; # punch in, flip burgers, punch out
package Employee;
use Moose;
use Moose::Util::TypeConstraints;
extends qw(Person);
class_type 'Manager';
coerce 'Manager' => (
from 'Str' => via { Manager->new( name => $_ ) },
);
has manager => (
is => 'ro',
isa => 'Manager',
required => 1,
coerce => 1,
);
# import type constraint keywords
use Moose::Util::TypeConstraints;
# define Manager, a subtype of Object
class_type "Manager";
# define the conversion
... via { Manager->new( name => $_ ) }
# enable it per attribute
has manager => (
...
coerce => 1,
);
breakdown of the example
class types are automatically created for all Moose classes
has employees => (
is => 'rw',
isa => 'ArrayRef[Employee]',
);
has shopping_carts => (
is => 'rw',
isa => 'ArrayRef[ArrayRef[ShinyBead]]'
);
Going to go into features of the type system for a bit
Parametrized types
has language => (
is => 'rw',
isa => 'English | Welsh | Scots | Gaelic',
);
has member => (
is => 'rw',
isa => 'Employee | ArrayRef[ Employee | Group ]',
);
Union types
package Foo;
use Moose;
use Moose::Util::TypeConstraints;
use Test::Deep qw(eq_deeply ...);
type 'SomethingTricky' => where {
eq_deeply( $_, ... );
};
has 'bar' => (
is => 'rw',
isa => 'SomethingTricky',
);
Test::Deep custom validator
Can use any validation from the CPAN
use Moose::Util::TypeConstraints;
subtype 'ArrayRef[Employee]' => as 'ArrayRef';
coerce 'ArrayRef[Employee]' => (
from 'ArrayRef[Str]' via {
[ map { Employee->new( name => $_ ) } @$_ ]
},
);
has staff => (
isa => 'ArrayRef[Employee]',
coerce => 1,
);
coerce parametrized ArrayRef[Employee] from ArrayRef[Str]
MooseX::Clone
- Flexible clone
methodMooseX::Storage
- Flexible serializationMooseX::Getopt
- @ARGV
aware constructorMooseX::LogDispatch
- $self->logger->info("something happenned")
MooseX::Param
- param
method like CGI.pm
’s,Some examples of small reusable behaviors
Param is good for interacting with e.g. CGI::Expand or similar modules
package Minion;
use Moose;
extends qw(Employee);
with qw(Salaried::Hourly);
package Boss;
use Moose;
extends qw(Employee);
with qw(Salaried::Monthly);
with
adds roles into your class
Salaried::Hourly
was added to Minion
package Salaried;
use Moose::Role;
requires 'paycheck_amount';
package Salaried::Hourly;
use Moose::Role;
with qw(Salaried);
has hourly_rate => (
isa => "Num",
is => "rw",
required => 1,
);
has logged_hours => (
isa => "Num",
is => "rw",
default => 0,
);
# satisfy the Salaried interface:
sub paycheck_amount {
my $self = shift;
$self->logged_hours * $self->hourly_rate;
}
roles can have attributes and methods roles provide behavior, not just interface
symmetric composition means no precedence - if two roles try to define the same thing you get a compile time error that needs to be resolved multiple inheritence silently assumes you want the first class
roles cause errors at compile time, unlike multiple inheritence
roles also provide easy ways to fix the errors
package Ballet;
use Moose::Role;
sub dance {
pirouette();
}
package Punk;
use Moose::Role
sub dance {
MOSH!!!11one();
}
package Foo;
use Moose;
# KABOOM:
with qw(
Punk
Ballet
);
conflicts
package Ent::Puppy;
use Moose;
with (
Tree => {
alias => {
bark => "tree_bark",
},
},
Dog => {
alias => {
bark => "bark_sound",
}
},
);
sub bark {
my $self = shift;
if ( $condition ) {
$self->tree_bark;
} else {
$self->bark_sound;
}
}
Composition parameters Easier conflict resolution Finer grained control
Class::MOP
my $class = $obj->meta; # $obj's metaclass
my $meta = MyApp->meta; # MyApp's metaclass
my $emo = $obj->meta->meta; # even more meta!
warn $obj->meta->name;
my $metaclass = $self->meta;
$metaclass->superclasses;
$metaclass->linearized_isa;
$metaclass->has_method("foo");
$metaclass->compute_all_applicable_attributes;
# … lots more
simple introspection
Moose::Meta::Class->create( Bar =>
version => '0.01',
superclasses => [ 'Foo' ],
attributes => [
Moose::Meta::Attribute->new( bar => ... ),
Moose::Meta::Attribute->new( baz => ... ),
],
methods => {
calculate_bar => sub { ... },
construct_baz => sub { ... }
},
);
my $anon_meta = Moose::Meta::Class->create_anon_class( ... );
Classes can be created programmatically
has foo => ( is => "rw" );
__PACKAGE__->meta->add_attribute(
"foo",
is => "rw",
);
has employees => (
metaclass => 'Collection::Array',
...
);
Moose::Meta::Class
, Moose::Meta::Attribute,
Moose::Meta::Method
, Moose::Meta::Method::Accessor
Moose::Meta::Instance
, Moose::Meta::Role
, Moose::Meta::TypeConstraint
, …,$work
project:MooseX::Compile
is in the worksMooseX::GlobRef::Object
package Units::Bytes;
use Moose::Role;
use Moose::Autobox;
sub bytes { $_[0] }
sub kilobytes { $_[0] * 1024 }
sub megabytes { $_[0] * 1024->kilobytes }
sub gigabytes { $_[0] * 1024->megabytes }
sub terabytes { $_[0] * 1024->gigabytes }
Moose::Autobox->mixin_additional_role(
SCALAR => 'Units::Bytes',
);
use Units::Bytes;
use Moose::Autobox; # autoboxing is lexical
is(5->bytes, 5, '... got 5 bytes');
is(5->kilobytes, 5120, '... got 5 kilobytes');
is(2->megabytes, 2097152, '... got 2 megabytes');
is(1->gigabytes, 1073741824, '... got 1 gigabyte');
is(2->terabytes, 2199023255552, '... got 2 terabytes');
oose.pm
perl -Moose -e 'has foo => ( is=> "rw" ); Class->new( foo => 1 )'
Devel::REPL
is cooler though ;-)
package Counter;
use MooseX::POE;
use MooseX::AttributeHelpers;
has count => (
traits => [qw(Counter)],
provides => { inc => "increment_count" },
);
sub START {
shift->yield('increment');
}
event increment => sub {
my $self = shift;
warn "Count is now " . $self->count;
$self->increment_count;
$self->yield('increment') unless $self->count > 3;
};
Counter->new( count => 0 );
POE::Kernel->run();
POE::Session
event
declares POE object statesSlides written by:
Slides deleted by: