Moose workshop

142
Ynon Perek [email protected] http://ynonperek.com Moose Workshop Friday, July 6, 12

description

Object Oriented got a lot easier since Moose came around. This keynote is a one-day advanced Moose workshop covering (almost) everything Moose has to offer perl developers

Transcript of Moose workshop

Page 2: Moose workshop

Agenda

•Moose Overview

• Classes

• Roles

• Attributes

•Meta Object System

• Type System

•Moose Extensions

•Design Patterns

Friday, July 6, 12

Page 3: Moose workshop

Assumptions

• You know how to write a Moose classes

• You know how to define methods and what $self means

Friday, July 6, 12

Page 4: Moose workshop

Moose Overview

•Developed by Stevan Little (on the right)

• Started 2006

• Goal: Change the world

Friday, July 6, 12

Page 5: Moose workshop

Moose Features

• From perl to OO

•No boilerplate code

•Optional type constraints

• Inheritance and Mixins

•Design Patterns

Friday, July 6, 12

Page 6: Moose workshop

Organizations Using Moose

• Cisco

• IMDb

• Infinity Interactive

•MusicBrainz

• Symantec

• And others: http://moose.iinteractive.com/about.html#organizations

Friday, July 6, 12

Page 7: Moose workshop

Moose Alternatives

•Mouse

•Moo

•Mo

• Class::Builder

Friday, July 6, 12

Page 8: Moose workshop

Moose HELP

• IRC: irc://irc.perl.org/#moose

•Mailing List: mailto:[email protected]

• Youtube: search for “perl moose”

Friday, July 6, 12

Page 9: Moose workshop

Moose Classes

Friday, July 6, 12

Page 10: Moose workshop

Moose Classes

• Import sugar functions: extends, has, with, ...

• Enable strict & warnings

• Subclass of Moose::Object for default ctor and dtor

• Create Moose::Meta::Class object

package Foo;use Moose;

Friday, July 6, 12

Page 11: Moose workshop

Moose::Object

• new ( %params )

• BUILDARGS ( %params )

•DESTROY

• does( $role_name )

•DOES( $class_or_role_name )

• dump( $maxdepth )

Friday, July 6, 12

Page 12: Moose workshop

Example: Getting Info

package Pet;use Moose;

has 'name', is => '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::Dumperwarn $dog->dump;

# DOES returns true for objects of the class, subclasses or# implementors of the roleprint "Good boy" if $dog->DOES('Pet');

Friday, July 6, 12

Page 13: Moose workshop

Class Construction

• new method is automatically generated.

• Takes parameters hash or hash ref

package main;# Pass a hash ref to prevent copyingmy $enterprise = Starship->new( {        captain => 'James T Kirk',        crew => ['Dr. McCoy', 'Scott', 'Lt. Uhura'],    });

Friday, July 6, 12

Page 14: Moose workshop

Class Construction

• Construction hooks:

• BUILD

• BUILDARGS

• attribute builders

Friday, July 6, 12

Page 15: Moose workshop

Class Construction

• BUILD is called every time a new 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

Page 16: Moose workshop

Object State Validation

package Starship;use Moose;

has 'captain', is => '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 errormy $enterprise = Starship->new( {        captain => 'James T Kirk',        crew => ['Dr. McCoy', 'Scott', 'Lt. Uhura', 'James T Kirk'],    });

Friday, July 6, 12

Page 17: Moose workshop

BUILDARGS

• Used to manipulate arguments before object creation

• Takes the arguments hash as input, returns hashref

•Wrap in ‘around’ modifier to change

• Used for :

• Single arguments ctor

Friday, July 6, 12

Page 18: Moose workshop

BUILDARGS Examplepackage Person;use Moose;

has 'name', is => '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

Page 19: Moose workshop

Class Destruction

•Moose implemented DESTROY, which will call your DEMOLISH

• It handles inheritance correctly: demolish child before super

Friday, July 6, 12

Page 20: Moose workshop

Class Destruction

•When program ends, prints out:

Bar ::DemolishFoo::Demolish

package Foo;use Moose;

sub DEMOLISH { warn 'Foo::Demolish' }

package Bar;use Moose;extends 'Foo';

sub DEMOLISH { warn 'Bar::Demolish' }

package main;

my $b = Bar->new;

