6 Moose::Cookbook::Basics::Recipe1 - The (always classic) B<Point> example.
13 has 'x' => (isa => 'Int', is => 'rw', required => 1);
14 has 'y' => (isa => 'Int', is => 'rw', required => 1);
27 has 'z' => (isa => 'Int', is => 'rw', required => 1);
29 after 'clear' => sub {
36 # hash or hashrefs are ok for the constructor
37 my $point1 = Point->new(x => 5, y => 7);
38 my $point2 = Point->new({x => 5, y => 7});
40 my $point3d = Point3D->new(x => 5, y => 42, z => -5);
44 This is the classic Point example. It is taken directly from the Perl
45 6 Apocalypse 12 document, and is similar to the example found in the
46 classic K&R C book as well.
48 As with all Perl 5 classes, a Moose class is defined in a package.
49 Moose handles turning on C<strict> and C<warnings> for us, so all we
50 need to do is say C<use Moose>, and no kittens will die.
52 When Moose is loaded, it exports a set of sugar functions into our
53 package. This means that we import some functions which serve as Moose
54 "keywords". These aren't real language keywords, they're just Perl
55 functions exported into our package.
57 Moose automatically makes our package a subclass of L<Moose::Object>.
58 The L<Moose::Object> class provides us with a constructor that
59 respects our attributes, as well other features. See L<Moose::Object>
62 Now, onto the keywords. The first one we see here is C<has>, which
63 defines an instance attribute in our class:
65 has 'x' => (isa => 'Int', is => 'rw', required => 1);
67 This will create an attribute named C<x>. The C<isa> parameter says
68 that we expect the value stored in this attribute to pass the type
69 constraint for C<Int> (1). The accessor generated for this attribute
72 The C<< requires => 1 >> parameter means that this attribute must be
73 provided when a new object is created. A point object without
74 coordinates doesn't make much sense, so we don't allow it.
76 We have defined our attributes; next we define our methods. In Moose,
77 as with regular Perl 5 OO, a method is just a subroutine defined
86 That concludes the B<Point> class.
88 Next we have a subclass of B<Point>, B<Point3D>. To declare our
89 superclass, we use the Moose keyword C<extends>:
93 The C<extends> keyword works much like C<use base>. First, it will
94 attempt to load your class if needed. However, unlike C<base>, the
95 C<extends> keyword will I<overwrite> any previous values in your
96 package's C<@ISA>, where C<use base> will C<push> values onto the
99 It is my opinion that the behavior of C<extends> is more intuitive.
102 Next we create a new attribute for B<Point3D> called C<z>.
104 has 'z' => (isa => 'Int', is => 'rw', required => 1);
106 This attribute is just like B<Point>'s C<x> and C<y> attributes.
108 The C<after> keyword demonstrates a Moose feature called "method
109 modifiers" (or "advice" for the AOP inclined):
111 after 'clear' => sub {
116 When C<clear> is called on a B<Point3D> object, our modifier method
117 gets called as well. Unsurprisingly, the modifier is called I<after>
120 In this case, the real C<clear> method is inherited from B<Point>. Our
121 modifier method receives the same arguments as those passed to the
122 modified method (just C<$self> here).
124 Of course, using the C<after> modifier is not the only way to
125 accomplish this. This B<is> Perl, right? You can get the same results
130 $self->SUPER::clear();
134 You could also use another Moose method modifier, C<override>:
136 override 'clear' => sub {
142 The C<override> modifier allows you to use the C<super> keyword to
143 dispatch to the superclass's method in a very Ruby-ish style.
145 The choice of whether to use a method modifier, and which one to use,
146 is often a question of style as much as functionality.
148 Since B<Point> inherits from L<Moose::Object>, it will also inherit
149 the default L<Moose::Object> constructor:
151 my $point1 = Point->new(x => 5, y => 7);
152 my $point2 = Point->new({x => 5, y => 7});
154 my $point3d = Point3D->new(x => 5, y => 42, z => -5);
156 The C<new> constructor accepts a named argument pair for each
157 attribute defined by the class, which you can provide as a hash or
158 hash reference. In this particular example, the attributes are
159 required, and calling C<new> without them will throw an error.
161 my $point = Point->new( x => 5 ); # no y, kaboom!
163 From here on, we can use C<$point> and C<$point3d> just as you would
164 any other Perl 5 object. For a more detailed example of what can be
165 done, you can refer to the
166 F<t/000_recipes/moose_cookbook_basics_recipe1.t> test file.
168 =head2 Moose Objects are Just Hashrefs
170 While this all may appear rather magical, it's important to realize
171 that Moose objects are just hash references under the hood (3). For
172 example, you could pass C<$self> to C<Data::Dumper> and you'd get
173 exactly what you'd expect.
175 You could even poke around inside the object's data structure, but
176 that is strongly discouraged.
178 The fact that Moose objects are hashrefs means it is easy to use Moose
179 to extend non-Moose classes, as long as they too are hash
180 references. If you want to extend a non-hashref class, check out
181 C<MooseX::InsideOut>.
185 This recipe demonstrates some basic Moose concepts, attributes,
186 subclassing, and a simple method modifier.
194 Moose provides a number of builtin type constraints are provided by,
195 of which C<Int> is one. For more information on the type constraint
196 system, see L<Moose::Util::TypeConstraints>.
200 The C<extends> keyword support multiple inheritance. Simply pass all
201 of your superclasses to C<extends> as a list:
203 extends 'Foo', 'Bar', 'Baz';
207 Moose supports using instance structures other than blessed hash
208 references (such as in a glob reference - see
209 L<MooseX::GlobRef::Object>).
217 =item Method Modifiers
219 The concept of method modifiers is directly ripped off from CLOS. A
220 great explanation of them can be found by following this link.
222 L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
228 Stevan Little E<lt>stevan@iinteractive.comE<gt>
230 Dave Rolsky E<lt>autarch@urth.orgE<gt>
232 =head1 COPYRIGHT AND LICENSE
234 Copyright 2006-2009 by Infinity Interactive, Inc.
236 L<http://www.iinteractive.com>
238 This library is free software; you can redistribute it and/or modify
239 it under the same terms as Perl itself.
243 my $point = Point->new( x => 1, y => 2 );
244 isa_ok( $point, 'Point' );
245 isa_ok( $point, 'Moose::Object' );
247 is( $point->x, 1, '... got the right value for x' );
248 is( $point->y, 2, '... got the right value for y' );
251 is( $point->y, 10, '... got the right (changed) value for y' );
256 '... cannot assign a non-Int to y';
261 '... must provide required attributes to new';
265 is( $point->x, 0, '... got the right (cleared) value for x' );
266 is( $point->y, 0, '... got the right (cleared) value for y' );
268 # check the type constraints on the constructor
271 Point->new( x => 0, y => 0 );
273 '... can assign a 0 to x and y';
276 Point->new( x => 10, y => 'Foo' );
278 '... cannot assign a non-Int to y';
281 Point->new( x => 'Foo', y => 10 );
283 '... cannot assign a non-Int to x';
287 my $point3d = Point3D->new( { x => 10, y => 15, z => 3 } );
288 isa_ok( $point3d, 'Point3D' );
289 isa_ok( $point3d, 'Point' );
290 isa_ok( $point3d, 'Moose::Object' );
292 is( $point3d->x, 10, '... got the right value for x' );
293 is( $point3d->y, 15, '... got the right value for y' );
294 is( $point3d->{'z'}, 3, '... got the right value for z' );
298 is( $point3d->x, 0, '... got the right (cleared) value for x' );
299 is( $point3d->y, 0, '... got the right (cleared) value for y' );
300 is( $point3d->z, 0, '... got the right (cleared) value for z' );
303 Point3D->new( x => 10, y => 'Foo', z => 3 );
305 '... cannot assign a non-Int to y';
308 Point3D->new( x => 'Foo', y => 10, z => 3 );
310 '... cannot assign a non-Int to x';
313 Point3D->new( x => 0, y => 10, z => 'Bar' );
315 '... cannot assign a non-Int to z';
318 Point3D->new( x => 10, y => 3 );
320 '... z is a required attribute for Point3D';
322 # test some class introspection
324 can_ok( 'Point', 'meta' );
325 isa_ok( Point->meta, 'Moose::Meta::Class' );
327 can_ok( 'Point3D', 'meta' );
328 isa_ok( Point3D->meta, 'Moose::Meta::Class' );
330 isnt( Point->meta, Point3D->meta,
331 '... they are different metaclasses as well' );
336 [ Point->meta->superclasses ],
338 '... Point got the automagic base class'
341 my @Point_methods = qw(meta x y clear);
342 my @Point_attrs = ( 'x', 'y' );
345 [ sort @Point_methods ],
346 [ sort Point->meta->get_method_list() ],
347 '... we match the method list for Point'
351 [ sort @Point_attrs ],
352 [ sort Point->meta->get_attribute_list() ],
353 '... we match the attribute list for Point'
356 foreach my $method (@Point_methods) {
357 ok( Point->meta->has_method($method),
358 '... Point has the method "' . $method . '"' );
361 foreach my $attr_name (@Point_attrs) {
362 ok( Point->meta->has_attribute($attr_name),
363 '... Point has the attribute "' . $attr_name . '"' );
364 my $attr = Point->meta->get_attribute($attr_name);
365 ok( $attr->has_type_constraint,
366 '... Attribute ' . $attr_name . ' has a type constraint' );
367 isa_ok( $attr->type_constraint, 'Moose::Meta::TypeConstraint' );
368 is( $attr->type_constraint->name, 'Int',
369 '... Attribute ' . $attr_name . ' has an Int type constraint' );
375 [ Point3D->meta->superclasses ],
377 '... Point3D gets the parent given to it'
380 my @Point3D_methods = qw( meta z clear );
381 my @Point3D_attrs = ('z');
384 [ sort @Point3D_methods ],
385 [ sort Point3D->meta->get_method_list() ],
386 '... we match the method list for Point3D'
390 [ sort @Point3D_attrs ],
391 [ sort Point3D->meta->get_attribute_list() ],
392 '... we match the attribute list for Point3D'
395 foreach my $method (@Point3D_methods) {
396 ok( Point3D->meta->has_method($method),
397 '... Point3D has the method "' . $method . '"' );
400 foreach my $attr_name (@Point3D_attrs) {
401 ok( Point3D->meta->has_attribute($attr_name),
402 '... Point3D has the attribute "' . $attr_name . '"' );
403 my $attr = Point3D->meta->get_attribute($attr_name);
404 ok( $attr->has_type_constraint,
405 '... Attribute ' . $attr_name . ' has a type constraint' );
406 isa_ok( $attr->type_constraint, 'Moose::Meta::TypeConstraint' );
407 is( $attr->type_constraint->name, 'Int',
408 '... Attribute ' . $attr_name . ' has an Int type constraint' );