1 package Moose::Cookbook::Basics::Recipe1;
3 # ABSTRACT: The (always classic) B<Point> example.
15 has 'x' => (isa => 'Int', is => 'rw', required => 1);
16 has 'y' => (isa => 'Int', is => 'rw', required => 1);
29 has 'z' => (isa => 'Int', is => 'rw', required => 1);
31 after 'clear' => sub {
38 # hash or hashrefs are ok for the constructor
39 my $point1 = Point->new(x => 5, y => 7);
40 my $point2 = Point->new({x => 5, y => 7});
42 my $point3d = Point3D->new(x => 5, y => 42, z => -5);
46 This is the classic Point example. It is taken directly from the Perl
47 6 Apocalypse 12 document, and is similar to the example found in the
48 classic K&R C book as well.
50 As with all Perl 5 classes, a Moose class is defined in a package.
51 Moose handles turning on C<strict> and C<warnings> for us, so all we
52 need to do is say C<use Moose>, and no kittens will die.
54 When Moose is loaded, it exports a set of sugar functions into our
55 package. This means that we import some functions which serve as Moose
56 "keywords". These aren't real language keywords, they're just Perl
57 functions exported into our package.
59 Moose automatically makes our package a subclass of L<Moose::Object>.
60 The L<Moose::Object> class provides us with a constructor that
61 respects our attributes, as well other features. See L<Moose::Object>
64 Now, onto the keywords. The first one we see here is C<has>, which
65 defines an instance attribute in our class:
67 has 'x' => (isa => 'Int', is => 'rw', required => 1);
69 This will create an attribute named C<x>. The C<isa> parameter says
70 that we expect the value stored in this attribute to pass the type
71 constraint for C<Int> (1). The accessor generated for this attribute
74 The C<< required => 1 >> parameter means that this attribute must be
75 provided when a new object is created. A point object without
76 coordinates doesn't make much sense, so we don't allow it.
78 We have defined our attributes; next we define our methods. In Moose,
79 as with regular Perl 5 OO, a method is just a subroutine defined
88 That concludes the B<Point> class.
90 Next we have a subclass of B<Point>, B<Point3D>. To declare our
91 superclass, we use the Moose keyword C<extends>:
95 The C<extends> keyword works much like C<use base>. First, it will
96 attempt to load your class if needed. However, unlike C<base>, the
97 C<extends> keyword will I<overwrite> any previous values in your
98 package's C<@ISA>, where C<use base> will C<push> values onto the
101 It is my opinion that the behavior of C<extends> is more intuitive.
104 Next we create a new attribute for B<Point3D> called C<z>.
106 has 'z' => (isa => 'Int', is => 'rw', required => 1);
108 This attribute is just like B<Point>'s C<x> and C<y> attributes.
110 The C<after> keyword demonstrates a Moose feature called "method
111 modifiers" (or "advice" for the AOP inclined):
113 after 'clear' => sub {
118 When C<clear> is called on a B<Point3D> object, our modifier method
119 gets called as well. Unsurprisingly, the modifier is called I<after>
122 In this case, the real C<clear> method is inherited from B<Point>. Our
123 modifier method receives the same arguments as those passed to the
124 modified method (just C<$self> here).
126 Of course, using the C<after> modifier is not the only way to
127 accomplish this. This B<is> Perl, right? You can get the same results
132 $self->SUPER::clear();
136 You could also use another Moose method modifier, C<override>:
138 override 'clear' => sub {
144 The C<override> modifier allows you to use the C<super> keyword to
145 dispatch to the superclass's method in a very Ruby-ish style.
147 The choice of whether to use a method modifier, and which one to use,
148 is often a question of style as much as functionality.
150 Since B<Point> inherits from L<Moose::Object>, it will also inherit
151 the default L<Moose::Object> constructor:
153 my $point1 = Point->new(x => 5, y => 7);
154 my $point2 = Point->new({x => 5, y => 7});
156 my $point3d = Point3D->new(x => 5, y => 42, z => -5);
158 The C<new> constructor accepts a named argument pair for each
159 attribute defined by the class, which you can provide as a hash or
160 hash reference. In this particular example, the attributes are
161 required, and calling C<new> without them will throw an error.
163 my $point = Point->new( x => 5 ); # no y, kaboom!
165 From here on, we can use C<$point> and C<$point3d> just as you would
166 any other Perl 5 object. For a more detailed example of what can be
167 done, you can refer to the
168 F<t/recipes/moose_cookbook_basics_recipe1.t> test file.
170 =head2 Moose Objects are Just Hashrefs
172 While this all may appear rather magical, it's important to realize
173 that Moose objects are just hash references under the hood (3). For
174 example, you could pass C<$self> to C<Data::Dumper> and you'd get
175 exactly what you'd expect.
177 You could even poke around inside the object's data structure, but
178 that is strongly discouraged.
180 The fact that Moose objects are hashrefs means it is easy to use Moose
181 to extend non-Moose classes, as long as they too are hash
182 references. If you want to extend a non-hashref class, check out
183 C<MooseX::InsideOut>.
187 This recipe demonstrates some basic Moose concepts, attributes,
188 subclassing, and a simple method modifier.
196 Moose provides a number of builtin type constraints, of which C<Int>
197 is one. For more information on the type constraint system, see
198 L<Moose::Util::TypeConstraints>.
202 The C<extends> keyword supports multiple inheritance. Simply pass all
203 of your superclasses to C<extends> as a list:
205 extends 'Foo', 'Bar', 'Baz';
209 Moose supports using instance structures other than blessed hash
210 references (such as glob references - see L<MooseX::GlobRef>).
218 =item Method Modifiers
220 The concept of method modifiers is directly ripped off from CLOS. A
221 great explanation of them can be found by following this link.
223 L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
229 my $point = Point->new( x => 1, y => 2 );
230 isa_ok( $point, 'Point' );
231 isa_ok( $point, 'Moose::Object' );
233 is( $point->x, 1, '... got the right value for x' );
234 is( $point->y, 2, '... got the right value for y' );
237 is( $point->y, 10, '... got the right (changed) value for y' );
244 '... cannot assign a non-Int to y'
252 '... must provide required attributes to new'
257 is( $point->x, 0, '... got the right (cleared) value for x' );
258 is( $point->y, 0, '... got the right (cleared) value for y' );
260 # check the type constraints on the constructor
264 Point->new( x => 0, y => 0 );
267 '... can assign a 0 to x and y'
272 Point->new( x => 10, y => 'Foo' );
275 '... cannot assign a non-Int to y'
280 Point->new( x => 'Foo', y => 10 );
283 '... cannot assign a non-Int to x'
288 my $point3d = Point3D->new( { x => 10, y => 15, z => 3 } );
289 isa_ok( $point3d, 'Point3D' );
290 isa_ok( $point3d, 'Point' );
291 isa_ok( $point3d, 'Moose::Object' );
293 is( $point3d->x, 10, '... got the right value for x' );
294 is( $point3d->y, 15, '... got the right value for y' );
295 is( $point3d->{'z'}, 3, '... got the right value for z' );
299 is( $point3d->x, 0, '... got the right (cleared) value for x' );
300 is( $point3d->y, 0, '... got the right (cleared) value for y' );
301 is( $point3d->z, 0, '... got the right (cleared) value for z' );
305 Point3D->new( x => 10, y => 'Foo', z => 3 );
308 '... cannot assign a non-Int to y'
313 Point3D->new( x => 'Foo', y => 10, z => 3 );
316 '... cannot assign a non-Int to x'
321 Point3D->new( x => 0, y => 10, z => 'Bar' );
324 '... cannot assign a non-Int to z'
329 Point3D->new( x => 10, y => 3 );
332 '... z is a required attribute for Point3D'
335 # test some class introspection
337 can_ok( 'Point', 'meta' );
338 isa_ok( Point->meta, 'Moose::Meta::Class' );
340 can_ok( 'Point3D', 'meta' );
341 isa_ok( Point3D->meta, 'Moose::Meta::Class' );
344 Point->meta, Point3D->meta,
345 '... they are different metaclasses as well'
351 [ Point->meta->superclasses ],
353 '... Point got the automagic base class'
356 my @Point_methods = qw(meta x y clear);
357 my @Point_attrs = ( 'x', 'y' );
360 [ sort @Point_methods ],
361 [ sort Point->meta->get_method_list() ],
362 '... we match the method list for Point'
366 [ sort @Point_attrs ],
367 [ sort Point->meta->get_attribute_list() ],
368 '... we match the attribute list for Point'
371 foreach my $method (@Point_methods) {
372 ok( Point->meta->has_method($method),
373 '... Point has the method "' . $method . '"' );
376 foreach my $attr_name (@Point_attrs) {
377 ok( Point->meta->has_attribute($attr_name),
378 '... Point has the attribute "' . $attr_name . '"' );
379 my $attr = Point->meta->get_attribute($attr_name);
380 ok( $attr->has_type_constraint,
381 '... Attribute ' . $attr_name . ' has a type constraint' );
382 isa_ok( $attr->type_constraint, 'Moose::Meta::TypeConstraint' );
383 is( $attr->type_constraint->name, 'Int',
384 '... Attribute ' . $attr_name . ' has an Int type constraint' );
390 [ Point3D->meta->superclasses ],
392 '... Point3D gets the parent given to it'
395 my @Point3D_methods = qw( meta z clear );
396 my @Point3D_attrs = ('z');
399 [ sort @Point3D_methods ],
400 [ sort Point3D->meta->get_method_list() ],
401 '... we match the method list for Point3D'
405 [ sort @Point3D_attrs ],
406 [ sort Point3D->meta->get_attribute_list() ],
407 '... we match the attribute list for Point3D'
410 foreach my $method (@Point3D_methods) {
411 ok( Point3D->meta->has_method($method),
412 '... Point3D has the method "' . $method . '"' );
415 foreach my $attr_name (@Point3D_attrs) {
416 ok( Point3D->meta->has_attribute($attr_name),
417 '... Point3D has the attribute "' . $attr_name . '"' );
418 my $attr = Point3D->meta->get_attribute($attr_name);
419 ok( $attr->has_type_constraint,
420 '... Attribute ' . $attr_name . ' has a type constraint' );
421 isa_ok( $attr->type_constraint, 'Moose::Meta::TypeConstraint' );
422 is( $attr->type_constraint->name, 'Int',
423 '... Attribute ' . $attr_name . ' has an Int type constraint' );