Friday, July 6, 12

Page 21: Moose workshop

Construction Destruction

Do’s and Don’ts

Friday, July 6, 12

Page 22: Moose workshop

Do

• Provide reasonable validations with BUILD

Friday, July 6, 12

Page 23: Moose workshop

Do

• Use warn( $obj->dump ) for debug

Friday, July 6, 12

Page 24: Moose workshop

Do

• Consider namespace::autoclean to remove Moose sugar methods from your classes (has, with, etc.)

Friday, July 6, 12

Page 25: Moose workshop

Do

• Consider using:

__PACKAGE__->meta->make_immutable;

• To improve performance of objects creation

• Consider MooseX::AutoImmute

Friday, July 6, 12

Page 26: Moose workshop

Don’t

•Never override new ( it’ll break stuff down the road )

Friday, July 6, 12

Page 27: Moose workshop

Don’t

•Don’t use BUILD when attribute builders are sufficient

Friday, July 6, 12

Page 28: Moose workshop

Don’t

•Never call $self->SUPER::BUILD

•Moose does that for you

Friday, July 6, 12

Page 29: Moose workshop

Don’t

•Don’t apply a method modifier (before, after, around) to BUILD

Friday, July 6, 12

Page 30: Moose workshop

Don’t

•Don’t write BUILD method for your roles ( Moose ignores them )

Friday, July 6, 12

Page 31: Moose workshop

after

• Add code after a method is executed

• 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

Page 32: Moose workshop

What Is Printed ?

package Foo;use Moose;

sub DEMOLISH { warn 'Foo::Demolish' }sub BUILD { warn 'Foo::Build' }

package Bar;use Moose;extends 'Foo';

sub DEMOLISH { warn 'Bar::Demolish' }sub BUILD { warn 'Bar::Build' }

package main;

my $b = Bar->new;

Friday, July 6, 12

Page 33: Moose workshop

Method Modifiers

• Alter code by injecting other code before or after the modified method

• Can use from roles, subclasses or class itself

Friday, July 6, 12

Page 34: Moose workshop

before

• Before lets us inject code before a method 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

Page 35: Moose workshop

around

• around has two more advantages:

• Can use return value of original method

• Can skip calling original method altogether

• You have the power

Friday, July 6, 12

Page 36: Moose workshop

around

• First parameter: original method as CODE ref

• 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

Page 37: Moose workshop

around

• Forbid login before noon

package User;use Mouse;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

Page 38: Moose workshop

Friday, July 6, 12

Page 39: Moose workshop

Moose RolesAn alternative to deep hierarchies and base classes

Friday, July 6, 12

Page 40: Moose workshop

Role

• Encapsulates behavior. Something that classes do

• Cannot be instansiated

• Classes consume roles - which means everything in the role is copied into the class

Friday, July 6, 12

Page 41: Moose workshop

Classes & Roles

Alive

Think Think

Alive

Person Computer Chicken

Friday, July 6, 12

Page 42: Moose workshop

Roles Example

package Glass;use Moose;

with 'Breakable';

package main;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

Page 43: Moose workshop

Moose Roles

• Use Moose::Role to define a role

• 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

Page 44: Moose workshop

Partial Implementation

• Use ‘requires’ in a role to force your consumer to define a method

• Useful for :

• Partial implementations (template method)

• Abstract Base Class

Friday, July 6, 12

Page 45: Moose workshop

Partial Implementationpackage MultipleFileUploader;use Moose::Role;

requires 'upload_file';

sub upload_files {    my $self = shift;

    my @success;

    foreach my $f ( @_ ) {        die "Invalid file: $f" if ! $f->DOES('File');        $self->upload_file ( $f ) && push @success, $f;    }

    return @success;}

Friday, July 6, 12

Page 46: Moose workshop

Method Conflicts

• Consuming two roles with the same method 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

Page 47: Moose workshop

Method Conflicts

package R1;use Moose::Role;

sub foo {    warn 'R1::foo'}

package R2;use Moose::Role;

sub foo {    warn 'R2::foo'}

package Test;use Moose;

with qw/R1 R2/;

Compilation Error

Friday, July 6, 12

Page 48: Moose workshop

Method Conflict

• Can use -alias to make a copy of a role’s method by another name

• Can use -excludes to avoid consuming a specific method

