Dzil-ize all the .pod files so they can be pod-woven
[gitmo/Moose.git] / lib / Moose / Cookbook / Basics / Recipe1.pod
1 package Moose::Cookbook::Basics::Recipe1;
2
3 # ABSTRACT: The (always classic) B<Point> example.
4
5 __END__
6
7
8 =pod
9
10 =head1 SYNOPSIS
11
12   package Point;
13   use Moose;
14
15   has 'x' => (isa => 'Int', is => 'rw', required => 1);
16   has 'y' => (isa => 'Int', is => 'rw', required => 1);
17
18   sub clear {
19       my $self = shift;
20       $self->x(0);
21       $self->y(0);
22   }
23
24   package Point3D;
25   use Moose;
26
27   extends 'Point';
28
29   has 'z' => (isa => 'Int', is => 'rw', required => 1);
30
31   after 'clear' => sub {
32       my $self = shift;
33       $self->z(0);
34   };
35
36   package main;
37
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});
41
42   my $point3d = Point3D->new(x => 5, y => 42, z => -5);
43
44 =head1 DESCRIPTION
45
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.
49
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.
53
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.
58
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>
62 for details.
63
64 Now, onto the keywords. The first one we see here is C<has>, which
65 defines an instance attribute in our class:
66
67   has 'x' => (isa => 'Int', is => 'rw', required => 1);
68
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
72 will be read-write.
73
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.
77
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
80 within the package:
81
82   sub clear {
83       my $self = shift;
84       $self->x(0);
85       $self->y(0);
86   }
87
88 That concludes the B<Point> class.
89
90 Next we have a subclass of B<Point>, B<Point3D>. To declare our
91 superclass, we use the Moose keyword C<extends>:
92
93   extends 'Point';
94
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
99 package's C<@ISA>.
100
101 It is my opinion that the behavior of C<extends> is more intuitive.
102 (2).
103
104 Next we create a new attribute for B<Point3D> called C<z>.
105
106   has 'z' => (isa => 'Int', is => 'rw', required => 1);
107
108 This attribute is just like B<Point>'s C<x> and C<y> attributes.
109
110 The C<after> keyword demonstrates a Moose feature called "method
111 modifiers" (or "advice" for the AOP inclined):
112
113   after 'clear' => sub {
114       my $self = shift;
115       $self->z(0);
116   };
117
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>
120 the real method.
121
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).
125
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
128 with this code:
129
130   sub clear {
131       my $self = shift;
132       $self->SUPER::clear();
133       $self->z(0);
134   }
135
136 You could also use another Moose method modifier, C<override>:
137
138   override 'clear' => sub {
139       my $self = shift;
140       super();
141       $self->z(0);
142   };
143
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.
146
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.
149
150 Since B<Point> inherits from L<Moose::Object>, it will also inherit
151 the default L<Moose::Object> constructor:
152
153   my $point1 = Point->new(x => 5, y => 7);
154   my $point2 = Point->new({x => 5, y => 7});
155
156   my $point3d = Point3D->new(x => 5, y => 42, z => -5);
157
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.
162
163   my $point = Point->new( x => 5 ); # no y, kaboom!
164
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/000_recipes/moose_cookbook_basics_recipe1.t> test file.
169
170 =head2 Moose Objects are Just Hashrefs
171
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.
176
177 You could even poke around inside the object's data structure, but
178 that is strongly discouraged.
179
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>.
184
185 =head1 CONCLUSION
186
187 This recipe demonstrates some basic Moose concepts, attributes,
188 subclassing, and a simple method modifier.
189
190 =head1 FOOTNOTES
191
192 =over 4
193
194 =item (1)
195
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>.
199
200 =item (2)
201
202 The C<extends> keyword support multiple inheritance. Simply pass all
203 of your superclasses to C<extends> as a list:
204
205   extends 'Foo', 'Bar', 'Baz';
206
207 =item (3)
208
209 Moose supports using instance structures other than blessed hash
210 references (such as in a glob reference - see
211 L<MooseX::GlobRef::Object>).
212
213 =back
214
215 =head1 SEE ALSO
216
217 =over 4
218
219 =item Method Modifiers
220
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.
223
224 L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
225
226 =back
227
228 =begin testing
229
230 my $point = Point->new( x => 1, y => 2 );
231 isa_ok( $point, 'Point' );
232 isa_ok( $point, 'Moose::Object' );
233
234 is( $point->x, 1, '... got the right value for x' );
235 is( $point->y, 2, '... got the right value for y' );
236
237 $point->y(10);
238 is( $point->y, 10, '... got the right (changed) value for y' );
239
240 isnt(
241     exception {
242         $point->y('Foo');
243     },
244     undef,
245     '... cannot assign a non-Int to y'
246 );
247
248 isnt(
249     exception {
250         Point->new();
251     },
252     undef,
253     '... must provide required attributes to new'
254 );
255
256 $point->clear();
257
258 is( $point->x, 0, '... got the right (cleared) value for x' );
259 is( $point->y, 0, '... got the right (cleared) value for y' );
260
261 # check the type constraints on the constructor
262
263 is(
264     exception {
265         Point->new( x => 0, y => 0 );
266     },
267     undef,
268     '... can assign a 0 to x and y'
269 );
270
271 isnt(
272     exception {
273         Point->new( x => 10, y => 'Foo' );
274     },
275     undef,
276     '... cannot assign a non-Int to y'
277 );
278
279 isnt(
280     exception {
281         Point->new( x => 'Foo', y => 10 );
282     },
283     undef,
284     '... cannot assign a non-Int to x'
285 );
286
287 # Point3D
288
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' );
293
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' );
297
298 $point3d->clear();
299
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' );
303
304 isnt(
305     exception {
306         Point3D->new( x => 10, y => 'Foo', z => 3 );
307     },
308     undef,
309     '... cannot assign a non-Int to y'
310 );
311
312 isnt(
313     exception {
314         Point3D->new( x => 'Foo', y => 10, z => 3 );
315     },
316     undef,
317     '... cannot assign a non-Int to x'
318 );
319
320 isnt(
321     exception {
322         Point3D->new( x => 0, y => 10, z => 'Bar' );
323     },
324     undef,
325     '... cannot assign a non-Int to z'
326 );
327
328 isnt(
329     exception {
330         Point3D->new( x => 10, y => 3 );
331     },
332     undef,
333     '... z is a required attribute for Point3D'
334 );
335
336 # test some class introspection
337
338 can_ok( 'Point', 'meta' );
339 isa_ok( Point->meta, 'Moose::Meta::Class' );
340
341 can_ok( 'Point3D', 'meta' );
342 isa_ok( Point3D->meta, 'Moose::Meta::Class' );
343
344 isnt(
345     Point->meta, Point3D->meta,
346     '... they are different metaclasses as well'
347 );
348
349 # poke at Point
350
351 is_deeply(
352     [ Point->meta->superclasses ],
353     ['Moose::Object'],
354     '... Point got the automagic base class'
355 );
356
357 my @Point_methods = qw(meta x y clear);
358 my @Point_attrs = ( 'x', 'y' );
359
360 is_deeply(
361     [ sort @Point_methods ],
362     [ sort Point->meta->get_method_list() ],
363     '... we match the method list for Point'
364 );
365
366 is_deeply(
367     [ sort @Point_attrs ],
368     [ sort Point->meta->get_attribute_list() ],
369     '... we match the attribute list for Point'
370 );
371
372 foreach my $method (@Point_methods) {
373     ok( Point->meta->has_method($method),
374         '... Point has the method "' . $method . '"' );
375 }
376
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' );
386 }
387
388 # poke at Point3D
389
390 is_deeply(
391     [ Point3D->meta->superclasses ],
392     ['Point'],
393     '... Point3D gets the parent given to it'
394 );
395
396 my @Point3D_methods = qw( meta z clear );
397 my @Point3D_attrs   = ('z');
398
399 is_deeply(
400     [ sort @Point3D_methods ],
401     [ sort Point3D->meta->get_method_list() ],
402     '... we match the method list for Point3D'
403 );
404
405 is_deeply(
406     [ sort @Point3D_attrs ],
407     [ sort Point3D->meta->get_attribute_list() ],
408     '... we match the attribute list for Point3D'
409 );
410
411 foreach my $method (@Point3D_methods) {
412     ok( Point3D->meta->has_method($method),
413         '... Point3D has the method "' . $method . '"' );
414 }
415
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' );
425 }
426
427 =end testing
428
429 =cut