X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=moose-class%2Fslides%2Findex.html;h=ddf2a4df71d8d7065846eaf93280b80431edeafd;hb=64864ac0897e335b2e3d795d73283ff4de28f94f;hp=10d0339e76129eee80f2d3b93fd3046f9deef440;hpb=ddd87d751a8c055d95240d6af73109770c743466;p=gitmo%2Fmoose-presentations.git diff --git a/moose-class/slides/index.html b/moose-class/slides/index.html index 10d0339..ddf2a4d 100644 --- a/moose-class/slides/index.html +++ b/moose-class/slides/index.html @@ -1,10 +1,10 @@ - +
package Person;
use Moose;
-has 'first_name' => ( is => 'rw' );
+has first_name => ( is => 'rw' );
before 'foo'
=> sub { warn 'About to call foo()' };
@@ -225,7 +235,10 @@ after 'foo'
my @return =
$self->$real_foo( @_, bar => 42 );
- return ( @return, 'modify return values' );
+ return (
+ @return,
+ 'modify return values'
+ );
};
package Person;
use Moose;
-has 'weight' => ( is => 'ro', isa => 'Int' );
+has weight => (
+ is => 'ro',
+ isa => 'Int',
+);
# kaboom
Person->new( weight => 'fat' );
@@ -268,14 +284,14 @@ Person->new( weight => 'fat' );
package Person;
use Moose;
-has 'blog_uri' => (
+has blog_uri => (
is => 'rw',
isa => 'URI',
- handles => { 'blog_hostname' => 'host' },
+ handles => { 'blog_host' => 'host' },
);
-$person->blog_hostname;
-# really calls $person->blog_uri->host
+$person->blog_host;
+# really calls $person->blog_uri->host
$self->{last_nane}
+ $self->{last_nane}
@_
, returns a hash reference of attribute names/value$class->SUPER::BUILDARGS(@_)
as a fallback!package Person;
+use Moose;
+
+sub BUILDARGS {
+ my $class = shift;
+
+ if ( @_ == 1 && ! ref $_[0] ) {
+ return { ssn => $_[0] };
+ }
+ return $class->SUPER::BUILDARGS(@_);
+}
+
+Person->new('123-45-6789')
+new
returnspackage Person;
+use Moose;
+
+sub BUILD {
+ my $self = shift;
+
+ if ( $self->country_of_residence
+ eq 'USA' ) {
+ die 'All US residents'
+ . ' must have an SSN'
+ unless $self->has_ssn;
+ }
+}
+Person->new(@_)
+
+ Person->BUILDARGS(@_)
to turn @_
into a hashref$new_object->BUILDALL($constructor_args)
+ BUILD
methodsDESTROY
, but Moose makes sure all DEMOLISH
methods in a hierarchy are calledextends
is sugar for declaring parent classesuse base
package Employee;
@@ -871,18 +980,18 @@ use Moose;
Each call to extends
resets your parents
- WRONG
+ Wrong
package EvilEmployee;
use Moose;
extends 'Person';
-extends 'Thief';
+extends 'Thief';
- package EvilEmployee;
use Moose;
-extends 'Person', 'Thief';
+extends 'Person', 'Thief';
package My::LWP;
use Moose;
-extends 'LWP';
+extends 'LWP';
Moose::Object
, so ...
@@ -905,28 +1014,30 @@ extends 'LWP';
overrides
and super
override
and super
overrides
is another method modifieroverride
is another method modifierSUPER::
overrides
and super
override
and super
package Employee;
use Moose;
-extends 'Person';
+extends 'Person';
-overrides work => sub {
+override work => sub {
my $self = shift;
- die "Pay me first" unless $self->got_paid;
+ die "Pay me first"
+ unless $self->got_paid;
super();
};
+super
has 'foo'
package Person;
use Moose;
-has 'first_name' => ( is => 'rw' );
+has first_name => ( is => 'rw' );
my $person =
Person->new( first_name => 'Dave' );
@@ -970,7 +1081,7 @@ print $person->first_name; # Stevan
package Person;
use Moose;
-has 'first_name' => ( is => 'ro' );
+has first_name => ( is => 'ro' );
my $person =
Person->new( first_name => 'Dave' );
@@ -1021,6 +1132,7 @@ Person->can('extends');
no Moose
at the end of a package is a best practiceuse namespace::autoclean
at the top$ cd exercises -$ prove -lv t/00-prereq.t +
use Moose
Class->meta
Moose::Object
base classextends
, override
, and super
has
, is => 'ro'
, & is => 'rw'
no Moose
__PACKAGE__->meta->make_immutable
# cd exercises + +# perl bin/prove -lv t/00-prereq.t + +# perl install-moose (if needed) + +# perl bin/prove -lv t/01-classes.t + +# edit lib/Person.pm and lib/Employee.pm + +Iterate til this passes all its tests+
package HasPermissions;
+use Moose::Role;
+# state
+has access_level => ( is => 'rw' );
+
+# behavior
+sub can_access {
+ my $self = shift;
+ my $required = shift;
+
+ return $self->access_level
+ >= $required;
+}
+
+package Printable;
+use Moose::Role;
+
+requires 'as_string';
+package Printable;
+use Moose::Role;
+
+requires 'as_string';
+
+has has_been_printed => ( is => 'rw' );
+
+sub print {
+ my $self = shift;
+ print $self->as_string;
+ $self->has_been_printed(1);
+}
+package Person;
+use Moose;
+
+with 'HasPermissions';
+my $person = Person->new(
+ first_name => 'Kenichi',
+ last_name => 'Asai',
+ access_level => 42,
+);
+
+print $person->full_name
+ . ' has '
+ . $person->can_access(42)
+ ? 'great power'
+ : 'little power';
+package Person;
+use Moose;
+
+with 'Printable';
+package Person;
+use Moose;
+
+with 'Printable';
+
+has has_been_printed => ( is => 'rw' );
+
+sub print {
+ my $self = shift;
+ print $self->as_string;
+ $self->has_been_printed(1);
+}
+if ( Person->does('Printable') ) { ... }
+
+# or ...
+
+Person->meta->does('Printable')
+
+package IsFragile;
+use Moose::Role;
+
+sub break { ... }
+
+package CanBreakdance;
+use Moose::Role;
+
+sub break { ... }
+package FragileDancer;
+use Moose;
+
+with 'IsFragile', 'CanBreakdance';
+
+ with
!package FragileDancer;
+use Moose;
+
+with 'IsFragile' =>
+ { -alias =>
+ { break => 'break_bone' } },
+ 'CanBreakdance' =>
+ { -alias =>
+ { break => 'break_it_down' } };
+
+ exclude
as wellpackage FragileDancer;
+use Moose;
+
+with 'IsFragile' =>
+ { -alias =>
+ { break => 'break_bone' },
+ -excludes => 'break' },
+ 'CanBreakdance' =>
+ { -alias =>
+ { break => 'break_it_down' },
+ -excludes => 'break' };
+package FragileDancer;
+use Moose;
+
+sub break {
+ my $self = shift;
+
+ $self->break_it_down;
+ if ( rand(1) < 0.5 ) {
+ $self->break_bone;
+ }
+}
+package Comparable;
+use Moose::Role;
+
+requires 'compare';
+package TestsEquality;
+use Moose::Role;
+
+with 'Comparable';
+
+sub is_equal {
+ my $self = shift;
+ return $self->compare(@_) == 0;
+}
+package Integer;
+use Moose;
+
+with 'TestsEquality';
+
+# Satisfies the Comparable role
+sub compare { ... }
+
+Integer->does('TestsEquality'); # true
+Integer->does('Comparable'); # also true!
+package HasSubProcess;
+use Moose::Role;
+
+sub execute { ... }
+
+package Killer;
+use Moose::Role;
+
+with 'HasSubProcess';
+
+sub execute { ... }
+package StateOfTexas;
+with 'Killer';
+
+ StateOfTexas
must implement its own execute
Killer
role by itself does not cause an errorrequire
methods of their consumerspackage HasSize;
+use Moose::Role;
+
+requires 'size';
+
+package Shirt;
+use Moose;
+
+with 'HasSize';
+
+has size => ( is => 'ro' );
+package HasSize;
+use Moose::Role;
+
+requires 'size';
+
+package Shirt;
+use Moose;
+
+has size => ( is => 'ro' );
+
+with 'HasSize';
+MooseX::Declare
does rewire Perl)package Comparison;
+use Moose;
+
+has [ 'left', 'right' ] => (
+ is => 'ro',
+ does => 'Comparable',
+);
+
+
+ use Moose::Util qw( apply_all_roles );
+
+my $fragile_person = Person->new( ... );
+apply_all_roles( $fragile_person,
+ 'IsFragile' );
+
+ Person
classpackage Comparable;
+use Moose::Role;
+
+requires 'compare';
+
+no Moose::Role;
+
+ requires
# cd exercises +# perl bin/prove -lv t/02-roles.t + +Iterate til this passes all its tests+
has
package Shirt;
+use Moose;
+
+has 'color' => ( is => 'ro' );
+has 'is_ripped' => ( is => 'rw' );
+undef
package Person;
+use Moose;
+
+has first_name => (
+ is => 'ro',
+ required => 1,
+);
+
+Person->new( first_name => undef ); # ok
+Person->new(); # kaboom
+undef
)package Person;
+use Moose;
+
+has bank => (
+ is => 'rw',
+ default => 'Spire FCU',
+);
+package Person;
+use Moose;
+
+has bank => (
+ is => 'rw',
+ default =>
+ sub { Bank->new(
+ name => 'Spire FCU' ) },
+);
+package Person;
+use Moose;
+
+has bank => (
+ is => 'rw',
+ default => Bank->new(
+ name => 'Spire FCU' ),
+);
+
+ package Person;
+use Moose;
+
+has packages => (
+ is => 'rw',
+ default => sub { [] },
+);
+package Person;
+use Moose;
+
+my $highlander_bank =
+ Bank->new( name => 'Spire FCU' );
+
+has bank => (
+ is => 'rw',
+ default => sub { $highlander_bank },
+);
+package Person;
+use Moose;
+
+has bank => (
+ is => 'rw',
+ builder => '_build_bank',
+);
+
+sub _build_bank {
+ my $self = shift;
+ return Bank->new(
+ name => 'Spire FCU' );
+}
+package HasBank;
+use Moose::Role;
+
+requires '_build_bank';
+
+has bank => (
+ is => 'ro',
+ builder => '_build_bank',
+);
+$self->size * 2
, but attribute initialization order is unpredictablepackage Person;
+use Moose;
+
+has shoe_size => (
+ is => 'ro',
+);
+has shoes => (
+ is => 'ro',
+ lazy => 1,
+ builder => '_build_shoes',
+);
+
+sub _build_shoes {
+ my $self = shift;
+
+ return Shoes->new(
+ size => $self->shoe_size );
+}
+undef
, or notpackage Person;
+use Moose;
+
+has account => (
+ is => 'ro',
+ lazy => 1,
+ builder => '_build_account',
+ clearer => '_clear_account',
+ predicate => 'has_account',
+);
+init_arg
to change thisinit_arg => undef
to make it unconstructableinit_arg
examplespackage Person;
+use Moose;
+
+has shoe_size => (
+ is => 'ro',
+ init_arg => 'foot_size',
+);
+
+Person->new( shoe_size => 13 );
+
+my $person =
+ Person->new( foot_size => 13 );
+print $person->shoe_size;
+init_arg
examplespackage Person;
+use Moose;
+
+has shoes => (
+ is => 'ro',
+ init_arg => undef,
+);
+
+Person->new( shoes => Shoes->new );
+init_arg => undef
?package Employee;
+use Moose;
+
+extends 'Person';
+
+has '+first_name' => (
+ default => 'Joe',
+);
+package Person;
+use Moose;
+
+has first_name => (
+ accessor => 'first_name',
+);
+
+ is => 'rw'
package Person;
+use Moose;
+
+has first_name => (
+ reader => 'first_name',
+ writer => undef,
+);
+
+ is => 'ro'
package Person;
+use Moose;
+
+has first_name => (
+ reader => 'get_first_name',
+ writer => 'set_first_name',
+);
+package Person;
+use Moose;
+
+has first_name => (
+ is => 'rw',
+ writer => '_first_name',
+);
+
+ is
and explicit namesMooseX::FollowPBP
get_foo
and set_foo
MooseX::SemiAffordanceAccessor
foo
and set_foo
package Person;
+use Moose;
+use MooseX::SemiAffordanceAccessor;
+
+has first_name => (
+ is => 'rw',
+);
+
+ first_name
and set_first_name
required
default
or builder
lazy
clearer
and/or predicate
init_arg
# cd exercises +# perl bin/prove -lv \ + t/03-basic-attributes.t + +Iterate til this passes all its tests+
before
and after
before
package Person;
+use Moose;
+
+before work => sub {
+ my $self = shift;
+ die 'I have no job!'
+ unless $self->has_title;
+};
+before
package Person;
+use Moose;
+
+before work => sub {
+ my $self = shift;
+ return unless $DEBUG;
+
+ warn "Called work on ",
+ $self->full_name,
+ "with the arguments: [@_]\n";
+};
+after
package Person;
+use Moose;
+
+after work => sub {
+ my $self = shift;
+ $self->work_count(
+ $self->work_count + 1 );
+};
+has password => (
+ is => 'rw',
+ clearer => 'clear_password',
+);
+has hashed_password => (
+ is => 'ro',
+ builder => '_build_hashed_password',
+ clearer => '_clear_hashed_password',
+);
+after clear_password => sub {
+ my $self = shift;
+ $self->_clear_hashed_password;
+};
+before
and after
Limitationsaround
Modifieraround
around insert => sub {
+ my $orig = shift;
+ my $self = shift;
+
+ $self->_validate_insert(@_);
+
+ my $new_user =
+ $self->$orig(
+ $self->_munge_insert(@_) );
+
+ $new_user->_assign_uri;
+ return $new_user;
+};
++before 2 + before 1 + around 2 + around 1 + wrapped method + around 1 + around 2 + after 1 +after 2 ++
package IsUnreliable;
+use Moose::Role;
+
+requires 'run';
+
+around run => sub {
+ my $orig = shift;
+ my $self = shift;
+
+ return if rand(1) < 0.5;
+
+ return $self->$orig(@_);
+};
+super
package Document;
+
+sub xml { '<doc>' . inner() . '</doc>' }
+
+package Report;
+extends 'Document';
+augment xml =>
+ sub { title() . inner() . summary() };
+
+package TPSReport;
+extends 'Report';
+augment xml =>
+ sub { tps_xml() . inner() };
+$tps->xml
...
+ Document->xml
Report->xml
TPSReport->xml
inner()
to "fill in the blank"inner()
in the terminal class, just in casebefore
and after
for ...
+ around
to ...
+ augment
and inner
to invert the normal subclassing flow ...
+ inner
in the most specific subclass to allow for future extension# cd exercises +# perl bin/prove -lv \ + t/04-method-modifiers.t + +Iterate til this passes all its tests+
+Any +Item + Bool + Maybe[`a] + Undef + Defined + Value + Num + Int + Str + ClassName + RoleName ++
+(Item) + (Defined) + Ref + ScalarRef + ArrayRef[`a] + HashRef[`a] + CodeRef + RegexpRef + GlobRef + FileHandle + Object ++
1
+924.1
+'true'
+{}
+
+ 0
+0.0
+'0'
+undef
+
+ if ($foo)
Value
is true when ! ref $thing
Value
and Str
are effectively the same, but Str
is more expressiveNum
constraint!ArrayRef[`a]
mean?s/`a/Int/
(or Str
or ...)ArrayRef
(== ArrayRef[Item]
)ArrayRef[Str]
ArrayRef[MyTypeName]
ArrayRef[HashRef[Maybe[Int]]]
undef
Maybe[Int]
accepts integers or undef
Int | ArrayRef[Int]
role_type
, duck_type
, or anything not a unionuse Moose::Util::TypeConstraints;
+
+subtype 'PositiveInt',
+ as 'Int',
+ where { $_ > 0 },
+ message
+ { "The value you provided ($_)"
+ . " was not a positive int." };
+
+has size => (
+ is => 'ro',
+ isa => 'PositiveInt',
+);
+package Employee;
+use Moose;
+
+has manager => (
+ is => 'rw',
+ isa => 'Employee',
+);
+
+has start_date => (
+ is => 'ro',
+ isa => 'DateTime',
+);
+class_type
use Moose::Util::TypeConstraints;
+class_type 'DateTime';
+
+subtype 'DateTime',
+ as 'Object',
+ where { $_->isa('DateTime') },
+ message { ... };
+role_type
use Moose::Util::TypeConstraints;
+role_type 'Printable';
+
+subtype 'Printable',
+ as 'Object',
+ where
+ { Moose::Util::does_role(
+ $_, 'Printable' ) },
+ message { ... };
+duck_type
use Moose::Util::TypeConstraints;
+duck_type Car => qw( run break_down );
+
+subtype 'Car',
+ as 'Object',
+ where { all { $_->can($_) }
+ qw( run break_down ) },
+ message { ... };
+enum
use Moose::Util::TypeConstraints;
+enum Color => qw( red blue green ) );
+
+my %ok = map { $_ => 1 }
+ qw( red blue green );
+
+subtype 'Color'
+ as 'Str',
+ where { $ok{$_} },
+ message { ... };
+package Person;
+
+my $posint =
+ subtype as 'Int', where { $_ > 0 };
+
+has size => (
+ is => 'ro',
+ isa => $posint,
+);
+
+ use Moose::Util::TypeConstraints;
+
+subtype 'UCStr',
+ as 'Str',
+ where { ! /[a-z]/ };
+coerce 'UCStr',
+ from 'Str',
+ via { uc };
+
+has shouty_name => (
+ is => 'ro',
+ isa => 'UCStr',
+ coerce => 1,
+);
+subtype 'My::DateTime',
+ as class_type 'DateTime';
+
+coerce 'My::DateTime',
+ from 'HashRef',
+ via { DateTime->new( %{$_} ) };
+
+coerce 'My::DateTime',
+ from 'Int',
+ via { DateTime->from_epoch(
+ epoch => $_ ) };
+
+ coerce 'ArrayRef[Int]',
+ from 'Int',
+ via { [ $_ ] };
+
+ Int | ArrayRef[Int]
package Person;
+
+has height => (
+ is => 'rw',
+ isa => 'Num',
+);
+
+has favorite_numbers => (
+ is => 'rw',
+ isa => 'ArrayRef[Int]',
+ coerce => 1,
+);
+Moose::Util::TypeConstraints
also needs cleanuppackage Person;
+
+use Moose;
+use Moose::Util::TypeConstraints;
+
+subtype ...;
+
+no Moose;
+no Moose::Util::TypeConstraints;
+package Person;
+use MooseX::Params::Validate qw( validated_list );
+
+sub work {
+ my $self = shift;
+ my ( $tasks, $can_rest ) =
+ validated_list(
+ \@_,
+ tasks =>
+ { isa => 'ArrayRef[Task]',
+ coerce => 1 },
+ can_rest =>
+ { isa => 'Bool',
+ default => 0 },
+ );
+ ...
+}
+package Person;
+
+use MooseX::Method::Signatures;
+
+method work ( ArrayRef[Task] :$tasks,
+ Bool :$can_rest = 0 ) {
+ my $self = shift;
+
+ ...
+}
+Moose::Meta::TypeConstraints
objectsuse Moose::Util::TypeConstraints;
+subtype 'MyApp::Type::DateTime',
+ as 'DateTime';
+
+coerce 'MyApp::Type::DateTime',
+ from 'HashRef',
+ via { DateTime->new( %{$_} ) }
+
+has creation_date => (
+ is => 'ro',
+ isa => 'MyApp::Type::DateTime',
+ coerce => 1,
+);
+subtype 'MyApp::Type::ArrayOfInt',
+ as 'ArrayRef[Int]';
+
+coerce 'MyApp::Type::ArrayOfInt',
+ from 'Int',
+ via { [ $_ ] };
+package MyApp::Types;
+
+use MooseX::Types
+ -declare => [ qw( ArrayOfInt ) ];
+use MooseX::Types::Moose
+ qw( ArrayRef Int );
+
+subtype ArrayOfInt,
+ as ArrayRef[Int];
+
+coerce ArrayOfInt
+ from Int,
+ via { [ $_ ] };
+package MyApp::Account;
+
+use MyApp::Types qw( ArrayOfInt );
+
+has transaction_history => (
+ is => 'rw',
+ isa => ArrayOfInt,
+);
+MooseX::Types::Moose
ArrayRef['Email::Address']
MooseX::Types::Moose
MooseX::Types
Moose::Util::TypeConstraints
are 98% compatible with MooseX::Types
anyway# cd exercises +# perl bin/prove -lv t/05-types.t + +Iterate til this passes all its tests+
my $foo = {};
+my $bar = { foo => $foo };
+$foo->{bar} = $bar;
+
+ $foo
nor $bar
go out of scopeuse Scalar::Util qw( weaken );
+
+my $foo = {};
+my $bar = { foo => $foo };
+$foo->{bar} = $bar;
+weaken $foo->{bar}
+
+ $bar
goes out of scope, $foo->{bar}
becomes undef
package Person;
+use Moose;
+
+has name => ( is => 'ro' );
+has friend => ( is => 'rw' );
+
+my $alice = Person->new( name => 'Alice' );
+my $bob = Person->new( name => 'Bob' );
+$bob->friend($alice);
+$alice->friend($bob);
+package Person;
+use Moose;
+
+has name => ( is => 'ro' );
+has friend => ( is => 'rw',
+ weak_ref => 1 );
+
+my $alice = Person->new( name => 'Alice' );
+my $bob = Person->new( name => 'Bob' );
+$bob->friend($alice);
+$alice->friend($bob);
+weak_ref
attribute calls weaken
...
+ after
modifier, but makes intentions clearerafter salary_level => {
+ my $self = shift;
+ return unless @_;
+ $self->clear_salary;
+};
+has salary_level => (
+ is => 'rw',
+ trigger => sub { $_[0]->clear_salary },
+);
+package Person;
+
+has lungs => (
+ is => 'ro',
+ isa => 'Lungs',
+ handles => [ 'inhale', 'exhale' ],
+);
+
+ $person->inhale
and ->exhale
methods$person->lungs->inhale
handles
Parameterpackage Person;
+use Moose;
+has account => (
+ is => 'ro',
+ isa => 'BankAccount',
+ handles => {
+ receive_money => 'deposit',
+ give_money => 'withdraw',
+ },
+);
+ handles => {
+ receive_money => 'deposit',
+ give_money => 'withdraw',
+ },
+
+ $person->receive_money
= $person->account->deposit
$person->give_money
= $person->account->withdraw
package Person;
+use Moose;
+
+has name => (
+ is => 'ro',
+ isa => 'Name',
+ handles => qr/.*/,
+);
+
+ meta
and methods inherited from Moose::Object
package Auditor;
+use Moose::Role;
+sub record_change { ... }
+sub change_history { ... }
+
+package Account;
+use Moose;
+
+has history => (
+ is => 'ro',
+ does => 'Auditor',
+ handles => 'Auditor',
+);
+Auditor
role
+ package Person;
+use Moose;
+has _favorite_numbers => (
+ traits => [ 'Array' ],
+ is => 'ro',
+ isa => 'ArrayRef[Int]',
+ default => sub { [] },
+ init_arg => undef,
+ handles =>
+ { favorite_numbers => 'elements',
+ add_favorite_number => 'push',
+ },
+);
+package Stack;
+use Moose;
+has depth => (
+ traits => [ 'Counter' ],
+ is => 'ro',
+ isa => 'Int',
+ default => 0,
+ init_arg => undef,
+ handles =>
+ { _inc_depth => 'inc',
+ _dec_depth => 'dec',
+ },
+);
+Moose::Meta::Attribute
has
)package Person;
+use Moose;
+use MooseX::LabeledAttributes;
+
+has ssn => (
+ traits => [ 'Labeled' ],
+ is => 'ro',
+ isa => 'Str',
+ label => 'Social Security Number',
+);
+print Person->meta
+ ->get_attribute('ssn')->label;
+package Person;
+use Moose;
+use MooseX::LabeledAttributes;
+
+has ssn => (
+ metaclass =>
+ 'MooseX::Meta::Attribute::Labeled',
+ is => 'ro',
+ isa => 'Str',
+ label => 'Social Security Number',
+);
+print Person->meta
+ ->get_attribute('ssn')->label;
+weak_ref
to avoid circular references# cd exercises +# perl bin/prove -lv \ + t/06-advanced-attributes.t + +Iterate til this passes all its tests+