• Combine both to work around a conflict

Friday, July 6, 12

Page 49: Moose workshop

Method Conflict

with 'Breakable' => { -alias => { break => 'break_bone' }, -excludes => 'break', },

'Breakdancer' => { -alias => { break => 'break_dance' }, -excludes => 'break', };

Friday, July 6, 12

Page 50: Moose workshop

Dynamic Roles

• Roles can be added to instances after creation

• Usage: debug tracing on specific obejct, dynamically change objects by configuration

• Code:

use Moose::Util qw( apply_all_roles );

my $car = Car->new; apply_all_roles( $car, 'Breakable' );

Friday, July 6, 12

Page 51: Moose workshop

Lab

• Implement a Comparable role which requires a single 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

Page 52: Moose workshop

Friday, July 6, 12

Page 53: Moose workshop

Attributes

Friday, July 6, 12

Page 54: Moose workshop

Moose Attributes

• An attribute is a property that every member of a class has

• Some attributes are optional (e.g. some people have a middle name)

Friday, July 6, 12

Page 55: Moose workshop

Attribute Options

• is, reader, writer

• isa

• required, default, builder

• lazy

Friday, July 6, 12

Page 56: Moose workshop

Readers & Writers

• Use ‘is’ to auto generate reader/writer

• 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

Page 57: Moose workshop

Isa

• Use isa to force a type constraint

• 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

Page 58: Moose workshop

Isa

package Store;use Moose;use Client;use Product;

has '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

Page 59: Moose workshop

Subtypes

• Use subtypes to easily define new constraints:

use Moose::Util::TypeConstraints;

subtype 'Age',    as 'Int',    where { $_ >= 0 && $_ <= 120 },    message { "Invalid Age: $_ "};

Friday, July 6, 12

Page 60: Moose workshop

Enumerations

• Use enum function to declare an enum subtype

• An enum takes a single value from a predefined list

enum 'EyeColor', [qw/green blue brown gray/];

Friday, July 6, 12

Page 61: Moose workshop

Required / Default / Builder

• Use required for fields that take their value from “outside”

• Use default / builder for everything else

Friday, July 6, 12

Page 62: Moose workshop

Builder

• Use builder for more complex initialization logic

• 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

Page 63: Moose workshop

lazy

• Create your attributes only when they are needed

• 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

Page 64: Moose workshop

Dependency Injection

• A technique used in testing to build more testable versions of your classes

• If an attribute has both a builder AND was passed externally, external value wins

Friday, July 6, 12

Page 65: Moose workshop

Lab

• Implement a Logger class with one method: log. 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

Page 66: Moose workshop

Delegation

• A relationship between classes. A class attribute is 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

Page 67: Moose workshop

Delegation

Contact EmailSend Mail Send Mail

Phone

CallCall

Friday, July 6, 12

Page 68: Moose workshop

Delegation

•Moose handles delegation for you

• Attribute should declare “handles” option

package Contact;use Moose;

has 'email' => (    is => 'ro',    handles => [ qw/send_mail/ ]);

Friday, July 6, 12

Page 69: Moose workshop

Delegate

• Another option is to delegate an entire role

•Moose will delegate all methods in the role automatically

has 'uri' => ( is => 'ro', isa => 'URI', handles => 'HasURI',);

Friday, July 6, 12

Page 70: Moose workshop

Native Delegation

• Give your object “native” feel by using 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

Page 71: Moose workshop

Native Delegation

•Native arrays have push, pop, shift, unshift and more

• Can now use:$q->add_itemto 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

Page 72: Moose workshop

Native Delegation

• Array functions: Moose::Meta::Attribute::Native::Trait::Array

•Hash functions:Moose::Meta::Attribute::Native::Trait::Hash

Friday, July 6, 12

Page 73: Moose workshop

Friday, July 6, 12

Page 74: Moose workshop

Attributes: Advanced Topics

• Predicates & Clearers

• Constructor Parameters

•Weak References

• Triggers

Friday, July 6, 12

Page 75: Moose workshop

Predicates & Clearers

• User can upload photos, other users can “like”

• Every photo starts with 0 likes

• How many “likes” do you have before the image is online ?

Uploading ImageNo likes yet

Image Online0 Likes.

Go find more friends

