X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=moose-class%2Fslides%2Findex.html;h=979bbfa1c67af03fa3cc230f7f443bd2af009153;hb=9b515ca454467f3399fadd456b7ed0945b30d618;hp=2acaf6c31aa1e0a1bc3bfac3fa205b0f3870a683;hpb=5d57268450de0579773e5835629b68666fc4eb70;p=gitmo%2Fmoose-presentations.git diff --git a/moose-class/slides/index.html b/moose-class/slides/index.html index 2acaf6c..979bbfa 100644 --- a/moose-class/slides/index.html +++ b/moose-class/slides/index.html @@ -40,7 +40,7 @@ img#me05 {top: 43px;left: 36px;} @@ -262,7 +275,7 @@ has weight => ( ); # kaboom -Person->new( weight => 'fat' ); +Person->new( weight => 'heavy' );
@@ -284,11 +297,11 @@ use Moose; 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
@@ -341,7 +354,7 @@ has blog_uri => (

Why Moose?

@@ -829,6 +842,16 @@ has last_name => ( ); +
+

More Why Moose?

+ + +
+

Part 1: Moose Classes

@@ -866,10 +889,103 @@ use Moose;
+

BUILDARGS

+ + +
+ +
+

BUILDARGS Example

+ +
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')
+
+ +
+

BUILD

+ + +
+ +
+

BUILD Example

+ +
package 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;
+    }
+}
+
+ +
+

Object Construction a la Moose

+ +
Person->new(@_)
+ +
    +
  1. Calls Person->BUILDARGS(@_) to turn @_ into a hashref
  2. +
  3. Blesses a reference
  4. +
  5. Populates attributes based on the hashref from #1
  6. +
  7. Calls $new_object->BUILDALL($constructor_args) +
    ... which calls all BUILD methods
  8. +
  9. Returns the object
  10. +
+
+ +
+

The Object is Opaque

+ + +
+ +
+

DEMOLISH

+ + +
+ +

extends

package Employee;
@@ -918,26 +1034,27 @@ extends 'LWP';
-

overrides and super

+

override and super

-

overrides and super

+

override and super

package Employee;
 use Moose;
 
 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();
 };
