9781118013847
moose.html

Chapter 13. Moose

WHAT YOU WILL LEARN IN THIS CHAPTER

  • Understanding why Moose was created

  • Declaring attributes

  • Using Constructors

  • Using Methods

  • Declaring Inheritance

  • Using Type constraints

  • Using Method modifiers

  • Understand popular Moose extensions

  • Understanding the Beauty of Roles

  • Converting old-style classes to Moose

What is Moose?

In the beginning there was Perl and on the fifth version Larry blessed references and said “Let there be objects” and all was good.

Well, sort of. Chapter 12, Object Oriented Perl showed how to create objects with Perl’s builtin bless syntax, but there’s a lot of tedium in validating that your data is correct and setting up your classes properly. If you’re familiar with object oriented programming in other languages, you may have found Perl’s implementation to be a bit crufty. You’d be right.

To work around Perl’s rather simple object facilities, many programmers have taken a swing at making it easier to write objects. There is Class::MakeMethods, Class::MethodMaker, Class::BuildMethods, Class::Accessor, Object::Tiny, and so on. In fact, as of this writing, there are almost 500 different packages in the Class:: and Object:: namespaces, many of which aren’t actually object builders. How do you figure out which one you really want? They offer bewildering sets of features and you certainly don’t have time to evaluate all of them.

Eventually, a rather insane programmer named Stevan Little wrote Moose. Moose is probably one of the most advanced object systems available. It draws heavily on theory from a wide variety of sources, but puts it all together in a way that suits Perl. Many companies are now adopting Moose as the standard way of doing object-oriented programming in Perl and the benefits are huge, not the least of which is that it has a very predictable syntax, meaning that it’s often much easier to understand a Moose class than a regular Perl class.

Though we’ll see quite a bit of Moose in this chapter, we’re only going to scratch the surface of its capabilities, so consider this chapter an introduction to the most common features, not an in-depth tutorial.

Moose is not shipped with Perl, so you’ll need to install it from the CPAN.

Basic Syntax

Writing a Moose class is mostly a matter of declaring attributes and methods. For example, let’s say you wanted to create a drawing program. You might start with a simple x and y coordinate system.

package Point;
use Moose;
has 'x' => ( is => 'ro', isa => 'Num', required => 1 );
has 'y' => ( is => 'ro', isa => 'Num', required => 1 );
1;

And that’s all you need! Here’s an example of how to use it:

my $point = Point->new( { x => 3.2, y => −7 } );
printf "x: %f y: %f\n" => $point->x, $point->y;

And that prints out:

x: 3.200000 y: −7.000000

Now maybe you want a line. A line is defined by its two endpoints.

package Line;
use Moose;
has 'point1' => ( is => 'ro', isa => 'Point', required => 1 );
has 'point2' => ( is => 'ro', isa => 'Point', required => 1 );
sub length {
    my $self = shift;
    return sqrt(
        ($self->point1->x - $self->point2->x)**2
        +
        ($self->point1->y - $self->point2->y)**2
    );
}
1;

And to use it:

my $line = Line->new({
   point1 => Point->new({ x => 1, y => −1 }),
   point2 => Point->new({ x => 4, y => −5 }),
});
print $line->length;

And that prints 5 as the length of that line.

Note

In Chapter 12, Object Oriented Perl, we showed constructors as always receiving a hash reference as the argument. This makes it easier to know that you really do have key/value pairs and you see the error quickly when the object code tries to dereference something that is not a hash reference.

Moose makes this optional. While we provided hash references to the constructors in our examples, you don’t need them with Moose. You may find your code cleaner without them.

my $line = Line->new(
   point1 => Point->new( x => 1, y => −1 ),
   point2 => Point->new( x => 4, y => −5 ),
);
print $line->length;

However, if you always stick to a hash references, you’ll get a warning if you pass an odd number of elements by accident (remember, a hash is a set of key and value pairs).

Amongst other things, you’ll note that we did not use strict or warnings. Why not? Because when you use Moose, it automatically uses strict and warnings for you.

You also didn’t even have to declare a new() constructor. Moose will provide that for you, allowing you to assign values to the declared attributes. Much easier, right? And if you tried to do this:

Point->new({ x => 'Foo', y => 3 });

You get a long stack trace starting with the message:

Attribute (x) does not pass the type constraint because:
  Validation failed for 'Num' with value Foo at ...

Needless to say, this is much easier than working with Perl’s standard bare bones OO. The CPAN page Moose::Manual::Unsweetened shows how much easier with a Moose vs Perl 5 OO example.

Note

A stack trace is a list of every subroutine called, along with their arguments, from the point where the error occurred back to the calling code that generated the error. They’re long and messy, but they’re invaluable in helping you understand how your code committed suicide.

Attributes

An attribute is a piece of data, such as quantity or color, associated with an object. They’re described in full in perldoc Moose::Manual::Attributes. The simplest attribute looks like this:

has 'name' => ( is => 'ro' );

The is => 'ro' means that the attribute is read-only. You can set it when you create the object but you cannot change it later. If you want it to be read-write, declare it with 'rw'.

has 'name' => ( is => 'rw' );

Note

Actually, you don’t even need the is => '...' for the attribute, but that creates an attribute with no accessor. There are reasons you might wish to do that, but we won’t cover that here.

Usually you want to specify what types the attribute can accept. For example, a name attribute should probably be a string:

has 'name' => ( is => 'ro', isa => 'Str' );

The isa => 'Str' says “this attribute isa string”. You can see the full list of built-in types with perldoc Moose::Util::TypeConstraints.

Any
 Item
     Bool
     Maybe[`a]
     Undef
     Defined
         Value
             Str
                 Num
                     Int
                 ClassName
                 RoleName
         Ref
             ScalarRef[`a]
             ArrayRef[`a]
             HashRef[`a]
             CodeRef
             RegexpRef
             GlobRef
             FileHandle
             Object

Most of those types should be self-explanatory, except for a few items we haven’t gone in-depth about.

Any means “anything” (duh). An Item is the same as Any. You could think of it as the difference between “something” and “anything”. In other words, it will not be of much practical difference in your work.

A Str is a string, a Num is any number, an Int is an integer, and so on. A Bool may be undef, the empty string, 0 or 1.

A Value is “anything which is defined and not a reference”. This can include something called a glob (instead of a GlobRef, as listed in the Ref types).