Friday, July 6, 12

Page 76: Moose workshop

Predicates & Clearers

• Provide two new methods on $self: 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

Page 77: Moose workshop

Predicates & Clearers

sub like {    my $self = shift;

    die 'Cannot like an Unpublished photo'        if ! $self->is_published;

    $self->likes ( $self->likes + 1 );}

Friday, July 6, 12

Page 78: Moose workshop

Constructor Parameters

• Sometimes the name of the attribute is 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

Page 79: Moose workshop

Example: init_arg

• Use to modify constructor parameter name

• 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

Page 80: Moose workshop

Example: init_arg

• Use init_arg => undef to prevent dependency injection

• Use with caution

has '_genetic_code' => ( is => 'ro', lazy_build => 1, init_arg => undef, );

Friday, July 6, 12

Page 81: Moose workshop

Weak References

Course

Student Student Student

Learns At

Friday, July 6, 12

Page 82: Moose workshop

Weak References

•When an object leaves scope, it’s ref-count decreases

• Circular references cause objects to remain in memory

•Weak references to the rescue

Friday, July 6, 12

Page 83: Moose workshop

Weak Ref

•When a Course object leaves the last 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/3031636Friday, July 6, 12

Page 84: Moose workshop

Triggers

• Called when attribute value is set

• Called 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

Page 85: Moose workshop

Friday, July 6, 12

Page 86: Moose workshop

Lab

• Improve Students/Course example to use native delegation

• Use method modifiers to add custom logic

Friday, July 6, 12

Page 87: Moose workshop

Meta Moose

Friday, July 6, 12

Page 88: Moose workshop

What is MOP

• An abstraction to build abstractions - 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

Page 89: Moose workshop

MOP Parts

• The Class protocol

• The Attribute protocol

• The Method protocol

• The Instance protocol

Friday, July 6, 12

Page 90: Moose workshop

Moose and Class::MOP

•Moose is built on top of Class::MOP

• Prefixed Moose::Meta (for example Moose::Meta::Class)

• Get with $self->meta

Friday, July 6, 12

Page 91: Moose workshop

What Meta Can Do For You

• Class and Object Introspection

•Modify objects and classes dynamically (add/remove methods, attributes, roles)

• Add more information to attributes (label, persistent)

Friday, July 6, 12

Page 92: Moose workshop

Object Introspection

package main;

my $z = Zombie->new;

for my $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/3032056Friday, July 6, 12

Page 93: Moose workshop

Object Introspection

• All meta methods listed under:

Class::MOP::Class and Moose::META::Class

• In most cases, using roles is a better idea than dynamic checking

Friday, July 6, 12

Page 94: Moose workshop

Validate Type Constraints

• Use $self->meta->get_attribtue(attr)->type_constraint to get meta object of type constraints

• $constraint->check( $value )

• $constraint->validate( $value ) or die $constraint->get_message( $value );

• $constraint->assert_valid( $value )

Friday, July 6, 12

Page 95: Moose workshop

Class Modification

• $meta->add_attribute

• $meta->remove_attribute

• $meta->add_method

• $meta->remove_method

• $meta->make_immutable

Friday, July 6, 12

Page 96: Moose workshop

Moose::Util

• A bundle of useful functions that take away some of the pain of working with meta

• Start here before implementing your own.

Friday, July 6, 12

Page 97: Moose workshop

Moose::Util

• find_meta( $class_or_obj )

• does_role( $class_or_obj, $role )

• apply_all_roles( $applicant, @roles )

• english_list( @items )

Friday, July 6, 12

Page 98: Moose workshop

Friday, July 6, 12

Page 99: Moose workshop

Moose TypesCustomizable Type System

Friday, July 6, 12

Page 100: Moose workshop

Moose Type System

• Verify attribute values are “valid” - 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

Page 101: Moose workshop

Stock Types

• Bool, Maybe[‘a], Str, Num, Int, ClassName, RoleName

• Ref, ScalarRef[‘a], ArrayRef[‘a], HashRef[‘a], CodeRef

• RegexpRef, GlobRef, FileHandle

•Object

Friday, July 6, 12

Page 102: Moose workshop

Type Registry

• A type is an instance of Moose::Meta::TypeConstraint

• All types are stored in the type registry. Use get_type_constraint_registry from Moose::Util::TypeConstraints to get it

