Moose::Cookbook::Recipe1 - The (always classic) Point example.
package Point; use Moose; has 'x' => (isa => 'Int', is => 'ro'); has 'y' => (isa => 'Int', is => 'rw'); sub clear { my $self = shift; $self->{x} = 0; $self->y(0); } package Point3D; use Moose; extends 'Point'; has 'z' => (isa => 'Int'); after 'clear' => sub { my $self = shift; $self->{z} = 0; };
This is the classic Point example. This one in particular I took from the Perl 6 Apocalypse 12 document, but it is similar to the example found in the classic K&R C book as well, and many other places. And now, onto the code:
As with all Perl 5 classes, a Moose class is defined in a package. Moose now handles turning strict and warnings on for you, so all you need do is say use Moose, and no kittens will die.
strict
warnings
use Moose
By loading Moose, we are enabeling the Moose "environment" to be loaded within our package. This means that we export some functions which serve as Moose "keywords". This is nothing fancier than that, just plain old exported functions.
Another important thing happens at this stage as well. Moose will automatically set your package's superclass to be Moose::Object. The reason we do this, is so that we can be sure that you will inherit from Moose::Object and get the benefits that provides (see the Moose::Object for those details). However, you don't actually have to inherit from Moose::Object if you don't want to, all other features of Moose are still accessible to you.
Now, onto the keywords. The first one we see here is has, which defines an instance attribute in your class.
has
has 'x' => (isa => 'Int', is => 'ro');
This will create an attribute named x, it will expect that the value stored in the attribute to pass the type constraint Int (1), and the accessor generated for this attribute will be read-only (abbreviated as ro).
x
Int
ro
The next has line is very similar, with only one difference.
has 'y' => (isa => 'Int', is => 'rw');
For the y attribute a read/write accessor will be generated (abbreviated as rw).
y
rw
At this point the attributes have been defined, and it is time to define our methods. In Moose, as with regular Perl 5 OO, a method is just a subroutine defined within the package. So here we create the clear method.
clear
sub clear { my $self = shift; $self->{x} = 0; $self->y(0); }
It is pretty standard, the only thing to note is that we are directly accessing the x slot in the instance (2). This is because the value was created with a read-only accessor. This also shows that Moose objects are not anything out of the ordinary, but just regular old blessed HASH references. This means they are very compatible with other Perl 5 (non-Moose) classes as well.
The next part of the code to review is the Point subclass, Point3D. The first item you might notice is that we do not use the standard use base declaration here. Instead we use the Moose keyword extends like so:
use base
extends
extends 'Point';
This keyword will function very much like use base does in that it will make an attempt to load your class if it has not already been loaded. However, it differs on one important point. The extends keyword will overwrite any previous values in your package's @ISA, where use base will push values onto the package's @ISA. It is my opinion that the behavior of extends is more intuitive in that it is more explicit about defining the superclass relationship.
@ISA
push
A small digression here, both Moose and extends support multiple inheritence. You simply pass all the superclasses to extends, like so:
extends 'Foo', 'Bar', 'Baz';
Now, back to our Point3D class. The next thing we do is to create a new attribute for Point3D called z.
z
has 'z' => (isa => 'Int');
As with Point's x and y attributes, this attribute has a type constraint of Int, but it differs in that it does not ask for any autogenerated accessors. The result being (aside from breaking object encapsulation), that x is a private attribute.
Next comes another Moose feature which we call method "modifiers" (or method "advice" for the AOP inclined). The modifier used here is the after modifier, and looks like this:
after
after 'clear' => sub { my $self = shift; $self->{z} = 0; };
This modifier tells Moose to install a clear method for Point3D that will first run the clear method for the superclass (in this case Point::clear), and then run this method after it (passing in the same arguments as the original method).
Point::clear
Now, of course using the after modifier is not the only way to accomplish this. I mean, after all, this is Perl right? You would get the same results with this code:
sub clear { my $self = shift; $self->SUPER::clear(); $self->{z} = 0; }
You could also use another Moose method modifier, override here, and get the same results again. Here is how that would look.
override
override 'clear' => sub { my $self = shift; super(); $self->{z} = 0; };
The override modifier allows you to use the super keyword within it to dispatch to the superclass's method in a very Ruby-ish style.
super
Now of course, what use is a class if you cant instantiate objects with it. Now since Point inherits from Moose::Object, it will inherit the default Moose::Object constructor called new. Here are two examples of how that is used:
new
my $point = Point->new(x => 1, y => 2); my $point3d = Point3D->new(x => 1, y => 2, z => 3);
As you can see, new accepts named argument pairs for any of the attributes. It does not require that you pass in the all the attributes, and it will politely ignore any named arguments it does not recognize.
From here on, you can use $point and $point3d just as you would any other Perl 5 object. For a more detailed example of what can be done, you can refer to the t/001_recipe.t test file.
$point
$point3d
I hope this recipe has given you some explaination of how to use Moose to build you Perl 5 classes. The next recipe will build upon the basics shown here with more complex attributes and methods. Please read on :)
Several default type constraints are provided by Moose, of which Int is one. For more information on the built-in type constraints and the type constraint system in general, see the Moose::Util::TypeConstraints documentation.
Future plans for Moose include allowing for alternate instance structures such as blessed ARRAY refs and such. If you want you Moose classes to be interchangable, it is advised that you avoid direct instance access, like that which is shown above.
The concept of method modifiers is directly ripped off from CLOS. A great explaination of them can be found by following this link.
http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html
Stevan Little <stevan@iinteractive.com>
Copyright 2006, 2007 by Infinity Interactive, Inc.
http://www.iinteractive.com
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install Moose, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Moose
CPAN shell
perl -MCPAN -e shell install Moose
For more information on module installation, please visit the detailed CPAN module installation guide.