Moose - A postmodern object system for Perl 5 |
Moose - A postmodern object system for Perl 5
package Point; use Moose; # automatically turns on strict and warnings
has 'x' => (is => 'rw', isa => 'Int'); has 'y' => (is => 'rw', isa => 'Int');
sub clear { my $self = shift; $self->x(0); $self->y(0); }
package Point3D; use Moose;
extends 'Point';
has 'z' => (is => 'rw', isa => 'Int');
after 'clear' => sub { my $self = shift; $self->z(0); };
Moose is an extension of the Perl 5 object system.
Yes, I know there has been an explosion recently of new ways to build objects in Perl 5, most of them based on inside-out objects and other such things. Moose is different because it is not a new object system for Perl 5, but instead an extension of the existing object system.
Moose is built on top of the Class::MOP manpage, which is a metaclass system for Perl 5. This means that Moose not only makes building normal Perl 5 objects better, but it also provides the power of metaclass programming.
Moose is based on the prototypes and experiments I did for the Perl 6 meta-model. However, Moose is NOT an experiment/prototype; it is for real.
Yes, I believe that it is.
Moose has been used successfully in production environemnts by several people and companies (including the one I work for). There are Moose applications which have been in production with little or no issue now for well over a year. I consider it highly stable and we are commited to keeping it stable.
Of course, in the end, you need to make this call yourself. If you have any questions or concerns, please feel free to email me, or even the list or just stop by #moose and ask away.
No. While Moose is very much inspired by Perl 6, it is not itself Perl 6. Instead, it is an OO system for Perl 5. I built Moose because I was tired of writing the same old boring Perl 5 OO code, and drooling over Perl 6 OO. So instead of switching to Ruby, I wrote Moose :)
So I was reading Larry Wall's talk from the 1999 Linux World entitled ``Perl, the first postmodern computer language'' in which he talks about how he picked the features for Perl because he thought they were cool and he threw out the ones that he thought sucked. This got me thinking about how we have done the same thing in Moose. For Moose, we have ``borrowed'' features from Perl 6, CLOS (LISP), Smalltalk, Java, BETA, OCaml, Ruby and more, and the bits we didn't like (cause they sucked) we tossed aside. So for this reason (and a few others) I have re-dubbed Moose a postmodern object system.
Nuff Said.
The the MooseX:: manpage namespace is the official place to find Moose extensions. There are a number of these modules out on CPAN right now the best way to find them is to search for MooseX:: on search.cpan.org.
Moose makes every attempt to provide as much convenience as possible during class construction/definition, but still stay out of your way if you want it to. Here are a few items to note when building classes with Moose.
Unless specified with extends
, any class which uses Moose will
inherit from the Moose::Object manpage.
Moose will also manage all attributes (including inherited ones) that are
defined with has
. And (assuming you call new
, which is inherited from
the Moose::Object manpage) this includes properly initializing all instance slots,
setting defaults where appropriate, and performing any type constraint checking
or coercion.
Moose provides a number of methods to all your classes, mostly through the inheritance of the Moose::Object manpage. There is however, one exception.
Moose will export a number of functions into the class's namespace which may then be used to set up the class. These functions all work directly on the current class.
superclass(es)
for the current class.
This approach is recommended instead of use base
, because use base
actually push
es onto the class's @ISA
, whereas extends
will
replace it. This is important to ensure that classes which do not have
superclasses still properly inherit from the Moose::Object manpage.
@roles
to the local class.
$name
into the current class.
The %options
are the same as those provided by
the Class::MOP::Attribute manpage, in addition to the list below which are provided
by Moose (the Moose::Meta::Attribute manpage to be more specific):
$name
of the attribute.
If you need more control over how your accessors are named, you can use the reader, writer and accessor options inherited from the Class::MOP::Attribute manpage, however if you use those, you won't need the is option.
$type_name
argument must be a
string. The string may be either a class name or a type defined using
Moose's type definition features. (Refer to the Moose::Util::TypeConstraints manpage
for information on how to define a new type, and how to retrieve type meta-data).
undef
with an accessor.
isa
option is either ArrayRef
or HashRef
.
NOTE: The class being delegated to does not need to be a Moose based class, which is why this feature is especially useful when wrapping non-Moose classes.
All handles option formats share the following traits:
You cannot override a locally defined method with a delegated method; an
exception will be thrown if you try. That is to say, if you define foo
in
your class, you cannot override it with a delegated foo
. This is almost never
something you would want to do, and if it is, you should do it by hand and not
use Moose.
You cannot override any of the methods found in Moose::Object, or the BUILD
and DEMOLISH
methods. These will not throw an exception, but will silently
move on to the next method in the list. My reasoning for this is that you would
almost never want to do this, since it usually breaks your class. As with
overriding locally defined methods, if you do want to do this, you should do it
manually, not with Moose.
You do not need to have a reader (or accessor) for the attribute in order to delegate to it. Moose will create a means of accessing the value for you, however this will be several times less efficient then if you had given the attribute a reader (or accessor) to use.
Below is the documentation for each option format:
ARRAY
HASH
This can be very useful for recursive classes like trees. Here is a quick example (soon to be expanded into a Moose::Cookbook::Recipe):
package Tree; use Moose;
has 'node' => (is => 'rw', isa => 'Any');
has 'children' => ( is => 'ro', isa => 'ArrayRef', default => sub { [] } );
has 'parent' => ( is => 'rw', isa => 'Tree', weak_ref => 1, handles => { parent_node => 'node', siblings => 'children', } );
In this example, the Tree package gets parent_node
and siblings
methods,
which delegate to the node
and children
methods (respectively) of the Tree
instance stored in the parent
slot.
REGEXP
NOTE: An isa option is required when using the regexp option format. This is so that we can determine (at compile time) the method list from the class. Without an isa this is just not possible.
ROLE
CODE
This takes a code reference, which should expect two arguments. The first is the attribute meta-object this handles is attached to. The second is the metaclass of the class being delegated to. It expects you to return a hash (not a HASH ref) of the methods you want mapped.
The default behavior here is to just load $metaclass_name
; however, we also
have a way to alias to a shorter name. This will first look to see if
Moose::Meta::Attribute::Custom::$metaclass_name exists. If it does, Moose
will then check to see if that has the method register_implementation
, which
should return the actual name of the custom attribute metaclass. If there is no
register_implementation
method, it will fall back to using
Moose::Meta::Attribute::Custom::$metaclass_name as the metaclass name.
@role_names
and apply them to the
attribute meta-object. This is very similar to the metaclass option, but
allows you to use more than one extension at a time. This too is an advanced
topic, we don't yet have a cookbook for it though.
As with metaclass, the default behavior is to just load $role_name
; however,
we also have a way to alias to a shorter name. This will first look to see if
Moose::Meta::Attribute::Custom::Trait::$role_name exists. If it does, Moose
will then check to see if that has the method register_implementation
, which
should return the actual name of the custom attribute trait. If there is no
register_implementation
method, it will fall back to using
Moose::Meta::Attribute::Custom::Trait::$metaclass_name as the trait name.
has
which allows you to
clone and extend an attribute from a superclass or from a role. Here is an
example of the superclass usage:
package Foo; use Moose;
has 'message' => ( is => 'rw', isa => 'Str', default => 'Hello, I am a Foo' );
package My::Foo; use Moose;
extends 'Foo';
has '+message' => (default => 'Hello I am My::Foo');
What is happening here is that My::Foo is cloning the message
attribute
from its parent class Foo, retaining the is => 'rw'
and isa =>
'Str'
characteristics, but changing the value in default
.
Here is another example, but within the context of a role:
package Foo::Role; use Moose::Role; has 'message' => ( is => 'rw', isa => 'Str', default => 'Hello, I am a Foo' ); package My::Foo; use Moose; with 'Foo::Role'; has '+message' => (default => 'Hello I am My::Foo');
In this case, we are basically taking the attribute which the role supplied and altering it within the bounds of this feature.
Aside from where the attributes come from (one from superclass, the other from a role), this feature works exactly the same. This feature is restricted somewhat, so as to try and force at least some sanity into it. You are only allowed to change the following attributes:
It is recommended that you use this freedom with caution. We used to only allow for extension only if the type was a subtype of the parent's type, but we felt that was too restrictive and is better left as a policy descision.
handles
definition, but you are not
allowed to change one.
builder
definition, but you are not
allowed to change one.
metaclass
definition, but you are
not allowed to change one.
traits
definition.
These traits will be composed into the attribute, but pre-existing traits
are not overridden, or removed.
super
is a no-op when called outside of an override
method. In
the context of an override
method, it will call the next most appropriate
superclass method with the same arguments as the original method.
override
method is a way of explicitly saying ``I am overriding this
method from my superclass''. You can call super
within this method, and
it will work as expected. The same thing can be accomplished with a normal
method call and the SUPER::
pseudo-package; it is really your choice.
inner
, much like super
, is a no-op outside of the context of
an augment
method. You can think of inner
as being the inverse of
super
; the details of how inner
and augment
work is best described in
the the Moose::Cookbook::Recipe7 manpage.
augment
method, is a way of explicitly saying ``I am augmenting this
method from my superclass''. Once again, the details of how inner
and
augment
work is best described in the the Moose::Cookbook::Recipe7 manpage.
Carp::confess
function, and exported here because I use it
all the time.
Scalar::Util::blessed
function, it is exported here because I
use it all the time. It is highly recommended that this is used instead of
ref
anywhere you need to test for an object's class name.
Moose offers a way to remove the keywords it exports, through the unimport
method. You simply have to say no Moose
at the bottom of your code for this
to work. Here is an example:
package Person; use Moose;
has 'first_name' => (is => 'rw', isa => 'Str'); has 'last_name' => (is => 'rw', isa => 'Str');
sub full_name { my $self = shift; $self->first_name . ' ' . $self->last_name }
no Moose; # keywords are removed from the Person package
Moose also offers some options for extending or embedding it into your own
framework. The basic premise is to have something that sets up your class'
metaclass and export the moose declarators (has
, with
, extends
,...).
Here is an example:
package MyFramework; use Moose;
sub import { my $CALLER = caller();
strict->import; warnings->import;
# we should never export to main return if $CALLER eq 'main'; Moose::init_meta( $CALLER, 'MyFramework::Base' ); Moose->import({into => $CALLER});
# Do my custom framework stuff
return 1; }
Moose's import
method supports the the Sub::Exporter manpage form of {into => $pkg}
and {into_level => 1}
Moose does some boot strapping: it creates a metaclass object for your class,
and then injects a meta
accessor into your class to retrieve it. Then it
sets your baseclass to Moose::Object or the value you pass in unless you already
have one. This is all done via init_meta
which takes the name of your class
and optionally a baseclass and a metaclass as arguments.
super
and inner
cannot be used in the same
method. However, they may be combined within the same class hierarchy; see
t/014_override_augment_inner_super.t for an example.
The reason for this is that super
is only valid within a method
with the override
modifier, and inner
will never be valid within an
override
method. In fact, augment
will skip over any override
methods
when searching for its appropriate inner
.
This might seem like a restriction, but I am of the opinion that keeping these two features separate (yet interoperable) actually makes them easy to use, since their behavior is then easier to predict. Time will tell whether I am right or not (UPDATE: so far so good).
It is important to note that we currently have no simple way of combining multiple extended versions of Moose (see EXTENDING AND EMBEDDING MOOSE above), and that in many cases they will conflict with one another. We are working on developing a way around this issue, but in the meantime, you have been warned.
super
/override
and inner
/augment
features. If you really
want to understand them, I suggest you read this.
All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT.
Stevan Little <stevan@iinteractive.com>
with contributions from:
Aankhen
Adam (Alias) Kennedy
Anders (Debolaz) Nor Berle
Nathan (kolibre) Gray
Christian (chansen) Hansen
Hans Dieter (confound) Pearcey
Eric (ewilhelm) Wilhelm
Guillermo (groditi) Roditi
Jess (castaway) Robinson
Matt (mst) Trout
Robert (phaylon) Sedlacek
Robert (rlb3) Boone
Scott (konobi) McWhirter
Shlomi (rindolf) Fish
Yuval (nothingmuch) Kogman
Chris (perigrin) Prather
Jonathan (jrockway) Rockway
Piotr (dexter) Roszatycki
Sam (mugwump) Vilain
Shawn (sartak) Moore
... and many other #moose folks
Copyright 2006-2008 by Infinity Interactive, Inc.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
Moose - A postmodern object system for Perl 5 |