Friday, July 6, 12

Page 103: Moose workshop

Example: Print All Constraints

use v5.14;use Data::Dumper;use Moose::Util::TypeConstraints;

my $registry = Moose::Util::TypeConstraints::get_type_constraint_registry();

print Dumper($registry->type_constraints);

Friday, July 6, 12

Page 104: Moose workshop

Extending The Type System

• Every Moose object is a new type

• There are also helper methods to create new types

• A new type can be named or anonymous

Friday, July 6, 12

Page 105: Moose workshop

Named Subtypes: enum

use v5.14;

package Person::Types;use Moose::Util::TypeConstraints;

enum '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

Page 106: Moose workshop

Anonymous Subtypes: enum

use v5.14;

package Person;use Moose;use Moose::Util::TypeConstraints;

has 'eyecolor' => (    is => 'ro',    isa => enum [qw/gray blue brown green/],);

Friday, July 6, 12

Page 107: Moose workshop

More Subtypes

• subtype( %opts ) - Create a new 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

Page 108: Moose workshop

Subtypes

• Provide ‘as’ to specify base type

• Provide ‘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

Page 109: Moose workshop

Subtypes Do’s

•Define all your subtype in a single module for code reuse. Use that module from every Moose class

Friday, July 6, 12

Page 110: Moose workshop

Subtypes Do’s

• Prefer namespaced subtypes: ZombieApocalipse::Human::EyeColor is better thanEyeColor

• Zombies may have different eye color ...

Friday, July 6, 12

Page 111: Moose workshop

Type CoercionProceed With Caution

Friday, July 6, 12

Page 112: Moose workshop

Type Coercion

• Automatically convert invalid data to valid

• Int ------> ArrayRef[Int]

• Str ------> Person

• High risk - an invalid value could coerce thus skipping type validation

Friday, July 6, 12

Page 113: Moose workshop

Type Coercion

use v5.14;

package Student;use Moose;use Moose::Util::TypeConstraints;

subtype 'GradesArray', as 'ArrayRef[Int]';coerce 'GradesArray', from 'Int', via { [ $_ ] };

has 'grades', is => 'ro', isa => 'GradesArray', coerce => 1;

package main;my $s = Student->new( grades => 77 );

print $s->dump;

Friday, July 6, 12

Page 114: Moose workshop

Coercion Don’ts

•Don’t add coercion on Moose’s subtypes (unfortunately it’ll work)

• Generally, try to avoid coercions

Friday, July 6, 12

Page 115: Moose workshop

Friday, July 6, 12

Page 116: Moose workshop

Subtypes Lab

•Define a new subtype for hex numbers (numbers of the format 0x22)

• Add a coercion from HexNum to Int

Friday, July 6, 12

Page 117: Moose workshop

MooseXMore Than Moose

Friday, July 6, 12

Page 118: Moose workshop

eXtending Moose

•Moose is (relatively) easy to change and 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

Page 119: Moose workshop

Useful MooseX

•MooseX::StrictConstructor

•MooseX::Singleton

•MooseX::Declare

•MooseX::FollowPBP

•MooseX::Privacy

•MooseX::SingleArg

•MooseX::MultiMethods

•MooseX::HasDefaults

•MooseX::APIRole

Friday, July 6, 12

Page 120: Moose workshop

Simple eXtensions

•MooseX::StrictConstructor

•MooseX::Singleton

•MooseX::FollowPBP

•MooseX::SingleArg

•MooseX::HasDefaults

•MooseX::Privacy

Friday, July 6, 12

Page 121: Moose workshop

MooseX::StrictConstructor

• Throw exception if constructor was passed an unexpected argument

package Contact;use Moose;use MooseX::StrictConstructor;

has 'email', is => 'ro';has 'name', is => 'ro';

package main;

# Throw an exceptionContact->new( name => 'Bob', emial => '[email protected]');

Friday, July 6, 12

Page 122: Moose workshop

MooseX::Singleton

• Create only one instance of a class

• 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

Page 123: Moose workshop

MooseX::FollowPBP

• Use set_x and get_x as default reader and writer

• SEE ALSO: Perl::Critic

Friday, July 6, 12

Page 124: Moose workshop

MooseX::SingleArg

