Perl - Moose

perl

Important:
http://www.theperlreview.com/articles/moose.html - done reading

http://www.slideshare.net/j1n3l0/oo-perl-with-moose
http://search.cpan.org/~ether/Moose-2.1603/lib/Moose.pm
http://search.cpan.org/dist/Moose/lib/Moose/Manual.pod
http://moose.iinteractive.com/en/
https://en.wikipedia.org/wiki/Moose_%28Perl%29
http://perlmaven.com/object-oriented-perl-using-moose
https://metacpan.org/release/Moose
https://www.perl.org/about/whitepapers/perl-object-oriented.html - done reading
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/Attributes.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/BestPractices.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/Classes.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/Concepts.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/Construction.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/Delegation.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/MooseX.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/Roles.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/Types.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Manual/Unsweetened.pod
https://metacpan.org/pod/distribution/Moose/lib/Moose/Meta/Attribute/Native/Trait.pm
https://metacpan.org/pod/Moose::Manual

What is Moose and why should we use Moose?

Moose is a declarative way of defining classes in Perl. Perl has had Object Oriented (OO) capabilities for fifteen years now, and today it has Moose. Moose helps developers focus on the underlying logic and requirements behind any project; encourages code reuse, simplification (making testing easier) and promotes best practices.

With Moose, gone are the days of writing constructors, destructors, and accessors. Moose provides a simple, declarative syntax for specifying classes. Most attribute validation code simply disappears. Moose handles it automatically as soon as the data type is specified.

Roles allow for easy reuse of behavior independent of class data. Moose is built to be as flexible as Perl, it can be change to suit your projects needs—see the growing MooseX namespace on CPANexternal link. One example of a MooseX module is MooseX::NonMooseexternal link, designed to make migrating existing codebases easier. And best of all, the user interface to Moose is Just Perl™, so developers don't need to learn a new syntax.

Attributes in Moose are simple and straightforward. A programmer declares them with a simple function call, using named parameters like many other Perl libraries. This declaration can specify whether the attribute is writable, what kind of values it accepts, a default for the attribute, custom builder / clearer methods, and more. Given type information for an attribute, Moose will optionally try to convert values to that type or throw an exception if it cannot do so. This checking allows the programmer to focus on writing their logic, without getting bogged down in the details of value checking.

Just as attributes change how Moose class data is handled, roles change how class behavior is written. Roles are reusable collections of methods that can be added to a class or an object at runtime. With roles, one can write behavior for a collection of classes without needing to know or care about the data those classes manage. Roles can require methods to exist in the classes that use them, and in this way can function like interfaces in more traditional object-oriented languages like Java and C#. As with attributes, exceptions will be thrown upon the failure to satisfy these criteria, providing a structured mechanism for adding behavior to classes.

You can create and extend classes, add to classes with roles without using inheritance, and apply extra behavior to instances.

What does MOP abbreviate for?

Meta Object Programming, which means that the object system itself is itself an object. Moose is really Class::MOP without the configuration and fiddly bits, since it takes care of all of that for you. It makes Class::MOP do the things that I most likely want and need when I want to create object classes, and suffers only slighly from all of the extra overhead (although there are some ways around that too).

How can we get started with Moose?

# in PetShop.pm
package PetShop;
use Moose;

1;

It's deceptively simple, but it's a full-on, ready-to-use class and I can create my pet shop object right away in pet_shop.pl:

# pet_shop.pl
use PetShop;
my $pet_shop = PetShop->new;

Notice that we didn't have to define the constructor new. Moose took care of it.

What does Moose actually do for me?

  1. It automatically give me the constructor new.
  2. It automatically adds 'use strict' and 'use warnings' to my package, so that I do not have to add those statements, but when I declare my variables, I still have to declare them appropriately with 'my' or 'our'

How can we tell Moose the attributes that our class have?

has 'pets' => (
    is  => 'rw',
    isa => 'ArrayRef',
    );

The above code tells Moose that our class has an attribute named 'pets'. This attribute is readable and writable, and it is a reference to an array.

We can have multiple attributes. We just have to use the 'has' statement multiple times, one for each attribute.

What happens when we declare an attribute?

Moose automatically sets up a method with the same name for accessing that attribute. Since I don't add any pets, I don't get any back, but the method still works:

# pet_shop.pl
use PetShop;
my $pet_shop = PetShop->new;
my $pets = $pet_shop->pets;
print "The pet shop has [@$pets].\n";

What parameter does the new constructor take?

It takes an anonymous hash. For example:

# pet_shop.pl
use PetShop;
my $pet_shop = PetShop->new( 
    pets => [ qw(cat cat bird dog) ] 
    );

my $pets = $pet_shop->pets;
print "The pet shop has [@$pets].\n";

In Perl, when we use the => construct, it automatically converts the parameters into a single hash.

How can we tell Moose that our attribute is a reference to an array of string?

has 'pets' => (
    is  => 'rw',
    isa => 'ArrayRef[Str]',
    );

How can we tell Moose that our attribute is a reference to an array of a particular class?

has 'pets' => (
    is  => 'rw',
    isa => 'ArrayRef[Pet]',
    );

How can we use the extends keyword to indicate the base class?

package Pet::Cat;
use Moose;
extends 'Pet';