The [`a] is a type parameter. For any types, you can follow them with the type parameter, replacing the `a with a given type. For example:

has 'temperatures' => (
    is       => 'rw',
    isa      => 'ArrayRef[Num]',
    required => 1,
);

And that will allow you to create a temperatures attribute that, if present, must be an array reference containing only numbers. You could use that to create an average_temperature() method. We used required => 1 to ensure that the temperatures attribute must be supplied.

use List::Util 'sum';
sub average_temperature {
    my $self = shift;
    my $temperatures = $self->temperatures;
    my $num_temperatures = @$temperatures
      or return; # an empty temperatures arrayref
    return sum(@$temperatures) / $num_temperatures;
}

In fact, you can force the $temperatures array reference to have at least once value:

use Moose;
use Moose::Util::TypeConstraints; # for subtype, as, where
has 'temperatures' => (
    is       => 'rw',
    isa      => subtype( as 'ArrayRef[Num]', where { @$_ > 0 } ),
    required => 1,
);

Then, if you try to pass an empty array reference, Moose will throw an exception. That’s a bit obscure right now, but we’ll cover that when we get to type constraints later in this chapter (and inline subtypes like that are generally not recommended in case other modules need that subtype).

A Maybe[`a] value will allow you to have either undef or a specific value in an attribute. So Maybe[Int] will allow either undef or Int.

If you don’t like your setter and getter to be a single method, then separate them:

has 'name' => (
    is     => 'rw',
    isa    => 'Str',
    writer => 'set_name',
    reader => 'get_name',
);

Or you could use the MooseX::FollowPBP and all attributes will be named get_* and set_* for you (but you won’t get set_* unless you use is => 'rw').

use Moose;
use MooseX::FollowPBP; # get_name and set_name will now be created
has 'name' => (
    is  => 'rw',
    isa => 'Str',
);

You can also use isa to declare that the attribute is a specific type of object:

has 'birthdate' => (
    is  => 'ro',
    isa => 'DateTime',
);

And now if you pass something that is not a DateTime object to birthdate, Moose will throw an exception.

You can also assign defaults to attributes. With the following, if you don’t supply a mininum_age to the constructor, it will default to 18.

has 'mininum_age' => (
    is      => 'ro',
    isa     => 'Int',
    default => 18,
);

If your default is a reference, you must wrap it in sub {} to ensure a new reference is returned every time.

has 'mininum_age' => (
    is      => 'ro',
    isa     => 'DateTime::Duration',
    default => sub { DateTime::Duration->new( years => 18 ) },
);

Moose will throw an exception if you do not wrap that referenec in a subroutine reference because it is assumed that you do not want every class changing the same reference.

Note

The name MooseX::FollowPBP refers to Perl Best Practices, an O’Reilly book by Damian Conway. It’s a few years old and some module recommendations are out of date, particularly the reference to using Class::Std to create your classes, but it’s still an excellent book.

There’s a lot more power in attributes, so read Moose::Manual::Attributes to see what other nifty things you can do.

Constructors

As explained, Moose provides a new() constructor for you. If you need to alter its behavior, do not override new(). Instead, you use BUILDARGS to modify arguments before new() is called, and BUILD is used after the constructor is called when you need further validation of the state of the object. We’ll cover BUILDARGS first. It has a bit of an unusual syntax.

BUILDARGS

We saw from our Point example that we could create a point like this:

my $point = Point->new( x => 3, y => 2 );

But you might want to write Point->new( 3, 2 ). That’s what BUILDARGS is for.

package Point;
use Moose;
has 'x' => ( is => 'ro', isa => 'Num', required => 1 );
has 'y' => ( is => 'ro', isa => 'Num', required => 1 );
around 'BUILDARGS' => sub {
    my $orig  = shift;
    my $class = shift;
    if ( @_ == 2 ) {
        my ( $x, $y ) = @_; # Point->new( x, y );
        return $class->$orig( x => $x, y => $y );
    }
    else {
        # Point->new(@list_or_hashref);
        return $class->$orig(@_);
    }
};   # Needs a trailing semicolon
1;

That will let you write:

my $point = Point->new( 3, 2 );
# or, the same thing
my $point = Point->new( x => 3, y => 2 );

When you include the use Moose line, your class will inherit from Moose::Object. There is a BUILDARGS method that checks to see if you’ve passed a hashref or a list and ensures that the object is called correctly.

The around() function takes the name of a method and a subroutine reference. A reference to the original method is passed as the first argument ($orig) and the invocant (a class name in this case, but it can also be an instance of an object) is the second argument. Any further arguments are passed as normal in @_. You can then test your arguments and call $class->$orig(@new_arguments) to build your object.

The around() function is called a method modifier and we’ll cover them later in this chapter.

BUILD

The BUILD method is called after new() and can be used to validate your object. Its return value is ignored, and, unlike BUILDARGS, it has a normal method syntax. You should throw an exception if you encounter any errors. In the case of our Line example, let’s assume that our two points cannot be the same.

package Line;
use Moose;
use Carp 'croak';
has 'point1' => ( is => 'ro', isa => 'Point', required => 1 );
has 'point2' => ( is => 'ro', isa => 'Point', required => 1 );
sub BUILD {
    my $self = shift;
    if (   $self->point1->x == $self->point2->x
        && $self->point1->y == $self->point2->y )
    {
        croak("Line points must not be the same");
    }
}
sub length {
    my $self = shift;
    return sqrt(
        ($self->point1->x - $self->point2->x)**2
        +
        ($self->point1->y - $self->point2->y)**2
    );
}
1;

You can see that this BUILD method lets us have tighter validation of our object than attributes alone can provide. If we pass in the same point twice, or two separate points with the same coordinates, Line->new will croak() with an appropriate error message.

We could have used BUILDARGS, too, to allow us to pass a list of two points. This would have looked very similar to the Point::BUILDARGS method.

around 'BUILDARGS' => sub {
    my $orig  = shift;
    my $class = shift;
    if ( @_ == 2 ) {
        my ( $point1, $point2 ) = @_;
        return $class->$orig(
            point1 => $point1,
            point2 => $point2,
        );
    }
    else {
        return $class->$orig(@_);
    }
};   # Don't forget that trailing semicolon!

And now we can write:

my $line = Line->new(
    Point->new( 1, −1 ),
    Point->new( 4, −5 ),
);

Instead of our more verbose:

my $line = Line->new(
   point1 => Point->new( x => 1, y => −1 ),
   point2 => Point->new( x => 4, y => −5 ),
);

Inheritance

Inheritance in Moose is very easy. With Perl’s basic OO behavior, TV::Episode::Broadcast would inherit from TV::Episode via one of these techniques:

use base 'TV::Episode';
# or
use parent 'TV::Episode';
# or
use TV::Episode;
our @ISA = 'TV::Episode';

In Moose, every Moose object inherits from Moose::Object. So you can’t use those techniques directly or else you lose your inheritance from Moose::Object. Instead, you use the extends function:

package TV::Episode::Broadcast;
use Moose;
extends 'TV::Episode';

That does assumes your parent class is a Moose class. If you’re using Moose, that’s usually the case, but sometimes you’ll need to inherit from a class not built with Moose. See MooseX::NonMoose for that. If TV::Episode::Broadcast is written with Moose and TV::Episode is not a Moose class:

package TV::Episode::Broadcast;
use Moose;
use MooseX::NonMoose;
extends 'TV::Episode';

That will take care of lots of fiddly bits for you and everything should work fine.

Taking Care of Your Moose

When you use Moose, Moose exports a lot of helper functions into your class, such as:

  • after

  • around

  • augment

  • before

  • extends

  • has

  • inner

  • override

  • super

  • with

You’ve seen a couple of them, such as has and around. Later, when someone is using your class, if they write:

$moose_object->can('around');

That will return a true value because subroutines and methods are treated the same way by Perl. You probably don’t want this behavior and there’s no need to leave all of those helper functions lying around. To deal with this, use namespace::autoclean (available on the CPAN).

package TV::Episode::Broadcast;
use Moose;
use namespace::autoclean;
# more code here
1;

That will automatically remove all of those helper functions for you.

Also, just before that trailing 1, you want to make your class immutable. A standard skeleton of a good Moose class looks like this:

package My::Class::Name;
use Moose;
use namespace::autoclean;

# your code here

__PACKAGE__->meta->make_immutable;

1;

The __PACKAGE__->meta->make_immutable doesn’t change the behavior, but it does a few internal things that make your code run a bit faster. The tradeoff is that you can’t use metaprogramming (that’s what the __PACKAGE__->meta is often used for) to change your class later, but if you know what that is, you know when to not make your class immutable.

Note

What is metaprogramming? If you like the Moose object system but you wish to change or extend some of its core behavior, then you’re beginning to get an idea of what metaprogramming is. See the full Moose docs on the CPAN. Every time you use has, extends and a variety of other functions, you’re altering a class definition and, under the hood, it’s done with metaprogramming.

If you really want to know, you can start with perldoc Moose::Manual::MOP (MOP stands for Meta Object Protocol) and then start reading Moose::Cookbook::Meta::Recipe1 through Moose::Cookbook::Meta::Recipe7. It’s pretty advanced stuff, so don’t worry if it doesn’t make much sense to you.

When using Moose, the mro (Method Resolution Order) pragma is in effect. Not only does this use breadth-first C3 method resolution order that was described in Chapter 12, Object Oriented Perl, it also substitutes the SUPER::some_method call with next::method (and $object->can($some_method) becomes $object->next::can($somet_method)). If you do not use multiple inheritance — and its use is discouraged — you will likely see no difference in behavior aside from the syntax.

For example, you may recall the as_string() method from TV::Episode::Broadcast:

sub as_string {
    my $self    = shift;
    my $episode = $self->SUPER::as_string;
    my $date    = $self->broadcast_date;
    $episode .= sprintf "%-14s - %4d-%2d-%2d\n" => 'Broadcast date',
      $date->year,
      $date->month,
      $date->day;
    return $episode;
}

That overrides the TV::Episode::as_string() method. When you convert it to Moose, you make the following change:

# my $episode = $self->SUPER::as_string;
    my $episode = $self->next::method;

Note that there is no need to embed the method name in the call to the parent method. This can avoid annoying bugs if you try to rename a method.

As a lovely bit of syntactic sugar, you can also override Moose methods with the override function:

package TV::Episode::Broadcast;
use Moose;
extends 'TV::Episode';
# lots of code
override 'as_string' => sub {
    my $self    = shift;
    my $episode = super();
    my $date    = $self->broadcast_date;
    $episode .= sprintf "%-14s - %4d-%2d-%2d\n" => 'Broadcast date',
      $date->year,
      $date->month,
      $date->day;
    return $episode;
}

This has two very strong advantages. First, it’s excellent documentation to a maintenance programmer that this method overrides a parent method. Second, the call to super() passes the same arguments to the parent method (if any) that were passed to the child’s as_string() method, ensuring that you do not accidentally change the interface. Any arguments to super() are ignored and you cannot change @_.

Advanced Syntax

In a few short pages, we’ve shown you Moose and how easy it is to create objects with it. We’ve also mentioned several times that there are “advanced” features of Moose. We’ll take a look at a few of those now.

Type constraints

When you declare an attribute, you can also declare what type it is:

package Hand;
use Moose;
has 'fingers' => (
    is       => 'ro',
    isa      => 'Int',
    required => 1,
);

Now presumably, your Hand can’t have less than zero fingers. You could write a BUILD method and validate that your object has zero or more fingers, but you really don’t want to do that for every class where you need to assert a non-negative integer. So you can use Moose::Util::TypeConstraints to create a custom type.

package Hand;
use Moose;
use Moose::Util::TypeConstraints;
use namespace::autoclean;
subtype 'NonNegativeInteger'
    => as      'Int'
    => where   { $_ >= 0 }
    => message { "A Hand must have 0 or more fingers, not $_" };
has 'fingers' => (
    is       => 'ro',
    isa      => 'NonNegativeInteger',
    required => 1,
);
__PACKAGE__->meta->make_immutable;
1;

With that subtype, when you try to create an object with fewer than 0 fingers:

Hand->new( fingers => −3 );

You get a very useful error message (with a stack trace that I’ve omitted).

Attribute (fingers) does not pass the type constraint because:
  A Hand must have 0 or more fingers, not −3 at ...

To create your own subtypes, you use the Moose::Util::TypeConstraints module to add a few new helper functions to your code.

A subtype is a more specific version of an existing type. In this case, it’s a more specific version of an Int (integer). That’s what the as => 'Int' is for.

subtype 'NonNegativeInteger'
    => as      'Int'
    => where   { $_ >= 0 }
    => message { "You must provide an integrer > 0, not $_" };

The where { ... } is a code block that is executed with $_ set to the supplied value of the attribute. If it returns false, the type check fails and the optional message { ... } is displayed. If you don’t supply the message, the error can be rather confusing:

Attribute (fingers) does not pass the type constraint because:
  Validation failed for '__ANON__' with the value −3 ...

However, what you really want to do is create your own type library so that you can share these types across your various classes. It’s recommended that you do two things:

  • Put all of your subtypes in one package that you use.

  • Name you subtypes so they won’t conflict with other subtypes.

Let’s say that you work for a company named “My Company”. Customers must be 18 years old or older, so we’ll create a subtype to handle that. Here’s one way to create the type package:

package My::Company::Moose::Types;
use Moose::Util::TypeConstraints;
use DateTime;
subtype 'MyCompany:NonNegativeInteger'
    => as      'Int'
    => where   { $_ >= 0 }
    => message { "You must provide a non-negative integer, not $_" };
subtype 'MyCompany:18orOlder'
    => as      'DateTime'
    => where   { ( DateTime->now - $_ )->years >= 18 }
    => message {
        my $age = ( DateTime->now - $_ )->years;
        "DateTime supplied must be 18 years old or older, not $age years"
    };
1;

Note

My::Company::Moose::Types.pm available for download at Wrox.com.

Note

Note that the name of our sample subtypes are My:CompanyNonNegativeIntger and MyCompany:18orOlder. Neither of those is a valid package name, but that’s OK. Just make sure that your names are don’t conflict with anything else.

Now let’s create a little test program for it

{
    # remember that the package declaration is lexically scoped!
    package Person;
    use Moose;
    use DateTime;
    use My::Company::Moose::Types;
    has 'name' => (
        is       => 'ro',
        isa      => 'Str',
        required => 1,
    );
    has 'age' => (
        is       => 'ro',
        isa      => 'MyCompany:18orOlder',
        required => 1,
    );
}
my $youngster = Person->new(
    name => 'Youngster',
    age  => DateTime->new(
        year  => 2000,
        month => 1,
        day   => 1,
    ),
);

When you run that, it will generate an error similar to the following (at the time the author wrote this):

Attribute (age) does not pass the type constraint because:
   DateTime supplied must be 18 years old or older, not 12 years at ...

Note

You may have been curious about this construct:

my $age = ( DateTime->now - $_ )->years;

In Chapter 12, Object Oriented Perl, we showed how subtracting one DateTime object from another returns a DateTime::Duration object. In the Exercises for Chapter 12, Object Oriented Perl we showed how to use the years() method with DateTime::Duration to determine the number of years the duration represents. When you wrap parentheses around expressions, the code inside of the parentheses is executed and the return value is substituted for the parentheses, making the above more or less equivalent to the following:

my $duration = DateTime->now - $_;
my $age      = $duration->years;

This type of code is moderately common with experienced Perl hackers and you will see it in production code, so it seemed appropriate to twist your brains a little more at this time.

Method modifiers

One interesting feature of Moose is the concept of method modifiers. These are subroutines that you can attach to existing Moose subroutines to add to or alter behavior. You have already seen the around and override modifiers. There are also before, after, augment and inner modifiers.

The before and after Modifiers

As you might expect, the before modifier runs before the method is called. Sometimes you want additional checks on methods that may not make sense for a type constraint, or a subclass may have different checks from another subclass. A before modifier can make life simpler. The modifier will be passed the same arguments as the original method. For example:

before 'buy_item' => sub {
    my ( $self, $item ) = @_;
    if (   $item->is_age_restricted
        && $self->age < $self->locale->minimum_age_to_purchase($item) )
    {
        my $name      = $self->name;
        my $item_name = $item->name;
        croak "$name is not old enough to buy $item";
    }
};

Note that unlike our example with around and override, you do not need to explicitly call the actual method. The before modifier will be called automatically before the modified method is called. Also, any changes made to @_ are ignored, as is any return value from the modifier.

The after modifier behaves similarly to the before modifier and runs immediately after the execution of the method.

after 'buy_item' => sub {
    my ( $self, $item ) = @_;
    if ( $item->is_age_restricted ) {
        $self->log_purchase($item);
    }
};

As with the before modifier, changes to @_ are ignored, as is the return value.

The augment/inner Pair

The augment and inner modifiers work together and are the two modifiers that Perl programmers get the most confused about. Effectively, they let you call the parent method instead of the child method. The parent is responsible for calling the child methods for you. Or to put it another way, it sort of inverts the inheritance tree. Got that?

No, you may not have gotten that, so let’s go with an example.

When you have a name attribute, it can be kind of hard to stop someone from setting a name to dsn334548h21234;& and, in fact, there’s a good chance you don’t want to do that. Some people have names that are just numbers, or unpronounceable symbols, or perhaps written in a character set you don’t recognize. More importantly, getting a name wrong is very common and tends not to be disastrous.

Calculating the payroll wrong would be disastrous, however, so you might want a bit more protection for the salary() method. When you create an Employee class and you’ve designed it to be subclassed, there might be a variety of subclasses that implement the salary() method in different ways. Now look at how someone writing payroll code might calculate the payroll:

my @employees = $company->get_employees;
my $month     = $company->get_fiscal_month;
my $payroll   = 0;
foreach my $employee (@employees) {
    $payroll += $employee->salary($month);
}

The @employees array might contain a wide variety of different payroll subclasses, including Employee::Standard, Employee::Manager, Employee::Consultant and so on. Each of those might have salary methods that behave differently, such as Employee::Consultant returning a Salary::External object, Employee::Standard expecting to receive “hours worked” instead of a fiscal month and a completely different subclass with a bug returning a negative salary if the month is February.

In short, you don’t want the salary() method to be wrong. This is how you might implement salary() in your Moosified (your author should trademark that term) Employee base class, using the inner() function.

package Employee;
use Moose;
# lots of code omitted
sub salary {
    my ( $self, $fiscal_month ) = @_;
    unless ( $fiscal_month->isa('Fiscal::Month') ) {
        croak "Argument to salary() must be a Fiscal::Month object";
    }
    my $salary = inner(); # this calls the child version of salary()
    if ( ref $salary or $salary < 0 ) {
        croak "Salary must be a non-negative number: $salary";
    }
    return $salary;
}

The inner() function calls your subclass implementation of salary() and that must be declared with augment():

package Employee::Manager.
use Moose;
extends 'Employee';
# lots of code omitted
augment 'salary' => sub {
    my $self = shift;
    # lots of code
    return $salary;
};

Assuming all subclasses properly declared salary() with augment, when you call $employee->salary, regardless of what subclass you are using, you will get the superclass Employee::salary() method and it will call inner() to call the augmented salary method in the child class, passing it the same arguments that you passed to salary(). You can have then program pre- and post- conditions into your salary() method to verify that you are not accepting or returning incorrect values.

Note

Those of you with a computer science background may recognize augment/inner as being able to implement a technique known as Design by Contract. See http://en.wikipedia.org/wiki/Design_by_contract for more details.

If you want to use this technique with non-Moose classes, you can have this in your Employee class:

sub salary {
    my ( $self, $fiscal_month ) = @_;
    unless ( $fiscal_month->isa('Fiscal::Month') ) {
        croak "Argument to salary() must be a Fiscal::Month object";
    }
    my $salary = $self->_salary($fiscal_month); # inner()
    if ( ref $salary or $salary < 0 ) {
        croak "Salary must be a non-negative number: $salary";
    }
    return $salary;
}

And in your documentation, explain that all subclasses must implement salary() as _salary() (note the leading underscore).

Roles

A role provides additional methods for a class, along with methods it requires to implement those methods. There are several implementations in Perl, but the most common is Moose::Role.

Generally speaking, this set of methods should be something that the class should provide, but is not inherent to the class’s expert knowledge. For example, with a Person class, they may have a name, a birthdate and be able to “talk”. But should a person intrinsically know how to serialize themselves with an as_json() method? Probably not, but you’ll notice that the TV::Episode classes and subclasses all had the ability to serialize themselves with an as_string() method. In fact, many completely unrelated classes might have an as_json() method, so it’s a perfect candidate to put into a role.

Note

Though not covered in extensively in this book, JSON, short for JavaScript Object Notation, is a very popular method of serializing data, converting it to a format that can be shared or stored. It is formally described at http://www.json.org/, but the Wikipedia article gives better examples: http://en.wikipedia.org/wiki/Json.

For the examples that require the JSON module, you will need to install it from the CPAN.

Basic Roles

A class that uses one or more roles is said to consume those roles. As for the roles, they are defined by the methods they require the class to provide and the methods the role provides to the class. Here’s how our role providing the as_json() method might look.

package Role::Serializable::JSON;
use Moose::Role;
use JSON 'encode_json';
# you can list multiple methods here
requires qw(
  serializable_attributes
);
sub as_json {
    my $self = shift;
    my %object = map { $_ => $self->$_ } $self->serializable_attributes;
    return encode_json( \%object );
}

This role will require the consuming class to have a method named serializable_attributes(). If the class does not have this method, your class will throw an error similar to:

'Role::Serializable::JSON' requires the method
'serializable_attributes' to be implemented by
'Some::Class::Name'

This error will be thrown at compile time. No more 2:30AM frantic phone calls from work about Method not found errors!

Note

A role might only have a requires section and provide no methods. When it does so, it’s what other languages define as an interface: it guarantees that any class consuming the role has a well-defined set of methods the class provides.

The Role::Serializable::JSON method will provide the as_json() method to the class. However, the encode_json() function, imported into the role namespace, will not be provided.

A class consuming this role may look like this:

package Soldier;
use Moose;
use namespace::autoclean;
with "Role::Serializable::JSON";
has 'name' => ( is => 'ro', isa => 'Str', required => 1 );
has 'rank' => ( is => 'ro', isa => 'Str', required => 1 );
sub serializable_attributes {
    return qw( name rank );
}
__PACKAGE__->meta->make_immutable;
1;

And here’s a sample program:

my $soldier = Soldier->new(
    {
        name => "Schultz",
        rank => "Sergeant",
    }
);
print $soldier->as_json;

And that will print out the attribute key/value pairs as JSON:

{"name":"Schultz","rank":"Sergeant"}

You can reuse this role with many other classes, regardless of whether or not they inherit from one another.

Note

We’ve had a few examples of classes that have a hard-coded list of methods. You might find that annoying and so does your author. This is an example of where metaprogramming can make your life easier. Instead of requiring the serializable_attributes() method, you could write something like this:

sub as_json {
    my $self       = shift;
    my @attributes = map { $_->name }
      $self->meta->get_all_attributes;
    my %object     = map { $_ => $self->$_ } @attributes;
    return encode_json( \%object );
}

The $self->meta call returns an instance of the metaclass for the object and the get_all_attributes() method returns a set of attribute objects. Of course, you might wonder if some of those should not be serialized into a JSON data structure (such as a password attribute). Metaprogramming will allow you to extend your class behavior to say which data can and cannot be exposed like this.

If you’re using a class and want to know if the class has a particular role, you cannot use the $object->isa($some_class) method. That’s because roles are not inherited. Instead, you use the does() method:

print $soldier->does('Role::Serializable::JSON') ? 'Yes' : 'No';

That snippet will print Yes. Also, note that role methods are added directly into your class definition during role composition (when you use the with() function). This is known as flattening the methods into your class.

Advanced Roles

Roles allow you to share behavior among unrelated classes, but if that’s all they did, they wouldn’t be terribly exciting. After all, other languages have various strategies for accomplishing this, such as mixins in Ruby.

Roles, however, attempt to guarantee compositional safety. This is easier to explain with an example. Let’s say that you are writing a game and you want to create a PracticalJoke class. When someone walks into a room, a fuse() method is called on the PracticalJoke instance and it will explode() after the fuse is done. Being a good programmer, you know that your Bomb and Spouse classes each have fuse() and explode() methods. Table 13.1, “Table 13-1” explains their properties:

Table 13.1. Table 13-1

Method

Description

Bomb::fuse()

Burns for a fixed amount of time

Spouse::fuse()

Burns for a random amount time

Bomb::explode()

Lethal

Spouse::explode()

Wish it was lethal


For your practical joke, you want the Bomb::fuse() method because you want to control the time it takes to explode, but you want the Spouse::explode() method because you don’t want to actually kill the player. With inheritance, this is tricky and you need to set up delegates for one or the other.

package PracticalJoke;
use strict;
use warnings;
use base qw(Bomb Spouse);
sub new {
    ...
}
sub explode {
    my $self = shift;
    $self->Spouse::explode();
}

Having to write a delegate to $self->Spouse::explode() is because we inherit from Bomb first and if we just relied on inheritance, we would call the Bomb::explode() method and kill our hapless player. But hard-coding class names like this is doing work that object-oriented programming is supposed to do for us! And if we later decide to inherit from a different class that provides an explode() method, we have to find all of the places where we’ve hard-coded the Spouse::explode() method and rewrite them. This is begging for bugs and it’s more work than we should have to do.

Instead, let’s assume that we want to share the Bomb and Spouse behavior with many different classes and put them into roles.

{
    package Bomb;
    use Moose::Role;
    sub fuse    { print "Bomb explode\n" }
    sub explode { print "Bomb fuse\n" }
}
{
    package Spouse;
    use Moose::Role;
    sub fuse    { print "Spouse explode\n" }
    sub explode { print "Spouse fuse\n" }
}
{
    package PracticalJoke;
    use Moose;
    with qw(Bomb Spouse);
}
my $joke = PracticalJoke->new();
$joke->explode();
$joke->fuse();

If you try to run this code, you’ll get the following error at compile-time.

Due to method name conflicts in roles 'Bomb' and 'Spouse',
the methods 'explode' and 'fuse' must be implemented or
excluded by 'PracticalJoke' at ...

Note

We’ve mentioned a couple of times that certain errors with roles will happen at compile time. This is a lie. They actually happen at what is known as composition time. This is a Moose-specific term and it happens after compile time but before your program runs.

This error happens because Moose sees that Bomb and Spouse each provide explore() and fuse() methods but Moose has no way of knowing which you want. You can either implement the methods yourself in your class, thus causing the corresponding role methods to be ignored, or you can exclude the methods you don’t want, keeping only the methods you do:

package PracticalJoke;
use Moose;
with 'Bomb'   => { excludes => 'explode' },
     'Spouse' => { excludes => 'fuse' };

If you make that change and rerun the code, it will print out:

Spouse explode
Bomb fuse

And that’s exactly what we were looking for.

Note that the order in which you consume the roles is not relevant. Further, because most developers list the roles the class consumes at the top, a well-named set of roles can allow maintenance programmers a quick and easy way to glance at a class and understand which behaviors it implements without necessarily digging deep into an inheritance hierarchy. Once you understand them, roles can make your OO codebase easier to manage and extend.

Note

On a somewhat controversial note: your author has built large OO systems using only roles and no inheritance. By viewing roles as a collection of building blocks to assemble into classes, he’s found it very easy to add new behavior safely to large systems. Others with whom he has spoken have reported similar results. However, the idea of completely eliminating inheritance in favor of roles is very controversial, so just pretend you didn’t read this note.

To better understand roles, we recommend:

Exploring MooseX

When learning Moose, you’ll sometimes find that you want to do things a bit differently. We’ve alluded to metaprogramming, but you’ll be happy to know that for many of the most common tasks, there are already modules on the CPAN that handle them for you. By convention, modules that are “unofficial” extensions to other modules have an X in the name, so the unofficial Moose extension modules are in the MooseX:: namespace. The ones that the core Moose team likes are described in Moose::Manual::MooseX. We’ll cover a few of the MooseX:: modules here. These modules are not included with Moose and must be installed separately.

MooseX::StrictConstructor

In Chapter 12, Object Oriented Perl, we often had the following bit of code at the end of our _initialize() methods to ensure that no unknown arguments were passed to the constructor.

if ( my $remaining = join ', ', keys %arg_for ) {
    croak("Unknown keys to $class\::new: $remaining");
}

By default, Moose only checks known arguments to the constructor. However, if you have an optional birthdate attribute and you misspell it as birtdhate, Moose will assume you didn’t supply the birthdate and it ignores the birtdhate argument. With MooseX::StrictConstructor, this becomes a fatal error.

package Person;
use Moose;
use MooseX::StrictConstructor;
has 'name'      => ( is => 'ro', isa => 'Str',      required => 1 );
has 'birthdate' => ( is => 'ro', isa => 'DateTime', required => 0 );

And later when you do this:

Person->new(name=>'foo', birtdhate => 1);

You get the following error:

Found unknown attribute(s) passed to the constructor: birtdhate

If you want a strong OO system, this module is highly recommended.

MooseX::Method::Signatures

The MooseX::Method::Signatures module is lovely. It gives you proper method signatures for your methods. Your author knows of one team who is using this on high-volume BBC code, so it may be an excellent choice if you long for the method signatures found in other languages.

Note

A method signature is common feature of many programming languages. Consider the following function:

sub reciprocal {
    my $number = shift;
    return 1/$number;
}

For many languages, that would be written like this pseudo-code:

float reciprocal(int $number) {
    return 1/$number;
}

Method signatures make it easier to see what kind of data your functions accepts and returns. Plus, they can handle some of the data validation for you. For languages with proper method signatures, they’ll throw an exception (or fail to compile) if you try to pass a string to a function expecting an integer.

Note that the MooseX::Method::Signatures module does not specify the return type.

From the documentation (you’ll note that this package declares no attributes):

package Foo;
   use Moose;
   use MooseX::Method::Signatures;
   method morning (Str $name) {
       $self->say("Good morning ${name}!");
   }
   method hello (Str :$who, Int :$age where { $_ > 0 }) {
       $self->say("Hello ${who}, I am ${age} years old!");
   }
   method greet (Str $name, Bool :$excited = 0) {
       if ($excited) {
           $self->say("GREETINGS ${name}!");
       }
       else {
           $self->say("Hi ${name}!");
       }
   }
   $foo->morning('Resi');                    # This works.
   $foo->hello(who => 'world', age => 42);     # This too.
   $foo->greet('Resi', excited => 1);            # Yup.
   $foo->hello(who => 'world', age => 'fortytwo'); # Nope.
   $foo->hello(who => 'world', age => −23);      # Too bad.
   $foo->morning;                              # Won't work.
   $foo->greet;                              # Fail!

You don’t need to declare the variables with my and you don’t need to declare $self. In your author’s opinion, this is one of the most exciting things to happen in Perl for years. Lacking proper subroutine and method signatures is one of the biggest complaints registered about Perl

MooseX::SemiAffordanceAccessor

The MooseX::SemiAffordanceAccessor module automatically names your read-write attributes as $attribute and get_$attribute. Try running this code:

{
    package Soldier;
    use Moose;
    use MooseX::SemiAffordanceAccessor;
    has name => ( is => 'ro', isa => 'Str', required => 1 );
    has rank => ( is => 'rw', isa => 'Str', required => 1 );
}
my $soldier = Soldier->new(
    name => "Billy",
    rank => "Private",
);
$soldier->set_rank("Corporal");
$soldier->set_name("Barbara");

That will fail with:

Can't locate object method "set_name" via package "Soldier" at ...

As you can see, while a soldier’s rank is read-write, the name cannot be changed. We assume the military is happy with this.

There are many more MooseX modules, of course, but these give you a great idea of some of the potential out there.

Rewriting Television::Episode

Let’s use Moose to rewrite our TV::Episode class from Chapter 12, Object Oriented Perl. It looked like this:

package TV::Episode;
use strict;
use warnings;
use Carp 'croak';
use Scalar::Util 'looks_like_number';
our $VERSION = '0.01';
my %IS_ALLOWED_GENRE = map { $_ => 1 } qw(
  comedy
  drama
  documentary
  awesome
);
sub new {
    my ( $class, $arg_for ) = @_;
    my $self = bless {} => $class;
    $self->_initialize($arg_for);
    return $self;
}
sub _initialize {
    my ( $self, %arg_for ) = @_;
    my %arg_for = %$arg_for;
    foreach my $property (qw/series director title/) {
        my $value = delete $arg_for{$property};
        # at least one non-space character
        unless ( defined $value && $value =~ /\S/ ) {
            croak("property '$property' must have at a value");
        }
        $self->{$property} = $value;
    }
    my $genre = delete $arg_for{genre};
    unless ( exists $IS_ALLOWED_GENRE{$genre} ) {
        croak("Genre 'genre' is not an allowed genre");
    }
    $self->{genre} = $genre;
    foreach my $property (qw/season episode_number/) {
        my $value = delete $arg_for{$property};
        unless ( looks_like_number($value) && $value > 0 ) {
            croak("$value must have a positive value");
        }
        $self->{$property} = $value;
    }
    if ( my $extra = join ', ' => keys %arg_for ) {
        croak("Unknown keys to new(): $extra");
    }
}
sub series         { shift->{series} }
sub title          { shift->{title} }
sub director       { shift->{director} }
sub genre          { shift->{genre} }
sub season         { shift->{season} }
sub episode_number { shift->{episode_number} }
sub as_string {
    my $self       = shift;
    my @properties = qw(
      series
      title
      director
      genre
      season
      episode_number
    );
    my $as_string = '';
    foreach my $property (@properties) {
        $as_string .= sprintf "%-14s - %s\n", ucfirst($property),
          $self->$property;
    }
    return $as_string;
}
1;

There are many strategies we could reuse to rewrite that. For this example, we’re going to embed Moose::Util::TypeConstraints directly into our module and use an anonymous enum type for the genre and create an IntPositive type for the episode_number and season.

package TV::Episode;
use Moose;
use MooseX::StrictConstructor;
use Moose::Util::TypeConstraints;
use namespace::autoclean;
use Carp 'croak';
our $VERSION = '0.01';
subtype 'IntPositive',
         as    'Int',
         where { $_ > 0 };
has 'series'
  => ( is => 'ro', isa => 'Str',         required => 1 );
has 'director'
  => ( is => 'ro', isa => 'Str',         required => 1 );
has 'title'
  => ( is => 'ro', isa => 'Str',         required => 1 );
has 'season'
  => ( is => 'ro', isa => 'IntPositive', required => 1 );
has 'episode_number'
  => ( is => 'ro', isa => 'IntPositive', required => 1 );
has 'genre'          => (
    is       => 'ro',
    isa      => enum(qw(comedy drama documentary awesome)),
    required => 1
);
sub as_string {
    my $self = shift;
    my @attributes =
      map { $_->name }
      $self->meta->get_all_attributes;
    my $as_string = '';
    foreach my $attribute (@attributes) {
        $as_string .= sprintf "%-14s - %s\n",
          ucfirst($attribute),
          $self->$attribute;
    }
    return $as_string;
}
__PACKAGE__->meta->make_immutable;
1;

Note

TV/Episode.pm available for download at Wrox.com.

As you can see, this version is much shorter than the original version. In fact, if you pushed the as_string method into a role, you could reduce the class to this:

package TV::Episode;
use Moose;
use MooseX::StrictConstructor;
use My::CustomTypes;      # for Genre and IntPositive
with 'Does::ToString';    # for the as_string method
use namespace::autoclean;
our $VERSION = '0.01';
has 'series'   => ( is => 'ro', isa => 'Str', required => 1 );
has 'director' => ( is => 'ro', isa => 'Str', required => 1 );
has 'title'    => ( is => 'ro', isa => 'Str', required => 1 );
has 'genre'    => ( is => 'ro', isa => 'Genre', required => 1 );
has 'season'
  => ( is => 'ro', isa => 'IntPositive', required => 1 );
has 'episode_number'
  => ( is => 'ro', isa => 'IntPositive', required => 1 );
__PACKAGE__->meta->make_immutable;
1;

As you can see, by carefully planning out your code, you can make new classes very simple and easy to build and maintain. This is why Moose has become the OO system of choice for many Perl programmers.

Considering that the original version was almost twice as long as our revised version, and almost four times longer than our “ideal” version, which would you rather write?

Moose Best Practices

If you read perldoc Moose::Manual::BestPractices, you get a handful of best practices for using Moose. Following them will make your code cleaner and easier to maintain. We’ll highlight a few of them here and toss in some of our own, but remember, there’s an exception for every rule. The key thing to remember here is that you should understand why these are best practices. If you don’t, you will likely make bad decisions when you decide to break them.

Note that this is not an exhaustive list.

Use namespace::autoclean and make your class immutable

A shell of all Moose classes should look like this:

package Person;
use Moose;
use namespace::autoclean;
# extends, roles, attributes, etc.
# methods
__PACKAGE__->meta->make_immutable;
1;

By using namespace::autoclean, you guarantee that imported functions are not accidentally treated as methods. By making your class immutable, you gain a significant performance improvement in your code.

Never override new

BUILDARGS allows you to alter the argument list before calling new() and BUILD allows you to do validation on your object that may be difficult in the constructor.

Always call your parent BUILDARGS method.

If you inherit from a class providing a BUILDARGS method, use super() to call that parent method. You’ve seen this from the override() function we described earlier.

Provide defaults if an attribute is not required.

This is often bad:

has 'budget' => ( is => 'ro', isa => 'Num' );

If you are not going to require attribute to be provided, your code may blow up when you try to use the attribute value. A better strategy is to provide a default and document it.

has 'budget' => ( is => 'ro', isa => 'Num', default => 100 );

Remember that if the default is a reference, you must wrap it in an anonymous subroutine:

has 'birthday' => (
    is      => 'ro',
    isa     => 'DateTime',
    default => sub { DateTime->now } );

Default to read-only.

Generally you want your attributes to be read-only. This makes it much harder for someone to change the state of the object to something invalid later on. Sometimes, though, your class needs to change the attribute value internally, so make a private “writer”:

has 'budget' => (
    is     => 'ro',
    isa    => 'Num',
    writer => '_set_budget',
);

Put your custom types in one module and give them a namespace.

subtype 'My::Company::NonNegativeInteger'
    => as      'Int'
    => where   { $_ >= 0 }
    => message { "A Hand must have 0 or more fingers, not $_" };

By doing this, it’s easy to find the types and manage them. Also, by adding a custom My::Company:: prefix to the subtype name, you are less likely to conflict with another subtype of the same name.

Don’t use multiple inheritance

Use roles instead.

If you feel that you must use multiple inheritance to solve a problem, pull out the code you wish to share and put it in a role. The extra class (or classes!) you want to inherit from should now use that role and the new class you are writing can use that role, too. This should make your code much easier to maintain and avoids bugs in inheritance order and accidentally overriding methods you did not mean to override.

Always consume all of your roles at once.

It’s perfectly legal to do this:

package Foo;
use Moose;
with 'RoleA';
with 'RoleB';

But what happens if both RoleA and RoleB provide a discharge() method? Well, RoleA will provide the discharge() method and RoleB’s discharge() method be silently ignored. This is because when you consume a role, Moose assumes that your class’s methods take precedence over role methods. Thus, when RoleA’s discharge() method is added to your class, by the time you consume RoleB, Moose sees that your Foo class already has a discharge() method and ignores the one from RoleB.

Instead, be explicit about what you want:

package Foo;
use Moose;
with 'RoleA' => { excludes => "discharge" },
     'RoleB';

Even if you really did want to exclude the RoleB discharge method:

package Foo;
use Moose;
with 'RoleA',
     'RoleB' => { excludes => "discharge" };

In this case, it might be functionally equivalent to consuming those roles separately; it’s cleaner and self-documenting. To really understand this, consider our PracticalJoke class from earlier:

package PracticalJoke;
use Moose;
with 'Bomb'   => { excludes => 'explode' },
     'Spouse' => { excludes => 'fuse' };

If you had written:

package PracticalJoke;
use Moose;
with 'Bomb';
with 'Spouse';

You may not have spotted the method conflicts and thus had your code fail in ways you did not expect.

Consume all of your roles at once and be explicit about what you are doing.

Summary

In this chapter, you’ve learned about Moose, the OO programming module that is becoming the de facto standard for OO programming in Perl. Moose allows you to easily declare attributes, their types, whether they are required or read-only, what their default values are, and much more. The type constraints in Perl are easily extended and you can share behavior between unrelated classes with roles. Classes are easier to write, easier to read, and easier to maintain.

Your author has been known to write non-Moose classes from time to time, but that’s the exception rather than the rule. If you wish to do OO programming in Perl, you should understand Perl’s default OO facilities, but write your classes in Moose if you have the choice.

Exercises

1. Passwords are generally supplied in plain text. However, it’s a very, very bad idea to ever store them like this. Many developers will use an MD5 “digest” to rewrite the password. An MD5 digest will take a string and convert it into a series of characters that are unique to that string. However, the process is one-way. Without using sophisticated software such as rainbow tables (http://en.wikipedia.org/wiki/Rainbow_table), you cannot get the original string back.

Write a User class that requires a username and password, but has a BUILD method that immediately changes the password to an MD5 digest. The Digest::MD5 module was first released with Perl 5.7.3 and its use looks like this:

use Digest::MD5 'md5_hex';
my $digest = md5_hex($string);

Make sure to include a password_eq() method to verify that a new password matches the old password.

Note: this example should not be considered cryptographically secure. There are some issues with it, but hey, this isn’t a book on software security!

2. Create a role named Does::ToHash that will return a hash reference representation of an object. It should only be used for attributes that do not return a reference. Have your User class from exercise 1 consume this role and print out the resulting object.

Note that Moose automatically provides a dump() method ($user->dump) to do this for you. This exercise is to help you learn how to create roles.

WHAT YOU LEARNED IN THIS CHAPTER

Topic

Key Concepts

Moose

A powerful object system available from the CPAN

Attributes

Basic pieces of data used by your classes

BUILDARGS

A method to override how arguments are supplied to new()

BUILD

A method to validate your classes after construction

extends

The keyword to use to inherit from another Moose class

subtype

The keyword to declare new type constraints in Moose

before/after

Method modifiers called before and after methods

around

A special modifier used to wrap a Moose method

augment/inner

Special modifiers that allow a parent class full control over a method’s behavior

Roles

A new, improved way to share behavior among classes

MooseX::

The default namespace for modules that extend Moose's capabilities

Answers to exercises

1. Passwords are generally supplied in plain text. However, it’s a very, very bad idea to ever store them like this. Many developers will use an MD5 “digest” to rewrite the password. An MD5 digest will take a string and convert it into a series of characters that are unique to that string. However, the process is one-way. Without using sophisticated software such as rainbow tables (http://en.wikipedia.org/wiki/Rainbow_table), you cannot get the original string back.

Write a User class that requires a username and password, but has a BUILD method that immediately changes the password to an MD5 digest. The Digest::MD5 module was first released with Perl 5.7.3 and its use looks like this:

use Digest::MD5 'md5_hex';
my $digest = md5_hex($string);

Make sure to include a password_eq() method to verify that a new password matches the old password.

package User;
use Moose;
use Digest::MD5 'md5_hex';
use namespace::autoclean;
has username => ( is => 'ro', isa => 'Str', required => 1 );
has password => (
    is     => 'ro',
    isa    => 'Str',
    writer => '_set_password',
);
sub BUILD {
    my $self = shift;
    $self->_set_password(md5_hex($self->password));
}
sub password_eq {
    my ( $self, $password ) = @_;
    $password = md5_hex($password);
    return $password eq $self->password;
}
__PACKAGE__->meta->make_immutable;
1;

You can test the above code with the following snippet:

my $user = User->new(
    username => 'Ovid',
    password => 'Corinna',
);
print $user->dump;
print "Yes" if $user->password_eq('Corinna');

2. Create a role named Does::ToHash that will return a hash reference representation of an object. It should only be used for attributes that do not return a reference. Have your User class from exercise 1 consume this role and print out the resulting object.

Note that Moose automatically provides a dump() method ($user->dump) to do this for you. This exercise is to help you learn how to create roles.

Here’s one way to write the role:

package Does::ToHash;
use Moose::Role;
sub to_hash {
    my $self = shift;
    my @attributes = map { $_->name } $self->meta->get_all_attributes;
    my %hash;
    foreach my $attribute (@attributes) {
        my $value = $self->$attribute;
        next if ref $value;
        $hash{$attribute} = $value;
    }
    return \%hash;
}
1;

And extending the User class to provide the Does::ToHash method looks like this:

package User;
use Moose;
with 'Does::ToHash';
use Digest::MD5 'md5_hex';
use namespace::autoclean;
has username => ( is => 'ro', isa => 'Str', required => 1 );
has password => (
    is     => 'ro',
    isa    => 'Str',
    writer => '_set_password',
);
sub BUILD {
    my $self = shift;
    $self->_set_password(md5_hex($self->password));
}
sub password_eq {
    my ( $self, $password ) = @_;
    $password = md5_hex($password);
    return $password eq $self->password;
}
__PACKAGE__->meta->make_immutable;
1;

And if you run this test script:

use Data::Dumper;
my $user = User->new(
    username => 'Ovid',
    password => 'Corinna',
);
print Dumper($user->to_hash);

You get the following output:

$VAR1 = {
          'password' => '5169c96db420b1157c60ba46a6d4b43c',
          'username' => 'Ovid'
        };
Site last updated on: July 5, 2012 at 11:41:08 AM PDT
Cover for Beginning Perl (Wrox)

View 2 comments

  1. Curtis Poe – Posted June 19, 2012

    I had meant "rather insane" as a light-hearted and complimentary thing, but it's clear that it really hasn't come across as such and will be removed from the book.

  2. Ben Bullock – Posted June 19, 2012

    "rather inspired Perl programmer"?

Add a comment

View 2 comments

  1. Ben Bullock – Posted June 19, 2012

    I would put the instructions for installing it here, for the sake of people who've skipped to this chapter:

    cpanm Moose

    or whatever.

  2. Curtis Poe – Posted July 3, 2012

    At this point of the book, they've installed multiple modules from the CPAN, so I don't repeat it every time.

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 2 comments

  1. Ben Bullock – Posted June 19, 2012

    This is great and the enthusiasm is clear, but you've jumped from "matter of declaring attributes and methods" right into the example, but you haven't actually said which part of this is attributes or methods. It might be worth saying here that "has" specifies the attributes, and there are no methods.

  2. Curtis Poe – Posted July 3, 2012

    Ben, that's a great catch. Fixed!

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 2 comments

  1. Ben Bullock – Posted June 19, 2012

    Maybe "perldoc" should be in the same typeface as Moose::... here.

  2. Curtis Poe – Posted July 3, 2012

    Fixed. Thanks!

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 2 comments

  1. Ben Bullock – Posted June 19, 2012

    I'd take out the "(duh)" here; this might seem a statement of the obvious, but the book is for beginners.

  2. Curtis Poe – Posted July 3, 2012

    Yes, you're right. Damn it :)

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 2 comments

  1. chrisjack1 – Posted June 28, 2012

    s/once/one/

    The author has indicated that the issue raised in this comment has been resolved.

  2. Curtis Poe – Posted July 3, 2012

    Fixed. Thanks.

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 2 comments

  1. chrisjack1 – Posted June 28, 2012

    s/need that/that need that/

    Edited on June 28, 2012, 3:30 a.m. PDT

    The author has indicated that the issue raised in this comment has been resolved.

  2. Curtis Poe – Posted July 3, 2012

    It's correct, but worded awkwardly. Fixed. Thanks.

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 2 comments

  1. chrisjack1 – Posted June 28, 2012

    you have a series of misspellings of what should be "minimum_age" - i.e. the second n should have been an m

    Edited on June 28, 2012, 3:32 a.m. PDT

    The author has indicated that the issue raised in this comment has been resolved.

  2. Curtis Poe – Posted July 3, 2012

    Oops. Fixed, thanks :)

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 3 comments

  1. Ben Bullock – Posted June 15, 2012

    referenec -> reference

  2. Ben Bullock – Posted June 19, 2012

    "Moose will throw an exception if you do not wrap that referenec in a subroutine reference because it is assumed that you do not want every class changing the same reference."

    This is really confusing; how about "Moose will throw an exception if your default is a reference without the sub {}. It assumes that you do not want to have every instance of the class using just one reference" or something.

  3. Curtis Poe – Posted July 3, 2012

    That's a good point. I've taken some of your wording and expanded it a bit.

Add a comment

View 2 comments

  1. chrisjack1 – Posted June 28, 2012

    Suggest you rewrite the 3rd sentence as:

    Instead, use BUILDARGS to modify arguments before new() is called, with BUILD being used after the constructor is called when you need further validation of the state of the object.

    i.e. remove the you and change the and/is to with/being

    The author has indicated that the issue raised in this comment has been resolved.

  2. Curtis Poe – Posted July 3, 2012

    That's better. Fixed!

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 2 comments

  1. stevan_little – Posted June 18, 2012

    You should mention that BUILD is called by BUILDALL which calls all the BUILD methods in the inheritance hierarchy in the correct (reverse) order.

  2. Curtis Poe – Posted June 19, 2012

    Thanks Stevan. Will do!

Add a comment

View 2 comments

  1. Ben Bullock – Posted June 16, 2012

    "does assumes" -> " does assume"

  2. Curtis Poe – Posted July 3, 2012

    Fixed. Thanks!

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 3 comments

  1. stevan_little – Posted June 18, 2012

    Actually you can still use metaprogramming, you simply need to make your class mutable again by calling the make_mutable method on the metaobject.

  2. Curtis Poe – Posted June 19, 2012

    Stevan, had no idea you could do that. I assume this is an expensive operation (e.g., I don't want to go back and forth between the two states)?

  3. stevan_little – Posted June 19, 2012

    Curtis, yes, it is not recommended to go back and forth to often, however if you are already deep in the metaprogramming hole, performance is often a secondary (if not tertiary) concern.

Add a comment

View 4 comments

  1. stevan_little – Posted June 18, 2012

    Moose does not activate the C3 mro, this is incorrect.

  2. Curtis Poe – Posted June 19, 2012

    @Stevan: thanks, I'll fix this.

  3. chrisjack1 – Posted June 28, 2012

    Suddenly you've started using "-" instead of "," for some reason.

    The author has indicated that the issue raised in this comment has been resolved.

  4. Curtis Poe – Posted July 3, 2012

    @chrisjack1: The mdash is more appropriate when you want to include extra information but it's not as tightly linked to the direct meaning as commas. Parentheses can also be used here, but I tend to abuse them.

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 2 comments

  1. stevan_little – Posted June 18, 2012

    This is misleading, SUPER:: still works as before.

  2. Curtis Poe – Posted June 19, 2012

    I'll note that. Thanks!

Add a comment

View 2 comments

  1. Ben Bullock – Posted June 15, 2012

    "Name you subtypes" -> "Name your subtypes"

  2. Curtis Poe – Posted July 3, 2012

    Fixed. Thanks.

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 2 comments

  1. chrisjack1 – Posted June 28, 2012

    s/are //

    The author has indicated that the issue raised in this comment has been resolved.

  2. Curtis Poe – Posted July 3, 2012

    Fixed. Thanks.

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment

View 1 comment

  1. Yary – Posted Aug. 23, 2012

    "...provides additional methods for a class, along with methods it requires..." I think having "method" twice is distracting... are you trying to say interface vs method? Could be clearer.

Add a comment

View 2 comments

  1. stevan_little – Posted June 18, 2012

    It should be noted that not all attributes will have readers that are equal to their name, so while this code will work for 90% of Moose code, it is not truly generic.

  2. Curtis Poe – Posted June 19, 2012

    Stevan: I'll add this note.

Add a comment

View 2 comments

  1. JoshuaKeroes – Posted June 18, 2012

    The methods are backward.

    fuse() should print "Mumble fuse"

    explode() should print "Mumble explode"

    Edited on June 18, 2012, 1:55 p.m. PDT

  2. Curtis Poe – Posted June 19, 2012

    @Joshua: ohh, silly of me. Thanks :)

Add a comment

View 3 comments

  1. stevan_little – Posted June 18, 2012

    Actually the order of role composition is important if your role has method modifiers in it (before, after and around).

  2. Curtis Poe – Posted June 19, 2012

    @Stevan: yes, I was hoping to avoid that topic as this is only an introduction. I'll have to rethink that a bit.

  3. stevan_little – Posted June 19, 2012

    Curtis, it is such a common usage I think it might be a good to mention.

Add a comment

View 1 comment

  1. Yary – Posted Aug. 23, 2012

    Error: "$attribute and get_$attribute" should be "$attribute and set_$attribute"

Add a comment

View 2 comments

  1. chrisjack1 – Posted June 28, 2012

    s/be/will be/

    The author has indicated that the issue raised in this comment has been resolved.

  2. Curtis Poe – Posted July 3, 2012

    Fixed. Thanks.

    The author has indicated that the issue raised in this comment has been resolved.

Add a comment