• Easily create single arg constructor (without wrapping BUILDARGS)

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

Page 125: Moose workshop

MooseX::HasDefaults

• Automatically use:

is => ‘ro’

or :

is => ‘rw’

use v5.14;

package Point;use Moose;use MooseX::HasDefaults::RO;

has ['x', 'y', 'z'];

Friday, July 6, 12

Page 126: Moose workshop

MooseX::Privacy

• Restrict visibility of methods

• private can only 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

Page 127: Moose workshop

Heavy Lifting

• Logging: Log4perl

•MooseX::APIRole

•MooseX::Declare

•MooseX::MultiMethods

Friday, July 6, 12

Page 128: Moose workshop

Log4perl

• Logging is all about keeping a record of your info at runtime

• Log4perl lets you control how your application logs its data

• Perl’s implementation of log4j

Friday, July 6, 12

Page 129: Moose workshop

Log4perl alternatives

• print/warn debug messages: Too simple for real apps

• roll-your-own: Too much work...

• Log4perl is currently the best logging solution for medium-large perl applications

Friday, July 6, 12

Page 130: Moose workshop

Log4perl and Moose

• Use MooseX::Log::Log4perl role in your 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

Page 131: Moose workshop

Log4perl Output

• Completely customizable

•Output log to: Screen, File, Socket, DBI, and more

• Example:

[2012/07/06 14:54:34] 130.pl MyApp::go 130.pl (9) MyApp - Starting method go[2012/07/06 14:54:34] 130.pl MyApp::go 130.pl (10) MyApp - Go go go[2012/07/06 14:54:34] 130.pl MyApp::go 130.pl (12) IO - reading data

Friday, July 6, 12

Page 132: Moose workshop

Log4perl Configuration

log4perl.logger = DEBUG, Screen, Logfile

log4perl.appender.Screen = Log::Log4perl::Appender::Screenlog4perl.appender.Screen.layout = PatternLayoutlog4perl.appender.Screen.layout.ConversionPattern= \ [%r] %F %l %c - %m%n

log4perl.appender.Logfile = Log::Log4perl::Appender::Filelog4perl.appender.Logfile.filename = my.loglog4perl.appender.Logfile.layout = PatternLayoutlog4perl.appender.Logfile.layout.ConversionPattern = \ [%d] %F %l %c - %m%n

Friday, July 6, 12

Page 133: Moose workshop

Log4perl Initialization

• Load configuration file on startup with: Log::Log4perl::init(filename)

package main;use Log::Log4perl;

Log::Log4perl::init('log.conf');

Friday, July 6, 12

Page 135: Moose workshop

MooseX::APIRole

• Automatically create a role out of a class

• 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 methodswith 'Logger::API';

Friday, July 6, 12

Page 136: Moose workshop

MooseX::Declare

• Use modern OO syntax for your moose objects

• ‘class’ keywords declares a class. Inside, MooseX::Method::Signatures is in effect.

• ‘role’ keyword declares a role

Friday, July 6, 12

Page 137: Moose workshop

MooseX::Declare

use MooseX::Declare;

class BankAccount {    has 'balance' => ( isa => 'Num', is => 'rw', default => 0 );

    method deposit (Num $amount) {        $self−>balance( $self−>balance + $amount );    }

    method withdraw (Num $amount) {        my $current_balance = $self−>balance();        ( $current_balance >= $amount )        || confess "Account overdrawn";        $self−>balance( $current_balance − $amount );    }}

Friday, July 6, 12

Page 138: Moose workshop

MooseX::Declare

• Still experimental, API could change

• Inside a method $self is already defined for you, as well as other input parameters

• Awesome. perldoc MooseX::Declare for more

Friday, July 6, 12

Page 139: Moose workshop

MooseX::MultiMethods

• Allow multi methods dispatcher based on input arguments

•Define multiple handlers instead of long if-else blocks

Friday, July 6, 12

Page 140: Moose workshop

MooseX::MultiMethods

package Paper; use Moose;package Scissors; use Moose;package 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 Rockprint $game->play(Paper->new, Rock->new);

Friday, July 6, 12

Page 141: Moose workshop

Friday, July 6, 12

Page 142: Moose workshop

Thanks For Listening

• Ynon Perek

[email protected]

• ynonperek.com

Friday, July 6, 12