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<< requires => 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 are provided by,
197 of which C<Int> is one. For more information on the type constraint
198 system, see L<Moose::Util::TypeConstraints>.
202 The C<extends> keyword support 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 in a glob reference - see
211 L<MooseX::GlobRef::Object>).
219 =item Method Modifiers
221 The concept of method modifiers is directly ripped off from CLOS. A
222 great explanation of them can be found by following this link.
224 L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
230 my $point = Point->new( x => 1, y => 2 );
231 isa_ok( $point, 'Point' );
232 isa_ok( $point, 'Moose::Object' );
234 is( $point->x, 1, '... got the right value for x' );
235 is( $point->y, 2, '... got the right value for y' );
238 is( $point->y, 10, '... got the right (changed) value for y' );
245 '... cannot assign a non-Int to y'
253 '... must provide required attributes to new'
258 is( $point->x, 0, '... got the right (cleared) value for x' );
259 is( $point->y, 0, '... got the right (cleared) value for y' );
261 # check the type constraints on the constructor
265 Point->new( x => 0, y => 0 );
268 '... can assign a 0 to x and y'
273 Point->new( x => 10, y => 'Foo' );
276 '... cannot assign a non-Int to y'
281 Point->new( x => 'Foo', y => 10 );
284 '... cannot assign a non-Int to x'
289 my $point3d = Point3D->new( { x => 10, y => 15, z => 3 } );
290 isa_ok( $point3d, 'Point3D' );
291 isa_ok( $point3d, 'Point' );
292 isa_ok( $point3d, 'Moose::Object' );
294 is( $point3d->x, 10, '... got the right value for x' );
295 is( $point3d->y, 15, '... got the right value for y' );
296 is( $point3d->{'z'}, 3, '... got the right value for z' );
300 is( $point3d->x, 0, '... got the right (cleared) value for x' );
301 is( $point3d->y, 0, '... got the right (cleared) value for y' );
302 is( $point3d->z, 0, '... got the right (cleared) value for z' );
306 Point3D->new( x => 10, y => 'Foo', z => 3 );
309 '... cannot assign a non-Int to y'
314 Point3D->new( x => 'Foo', y => 10, z => 3 );
317 '... cannot assign a non-Int to x'
322 Point3D->new( x => 0, y => 10, z => 'Bar' );
325 '... cannot assign a non-Int to z'
330 Point3D->new( x => 10, y => 3 );
333 '... z is a required attribute for Point3D'
336 # test some class introspection
338 can_ok( 'Point', 'meta' );
339 isa_ok( Point->meta, 'Moose::Meta::Class' );
341 can_ok( 'Point3D', 'meta' );
342 isa_ok( Point3D->meta, 'Moose::Meta::Class' );
345 Point->meta, Point3D->meta,
346 '... they are different metaclasses as well'
352 [ Point->meta->superclasses ],
354 '... Point got the automagic base class'
357 my @Point_methods = qw(meta x y clear);
358 my @Point_attrs = ( 'x', 'y' );
361 [ sort @Point_methods ],
362 [ sort Point->meta->get_method_list() ],
363 '... we match the method list for Point'
367 [ sort @Point_attrs ],
368 [ sort Point->meta->get_attribute_list() ],
369 '... we match the attribute list for Point'
372 foreach my $method (@Point_methods) {
373 ok( Point->meta->has_method($method),
374 '... Point has the method "' . $method . '"' );
377 foreach my $attr_name (@Point_attrs) {
378 ok( Point->meta->has_attribute($attr_name),
379 '... Point has the attribute "' . $attr_name . '"' );
380 my $attr = Point->meta->get_attribute($attr_name);
381 ok( $attr->has_type_constraint,
382 '... Attribute ' . $attr_name . ' has a type constraint' );
383 isa_ok( $attr->type_constraint, 'Moose::Meta::TypeConstraint' );
384 is( $attr->type_constraint->name, 'Int',
385 '... Attribute ' . $attr_name . ' has an Int type constraint' );
391 [ Point3D->meta->superclasses ],
393 '... Point3D gets the parent given to it'
396 my @Point3D_methods = qw( meta z clear );
397 my @Point3D_attrs = ('z');
400 [ sort @Point3D_methods ],
401 [ sort Point3D->meta->get_method_list() ],
402 '... we match the method list for Point3D'
406 [ sort @Point3D_attrs ],
407 [ sort Point3D->meta->get_attribute_list() ],
408 '... we match the attribute list for Point3D'
411 foreach my $method (@Point3D_methods) {
412 ok( Point3D->meta->has_method($method),
413 '... Point3D has the method "' . $method . '"' );
416 foreach my $attr_name (@Point3D_attrs) {
417 ok( Point3D->meta->has_attribute($attr_name),
418 '... Point3D has the attribute "' . $attr_name . '"' );
419 my $attr = Point3D->meta->get_attribute($attr_name);
420 ok( $attr->has_type_constraint,
421 '... Attribute ' . $attr_name . ' has a type constraint' );
422 isa_ok( $attr->type_constraint, 'Moose::Meta::TypeConstraint' );
423 is( $attr->type_constraint->name, 'Int',
424 '... Attribute ' . $attr_name . ' has an Int type constraint' );