@@ -949,11 +1066,12 @@ use Moose;
  • Mostly like $self->SUPER::work(@_)
  • But cannot change @_!
  • Binds the parent's method at compile time
  • +
  • Parent determined by checking Child->meta()->superclasses()
  • -

    Attributes (Part 1)

    +

    Minimal Attributes

    + +
    +

    Cleaning Up Moose Droppings

    + +
    package Person;
    +use namespace::autoclean;
    +use Moose;
    +
    +...
     
     # false
    -Person->can('extends');
    +Person->can('extends');

    No Moose

    @@ -1043,13 +1176,13 @@ Person->can('extends');

    Immutability

    package Person;
     use Moose;
     
    -__PACKAGE__->meta->make_immutable;
    +__PACKAGE__->meta->make_immutable;
    @@ -1080,7 +1213,7 @@ use Moose;
  • use Moose
  • Class->meta
  • Moose::Object base class
  • -
  • extends, overrides, and super
  • +
  • extends, override, and super
  • Simple attributes: has, is => 'ro', & is => 'rw'
  • no Moose
  • __PACKAGE__->meta->make_immutable
  • @@ -1095,12 +1228,15 @@ use Moose;

    Exercises

    # cd exercises
    -$ perl bin/prove -lv t/00-prereq.t
     
    -Missing anything? Install it. (see tarballs/)
    +# 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
    @@ -1118,11 +1254,10 @@ Iterate til this passes all its tests
    -

    Roles Can Have State and Behavior

    +

    Roles - State and Behavior

    package HasPermissions;
     use Moose::Role;
    -
     # state
     has access_level => ( is => 'rw' );
     
    @@ -1131,7 +1266,8 @@ sub can_access {
         my $self     = shift;
         my $required = shift;
     
    -    return $self->access_level >= $required;
    +    return $self->access_level
    +             >= $required;
     }
    @@ -1175,8 +1311,8 @@ with 'HasPermissions';

    Classes Consume Roles

    my $person = Person->new(
    -    first_name => 'Kenichi',
    -    last_name => 'Asai',
    +    first_name   => 'Kenichi',
    +    last_name    => 'Asai',
         access_level => 42,
     );
     
    @@ -1232,7 +1368,7 @@ sub print {
     
     # or ...
     
    -if ( Person->meta->does('Printable') ) { ... }
    +Person->meta->does_role('Printable') @@ -1286,7 +1422,7 @@ use Moose;

    Conflict Resolution

    @@ -1298,10 +1434,10 @@ use Moose; use Moose; with 'IsFragile' => - { alias => + { -alias => { break => 'break_bone' } }, 'CanBreakdance' => - { alias => + { -alias => { break => 'break_it_down' } }; + + +
    +

    Basic Attributes Summary

    + +
    @@ -2111,18 +2259,18 @@ Iterate til this passes all its tests
    -

    What is a Method Modifier

    +

    What Are Method Modifiers For?

    @@ -2166,7 +2314,8 @@ before work => sub { my $self = shift; return unless $DEBUG; - warn "Called work on ", $self->full_name, + warn "Called work on ", + $self->full_name, "with the arguments: [@_]\n"; }; @@ -2198,19 +2347,17 @@ after work => sub {
    -

    Other Uses Example

    +

    More Modifier Examples

    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;
    @@ -2250,8 +2397,7 @@ after clear_password => sub {
             $self->$orig(
                 $self->_munge_insert(@_) );
     
    -    $new_user->_assign_uri;
    -
    +    $new_user->_assign_uri;
         return $new_user;
     };
    @@ -2260,7 +2406,7 @@ after clear_password => sub {

    Modifier Order

    @@ -2315,6 +2461,7 @@ around run => sub { @@ -2329,13 +2476,13 @@ sub xml { '<doc>' . inner() . '</doc> package Report; extends 'Document'; - -augment xml => { title() . inner() . summary() }; +augment xml => + sub { title() . inner() . summary() }; package TPSReport; extends 'Report'; - -augment xml => { tps_xml() . inner() }; +augment xml => + sub { tps_xml() . inner() };
    @@ -2414,7 +2561,8 @@ extends 'Report';

    Exercises

    # cd exercises
    -# perl bin/prove -lv t/04-method-modifiers.t
    +# perl bin/prove -lv \
    +      t/04-method-modifiers.t
     
     Iterate til this passes all its tests
    @@ -2423,239 +2571,789 @@ Iterate til this passes all its tests

    Part 5: Types

    -
    -

    Part 6: Advanced Attributes

    -
    -
    -

    Weak References

    +

    A Type System for Perl

    -

    Circular Reference Illustrated

    - -
    my $foo = {};
    -my $bar = { foo => $foo };
    -$foo->{bar} = $bar;
    +

    Components of a Moose Type

    -

    Weakening Circular References

    +

    Built-in Type Hierarchy

    -
    use Scalar::Util qw( weaken );
    +  
    +Any
    +Item
    +    Bool
    +    Maybe[`a]
    +    Undef
    +    Defined
    +        Value
    +            Str
    +                Num
    +                    Int
    +                ClassName
    +
    +
    -my $foo = {}; -my $bar = { foo => $foo }; -$foo->{bar} = $bar; -weaken $foo->{bar} +
    +

    Built-in Type Hierarchy

    - +
    +(Item)
    +    (Defined)
    +        (Value)
    +        Ref
    +            ScalarRef
    +            ArrayRef[`a]
    +            HashRef[`a]
    +            CodeRef
    +            RegexpRef
    +            GlobRef
    +                FileHandle
    +            Object
    +
    -

    Circular References in Attributes

    +

    Bool

    -
    package Person;
    -use Moose;
    +  

    True

    +
    1
    +924.1
    +'true'
    +{}
    -has name => ( is => 'ro' ); -has friend => ( is => 'rw' ); +

    False

    +
    0
    +0.0
    +'0'
    +undef
    -my $alice = Person->new( name => 'Alice' ); -my $bob = Person->new( name => 'Bob' ); -$bob->friend($alice); -$alice->friend($bob);
    +
    -

    The Fix

    +

    Value (and subtypes)

    -
    package Person;
    -use Moose;
    +  
      +
    • Value is true when ! ref $thing
    • +
    • Value and Str are effectively the same, but Str is more expressive
    • +
    • Num is true when a $scalar looks like a number
    • +
    • An overloaded object which numifies does not pass the Num constraint!
    • +
    • Perl 5's overloading is hopelessly broken
    • +
    +
    -has name => ( is => 'ro' ); -has friend => ( is => 'rw', weak_ref => 1 ); +
    +

    ClassName and RoleName

    -my $alice = Person->new( name => 'Alice' ); -my $bob = Person->new( name => 'Bob' ); -$bob->friend($alice); -$alice->friend($bob);
    +
    -

    Under the Hood

    +

    Parameterizable Types

    -

    Triggers

    +

    Maybe[`a]

    +
    -

    Gross

    +
    +

    Type Union

    -
    after salary_level => {
    -    my $self = shift;
    -    return unless @_;
    -    $self->clear_salary;
    -};
    +
    -

    Use a Trigger Instead

    +

    Making Your Own Types

    -

    Cleaner

    +
    use Moose::Util::TypeConstraints;
     
    -  
    has salary_level => (
    -    is      => 'rw',
    -    trigger => sub { $_[0]->clear_salary },
    +subtype 'PositiveInt',
    +    as      'Int',
    +    where   { $_ > 0 },
    +    message
    +        { "The value you provided ($_)"
    +          . " was not a positive int." };
    +
    +has size => (
    +    is  => 'ro',
    +    isa => 'PositiveInt',
     );
    -

    Delegation

    +

    Automatic Types

    -

    Delegation Examples

    +

    Automatic Types

    -
    package Person;
    +  
    package Employee;
    +use Moose;
     
    -has lungs => (
    -    is      => 'ro',
    -    isa     => 'Lungs',
    -    handles => [ 'inhale', 'exhale' ],
    -);
    +has manager => ( + is => 'rw', + isa => 'Employee', +); -
      -
    • Creates $person->inhale and ->exhale methods
    • -
    • Internally calls $person->lungs->inhale
    • -
    +has start_date => ( + is => 'ro', + isa => 'DateTime', +);
    -

    Why Delegation?

    +

    Subtype Shortcuts - class_type

    - -
    +
    use Moose::Util::TypeConstraints;
    +class_type 'DateTime';
    -
    -

    Moose's handles Parameter

    +
    - -
    +
    subtype 'DateTime',
    +    as      'Object',
    +    where   { $_->isa('DateTime') },
    +    message { ... };
    +
    -

    Array Reference

    +

    Subtype Shortcuts - role_type

    - +
    use Moose::Util::TypeConstraints;
    +role_type 'Printable';
    + +
    + +
    subtype 'Printable',
    +    as      'Object',
    +    where
    +        { Moose::Util::does_role(
    +              $_, 'Printable' ) },
    +    message { ... };
    -

    Hash Reference

    +

    Subtype Shortcuts - duck_type

    -
      -
    • Mapping of names in the delegating class to the delegatee class
    • -
    +
    use Moose::Util::TypeConstraints;
    +duck_type Car => qw( run break_down );
    -
    package Person;
    -use Moose;
    +
    -has account => ( - is => 'ro', - isa => 'BankAccount', - handles => { - receive_money => 'deposit', - give_money => 'withdraw', - }, -);
    +
    subtype 'Car',
    +    as      'Object',
    +    where   { all { $_->can($_) }
    +              qw( run break_down ) },
    +    message { ... };
    -

    Hash Reference Detailed

    +

    Subtype Shortcuts - enum

    -
        handles => {
    -        receive_money => 'deposit',
    -        give_money    => 'withdraw',
    -    },
    +
    use Moose::Util::TypeConstraints;
    +enum Color => qw( red blue green ) );
    -
      -
    • $person->receive_money = $person->account->deposit
    • -
    • $person->give_money = $person->account->withdraw
    • -
    +
    + +
    my %ok = map { $_ => 1 }
    +             qw( red blue green );
    +
    +subtype 'Color'
    +    as      'Str',
    +    where   { $ok{$_} },
    +    message { ... };
    -

    Regex

    +

    Anonymous Subtypes

    package Person;
    -use Moose;
     
    -has name => (
    -    is      => 'ro',
    -    isa     => 'Name',
    -    handles => qr/.*/,
    +my $posint =
    +    subtype as 'Int', where { $_ > 0 };
    +
    +has size => (
    +    is  => 'ro',
    +    isa => $posint,
     );
      -
    • Creates a delegation for every method in the Name class
    • -
    • Excludes meta and methods inherited from Moose::Object
    • +
    • Shortcuts have anonymous forms as well
    -

    Role Name

    +

    Coercions

    -
    package Auditor;
    -use Moose::Role;
    +  
    use Moose::Util::TypeConstraints;
     
    -sub record_change  { ... }
    -sub change_history { ... }
    +subtype 'UCStr',
    +    as    'Str',
    +    where { ! /[a-z]/ };
    +
    + +
    +

    Coercions

    + +
    coerce 'UCStr',
    +    from 'Str',
    +    via  { uc };
    +
    +has shouty_name => (
    +    is     => 'ro',
    +    isa    => 'UCStr',
    +    coerce => 1,
    +);
    +
    + +
    +

    Coercion Examples

    + +
    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 => $_ ) };
    + +
      +
    • Use coercion to inflate a value
    • +
    +
    + +
    +

    Coercion Examples

    + +
    coerce 'ArrayRef[Int]',
    +    from 'Int',
    +    via  { [ $_ ] };
    + +
      +
    • Instead of union - Int | ArrayRef[Int]
    • +
    +
    + +
    +

    Using Types with Attributes

    + +
    package Person;
    +
    +has height => (
    +    is  => 'rw',
    +    isa => 'Num',
    +);
    +
    +has favorite_numbers => (
    +    is     => 'rw',
    +    isa    => 'ArrayRef[Int]',
    +    coerce => 1,
    +);
    +
    + +
    +

    More Droppings

    + +
      +
    • Moose::Util::TypeConstraints also needs cleanup
    • +
    + +
    package Person;
    +
    +use Moose;
    +use Moose::Util::TypeConstraints;
    +
    +subtype ...;
    +
    +no Moose;
    +no Moose::Util::TypeConstraints;
    +
    + +
    +

    Typed Methods (Low-tech)

    + +
    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 },
    +        );
    +    ...
    +}
    +
    + +
    +

    Typed Methods (High-tech)

    + +
    package Person;
    +
    +use MooseX::Method::Signatures;
    +
    +method work ( ArrayRef[Task] :$tasks,
    +                        Bool :$can_rest = 0 ) {
    +    my $self = shift;
    +
    +    ...
    +}
    +
    + +
    +

    Digression: The Type Registry

    + +
      +
    • Types are actually Moose::Meta::TypeConstraints objects
    • +
    • Stored in an interpreter-global registry mapping names to objects
    • +
    +
    + +
    +

    Danger!

    + +
      +
    • Coercions are attached to type objects
    • +
    • Therefore also global
    • +
    • Name conflicts between modules!
    • +
    • Coercion conflicts between modules!
    • +
    +
    + +
    +

    Namespace Fix

    + +
      +
    • Use some sort of pseudo-namespacing scheme
    • +
    • Never coerce directly to a class name, or to built-in types
    • +
    +
    + +
    +

    Namespace Fix

    + +
    use 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,
    +);
    +
    + +
    +

    Namespace Fix

    + +
    subtype 'MyApp::Type::ArrayOfInt',
    +    as 'ArrayRef[Int]';
    +
    +coerce 'MyApp::Type::ArrayOfInt',
    +    from 'Int',
    +    via  { [ $_ ] };
    +
    + +
    +

    Namespace Fix Pros and Cons

    + +
      +
    • Relatively simple
    • +
    • Already built into Moose
    • +
    • Conflates type and module namespaces
    • +
    • Type names are strings, so typos are easy to make and may be hard to find
    • +
    +
    + +
    +

    MooseX::Types

    + +
    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  { [ $_ ] };
    +
    + +
    +

    MooseX::Types

    + +
    package MyApp::Account;
    +
    +use MyApp::Types qw( ArrayOfInt );
    +
    +has transaction_history => (
    +    is  => 'rw',
    +    isa => ArrayOfInt,
    +);
    +
    + +
    +

    MooseX::Types

    + +
      +
    • Type names are exported functions, catches typos early
    • +
    • Types must be pre-declared
    • +
    • Types are stored with namespaces internally, but you use short names
    • +
    • Import existing Moose types as functions from MooseX::Types::Moose
    • +
    • Still need string names for things like ArrayRef['Email::Address']
    • +
    +
    + +
    +

    MooseX::Types Pros and Cons

    + +
      +
    • Catches typos at compile time
    • +
    • Automatic namespacing
    • +
    • One more thing to install and learn
    • +
    • Every name is typed twice (declared and then defined)
    • +
    • Still stuck with strings when referring to class or role names
    • +
    • Coercion gotcha from earlier still applies to types exported from MooseX::Types::Moose
    • +
    +
    + +
    +

    Recommendation

    + +
      +
    • Use MooseX::Types
    • +
    • Compile time error catching and automatic namespacing are huge wins
    • +
    • Docs from Moose::Util::TypeConstraints are 98% compatible with MooseX::Types anyway
    • +
    • A function exported by a type library works wherever a type name would
    • +
    +
    + +
    +

    Questions?

    +
    + +
    +

    Exercises

    + +
    # cd exercises
    +# perl bin/prove -lv t/05-types.t
    +
    +Iterate til this passes all its tests
    +
    + +
    +

    Part 6: Advanced Attributes

    +
    + +
    +

    Weak References

    + +
      +
    • A weak reference lets you avoid circular references
    • +
    • Weak references do not increase the reference count
    • +
    +
    + +
    +

    Circular Reference Illustrated

    + +
    my $foo = {};
    +my $bar = { foo => $foo };
    +$foo->{bar} = $bar;
    + +
      +
    • Neither $foo nor $bar go out of scope
      + (until the program exits)
    • +
    +
    + +
    +

    Weakening Circular References

    + +
    use Scalar::Util qw( weaken );
    +
    +my $foo = {};
    +my $bar = { foo => $foo };
    +$foo->{bar} = $bar;
    +weaken $foo->{bar}
    + +
      +
    • When $bar goes out of scope, $foo->{bar} becomes undef
    • +
    +
    + +
    +

    Circular References in Attributes

    + +
    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);
    +
    + +
    +

    The Fix

    + +
    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);
    +
    + +
    +

    Under the Hood

    + +
      +
    • A weak_ref attribute calls weaken ... +
        +
      • during object construction
      • +
      • when the attribute is set via a writer
      • +
      +
    • +
    +
    + +
    +

    Triggers

    + +
      +
    • A code reference run after an attribute is set
    • +
    • Like an after modifier, but makes intentions clearer
    • +
    + +

    Gross

    + +
    after salary_level => {
    +    my $self = shift;
    +    return unless @_;
    +    $self->clear_salary;
    +};
    +
    + +
    +

    Use a Trigger Instead

    + +

    Cleaner

    + +
    has salary_level => (
    +    is      => 'rw',
    +    trigger =>
    +        sub { $_[0]->clear_salary },
    +);
    +
    + +
    +

    Trigger Arguments

    + +
      +
    • $self
    • +
    • $new_value
    • +
    • $old_value - if one exists
    • +
    +
    + +
    +

    Delegation

    + +
      +
    • Attributes can be objects
    • +
    • Delegation transparently calls methods on those objects
    • +
    +
    + +
    +

    Delegation Examples

    + +
    package Person;
    +
    +has lungs => (
    +    is      => 'ro',
    +    isa     => 'Lungs',
    +    handles => [ 'inhale', 'exhale' ],
    +);
    + +
      +
    • Creates $person->inhale and ->exhale methods
    • +
    • Internally calls $person->lungs->inhale
    • +
    +
    + +
    +

    Why Delegation?

    + +
      +
    • Reduce the number of classes exposed
    • +
    • Re-arrange class internals -
      + turn a method into an attribute with delegation
    • +
    • Provide convenenience methods
    • +
    +
    + +
    +

    Moose's handles Parameter

    + +
      +
    • Accepts many arguments ... +
        +
      • Array reference - list of methods to delegate as-is
      • +
      • Hash reference - map of method names
      • +
      • Regex - delegates all matching methods
      • +
      • Role name - delegates all methods in the role
      • +
      • Sub reference - does something complicated ;)
      • +
      +
    • +
    +
    + +
    +

    Array Reference

    + +
      +
    • 1-to-1 mapping
    • +
    • Takes each method name and creates a simple delegation from the delegating class to the delegatee attribute
    • +
    +
    + +
    +

    Hash Reference

    + +
      +
    • Mapping of names in the delegating class to the delegatee class
    • +
    + +
    package Person;
    +use Moose;
    +has account => (
    +    is      => 'ro',
    +    isa     => 'BankAccount',
    +    handles => {
    +        receive_money => 'deposit',
    +        give_money    => 'withdraw',
    +    },
    +);
    +
    + +
    +

    Hash Reference Detailed

    + +
        handles => {
    +        receive_money => 'deposit',
    +        give_money    => 'withdraw',
    +    },
    + +
      +
    • $person->receive_money = $person->account->deposit
    • +
    • $person->give_money = $person->account->withdraw
    • +
    +
    + +
    +

    Regex

    + +
    package Person;
    +use Moose;
    +
    +has name => (
    +    is      => 'ro',
    +    isa     => 'Name',
    +    handles => qr/.*/,
    +);
    + +
      +
    • Creates a delegation for every method in the Name class
    • +
    • Excludes meta and methods inherited from Moose::Object
    • +
    +
    + +
    +

    Role Name

    + +
    package Auditor;
    +use Moose::Role;
    +sub record_change  { ... }
    +sub change_history { ... }
     
     package Account;
     use Moose;
    @@ -2681,6 +3379,120 @@ has history => (
     
    +

    Native Delegation

    + +
      +
    • Delegate to unblessed Perl types
    • +
    • Scalar, array or hash ref, etc
    • +
    • Treat Perl types as objects
    • +
    • Still uses handles
    • +
    • Pretend that native Perl types have methods
    • +
    +
    + +
    +

    Native Delegation - Array(Ref)

    + +
      +
    • Methods include: +
        +
      • push
      • +
      • shift
      • +
      • elements - returns all elements
      • +
      • count
      • +
      • is_empty
      • +
      • quite a few more
      • +
      +
    • +
    +
    + +
    +

    Native Delegation - Array(Ref)

    + +
    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',
    +      },
    +);
    +
    + +
    +

    Native Delegation - Array(Ref)

    + +
    my $person = Person->new();
    +
    +$person->add_favorite_number(7);
    +$person->add_favorite_number(42);
    +
    +print "$_\n"
    +    for $person->favorite_numbers;
    +
    +# 7
    +# 42
    +
    + +
    +

    Native Delegation

    + +
      +
    • Native types are ... +
        +
      • Number - add, mul, ...
      • +
      • String - append, chop, ...
      • +
      • Counter - inc, dec, ...
      • +
      • Bool - set, toggle, ...
      • +
      • Hash - get, set, ...
      • +
      • Array - already saw it
      • +
      • Code - execute and execute_method
      • +
      +
    • +
    +
    + +
    +

    Curried Delegation

    + +
      +
    • A delegation with some preset arguments
    • +
    • Works with object or Native delegation
    • +
    +
    + +
    +

    Curried Delegation

    + +
    package Person;
    +use Moose;
    +has account => (
    +    is      => 'ro',
    +    isa     => 'BankAccount',
    +    handles => {
    +        receive_100 =>
    +            [ 'deposit', 100 ]
    +        give_100    =>
    +            [ 'withdraw', 100 ]
    +    },
    +);
    +
    + +
    +

    Curried Delegation

    + +
    $person->receive_100;
    +# really is
    +$person->account->deposit(100);
    +
    + +

    Traits and Metaclasses

      @@ -2705,7 +3517,7 @@ has history => (

      Traits and Metaclasses

        -
      • Can add/alter/remove attribute parameter (from has)
      • +
      • Can add/alter/remove an attribute parameter (from has)
      • Can change behavior of created attribute
    @@ -2723,7 +3535,6 @@ has ssn => ( isa => 'Str', label => 'Social Security Number', ); - print Person->meta ->get_attribute('ssn')->label;
    @@ -2742,7 +3553,6 @@ has ssn => ( isa => 'Str', label => 'Social Security Number', ); - print Person->meta ->get_attribute('ssn')->label;
    @@ -2769,6 +3579,241 @@ print Person->meta +
    +

    Questions?

    +
    + +
    +

    Exercises

    + +
    # cd exercises
    +# perl bin/prove -lv \
    +      t/06-advanced-attributes.t
    +
    +Iterate til this passes all its tests
    +
    + +
    +

    Part 7: Introspection

    +
    + +
    +

    Part 8: A Brief Tour of MooseX

    +
    + +
    +

    Notable MX Modules on CPAN

    + +
      +
    • Not comprehensive
    • +
    • 152 MooseX distributions on CPAN as of 02/02/2010
    • +
    • Some of them are crap
    • +
    +
    + +
    +

    Already Mentioned Several

    + +
      +
    • MooseX::NonMoose - best solution for subclassing non-Moose parents
    • +
    • MooseX::Declare - real Perl 5 OO
    • +
    • MooseX::FollowPBP and MooseX::SemiAffordanceAccessor
    • +
    • MooseX::Params::Validate and MooseX::Method::Signatures
    • +
    • MooseX::Types
    • +
    +
    + +
    +

    MooseX::Declare

    + +
    use MooseX::Declare;
    +use 5.10.0; # for say
    +
    +class Person {
    +    has greeting =>
    +        ( is => 'ro', isa => 'Str' );
    +
    +    method speak {
    +        say $self->greeting;
    +    }
    +}
    +
    + +
    +

    MooseX::Declare

    + +
      +
    • Still experimental-ish, but seeing more and more use
    • +
    • Not a source filter!
    • +
    • Hooks into the Perl parser rather than filtering all your code
    • +
    • But not supported by PPI, perltidy, etc.
    • (yet?) +
    +
    + +
    +

    MooseX::StrictConstructor

    + +
      +
    • By default, unknown constructor arguments are ignored
    • +
    • MX::StrictConstructor turns these into an error
    • +
    +
    + +
    +

    MooseX::StrictConstructor

    + +
    package Person;
    +
    +use Moose;
    +use MooseX::StrictConstructor;
    +
    +has name => ( is => 'ro' );
    +
    +Person->new
    +    ( nane => 'Ringo Shiina' ); # kaboom
    +
    + +
    +

    MooseX::Traits

    + +
      +
    • Combines object construction and role application
    • +
    • Makes it easy to create one-off customized objects
    • +
    +
    + +
    +

    MooseX::Traits

    + +
    package MyApp::Thingy;
    +use Moose;
    +
    +with 'MooseX::Traits';
    +
    +my $thing =
    +    MyApp::Thingy->new_with_traits
    +        ( traits => [ 'Foo', 'Bar' ],
    +          size   => 42 );
    +
    + +
    +

    MooseX::Getopt

    + +
      +
    • Makes command-line interface programs easy!
    • +
    • Construct an object from CLI arguments
    • +
    +
    + +
    +

    MooseX::Getopt

    + +
    package App::CLI;
    +use Moose;
    +
    +with 'MooseX::Getopt';
    +
    +has file    =>
    +    ( is => 'ro', required => 1 );
    +has filters =>
    +    ( is => 'ro', isa => 'ArrayRef[Str]' );
    +
    +sub run { ... }
    +
    + +
    +

    MooseX::Getopt

    + +
      +
    • Then call it like this:
    • +
    + +
    #!/usr/bin/perl
    +
    +use App::CLI;
    +
    +App::CLI->new_with_options()->run();
    + +
    $ myapp-cli \
    +   --file foo \
    +   --filters compress \
    +   --filters sanitize
    +
    + +
    +

    MooseX::Clone

    + +
    package Person;
    +
    +use Moose;
    +with 'MooseX::Clone';
    +
    +my $person = Person->new;
    +my $clone  = $person->clone;
    +
    + +
    +

    MooseX::NonMoose

    + +
      +
    • Highly recommended for subclassing non-Moose parents
    • +
    • Gets all the little annoying details right
    • +
    +
    + +
    +

    MooseX::Role::Parameterized

    + +
    package HasCollection;
    +use MooseX::Role::Parameterized;
    +parameter type => ( isa     => 'Str',
    +                    default => 'Item' );
    +role {
    +    my $p = shift;
    +
    +    my $type =
    +        'ArrayRef[' . $p->type() . ']';
    +    has collection =>
    +        ( is  => 'ro',
    +          isa => $type );
    +};
    +
    + +
    +

    MooseX::Role::Parameterized

    + +
    package Person;
    +
    +use Moose;
    +with HasCollection => { type => 'Int' };
    +
    + +
    +

    Questions?

    +
    + +
    +

    Part 9: Writing Moose Extensions

    +
    + +
    +

    The End

    +
    + +
    +

    More Information

    + + +
    +