How can we specify that our class inherit an attribute from the base class?

Use the + character:

has '+type' => ( 
    default => 'cat' 
    );

What are the methods that Moose exports into my module?

  • has
  • and many more

How can I clean up the methods that Moose exports into my module?

If a named subroutine is the problem and I can't do without the subroutine, maybe I can do without the name. When I'm done using the Moose exports, I can unimport them:

package Pet;
use Moose;

...

no Moose;

What is the purpose of the 'use namespace::autoclean;' statement?

If you imported anything from other classes, those subroutines shouldn't become methods either. I can use namespace::autoclean to get rid of those exports too:

package Pet;
use Moose;
use namespace::autoclean;

namespace::autoclean removes the names (but not the code) from any subroutine in the package that looks like it isn't a method. It's similar to namespace::clean, but it works with the meta object to see which methods should be left alone.

What is an immutable class?

Perl, being the dynamic language it is, can change its classes, redefine its subroutine, and alter all manner of things at practically any time. This means that, in general, Moose cannot assume that anything stays the same. That is, unless I promise not to change anything. I can tell Moose that I'm not going to change my class, giving it the opportunity to cache much of the stuff that it would have to look up repeatedly, by calling make_immutable on the meta object. Putting that together with autoclean, the Moose best practice becomes:

package Pet;
use Moose;
use namespace::autoclean;
...
__PACKAGE__->meta->make_immutable;

Making your Moose class immutable somewhat alleviates the penalty I pay for the meta object underpinnings. My program will still run slightly slower than it would without Moose, but the penalty isn't as bad. Moose does a little extra work at compile-time, but the run-time speedup more than makes up for it.

What is Mouse?

It is another meta object framework similar to Moose, but tries even harder to alleviate the speed penalty. It's a subset of what you'll find in Moose, and since it doesn't do everything that Moose does it can be a bit more efficient. It's also a drop-in replacement for Moose.

What is a Role?

In my Pet class I added a speak method. That's not something that describes an animal, it's something that an animal can do. I don't have to treat those the same. Moose lets me define a “role” that I can use to add behavior to a class.

A role is quite a bit different from the conventional way that people share and reuse methods. Without roles, I might use inheritance even though the inheritor really isn't a more specialized version. I might use a mixin to insert methods into a package, but that has its own disadvantages, such as a lack of accountability (just where did those methods come from?).

Instead of defining speak inside Pet, I'll make a role just for it. There are other, non-Pet, things that might speak too and they'll be able to use the same role:

package Speech;
use Moose::Role;
use namespace::autoclean;       

sub speak { $_[0]->sound }

no Moose::Role;

In the above code, we defined a package named Speech, and we use Moose::Role. I don't make roles immutable because they aren't classes, but I do clean up after myself by unimporting with no Moose::Role, and in the Pet package, instead of extends, I use with to pull in the role:

package Pet;
use Moose;
use namespace::autoclean;       
with 'Speech';

We use the 'with' statement to pull in the role. By using a role, I can check that my object performs a role rather than asking it if it can respond to a particular method.

How can we specify multiple roles for a class?

Once I define the roles, I can add them to the appropriate classes. I pull in any of the roles that I need by using with, which I can give a list:

package Pet::Cat;
use Moose;
use namespace::autoclean;       
extends 'Pet';
with qw( Digger Feline );
...
__PACKAGE__->meta->make_immutable;

What is the purpose of the MooseX::FollowPBP module?

Perhaps I don't want a pets method, but would like to have get_pets and set_pets. If I use the MooseX::FollowPBP module PetShop, I get those for free.

package PetShop;
use MooseX::FollowPBP;
use Moose;
use namespace::autoclean;

has 'pets' => (
    is     => 'rw',
    isa    => 'ArrayRef[Pet]',
    auto_deref => 1,
    );

How can we apply roles to individual object instances instead of the entire class?

First, create a role like you did previously:

package Microchip;
use Moose::Role;
use namespace::autoclean;

has 'microchip' => (
    is  => 'rw',
    isa => 'Str',
    );

no Moose::Role

Apply the role to instances. There are a couple of ways to do this, but I can use the meta method in the particular role to a single instance:

Microchip->meta->apply( $buster );

We can add a method to Pets to add the role on request:

sub add_microchip 
    {
    my( $self, $id ) = @_;

    Microchip->meta->apply( $self );
    $self->microchip( $id );
    }

__PACKAGE__->meta->make_immutable;

What is the purpose of Moose::Util::TypeConstraints?

I need to make it validate the microchip value I pass to add_microchip. For that, I pull in Moose::Util::TypeConstraints, which allows me to create subtypes. For each subtype, I can specify a type that it is based on (through exports from Moose::Util::TypeConstraints), and also apply my own validation with a where clause.

package Microchip;
use Moose::Role;
use namespace::autoclean;

use Moose::Util::TypeConstraints;

subtype 'MicrochipID', 
    => as Str 
    => where   { /\A[A-Z]{3}\d{3}\z/ };

has 'microchip' => (
    is  => 'rw',
    isa => 'MicrochipID',
    );

no Moose::Role

Notice that the where clause operates on $_. Once I define my subtype, I use it with my defintion for the microchip attribute.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License