remove trailing whitespace
[gitmo/Moose.git] / lib / Moose / Cookbook / Basics / Recipe1.pod
1
2 =pod
3
4 =head1 NAME
5
6 Moose::Cookbook::Basics::Recipe1 - The (always classic) B<Point> example.
7
8 =head1 SYNOPSIS
9
10   package Point;
11   use Moose;
12
13   has 'x' => (isa => 'Int', is => 'rw', required => 1);
14   has 'y' => (isa => 'Int', is => 'rw', required => 1);
15
16   sub clear {
17       my $self = shift;
18       $self->x(0);
19       $self->y(0);
20   }
21
22   package Point3D;
23   use Moose;
24
25   extends 'Point';
26
27   has 'z' => (isa => 'Int', is => 'rw', required => 1);
28
29   after 'clear' => sub {
30       my $self = shift;
31       $self->z(0);
32   };
33
34   package main;
35
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});
39
40   my $point3d = Point3D->new(x => 5, y => 42, z => -5);
41
42 =head1 DESCRIPTION
43
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.
47
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.
51
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.
56
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>
60 for details.
61
62 Now, onto the keywords. The first one we see here is C<has>, which
63 defines an instance attribute in our class:
64
65   has 'x' => (isa => 'Int', is => 'rw', required => 1);
66
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
70 will be read-write.
71
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.
75
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
78 within the package:
79
80   sub clear {
81       my $self = shift;
82       $self->x(0);
83       $self->y(0);
84   }
85
86 That concludes the B<Point> class.
87
88 Next we have a subclass of B<Point>, B<Point3D>. To declare our
89 superclass, we use the Moose keyword C<extends>:
90
91   extends 'Point';
92
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
97 package's C<@ISA>.
98
99 It is my opinion that the behavior of C<extends> is more intuitive.
100 (2).
101
102 Next we create a new attribute for B<Point3D> called C<z>.
103
104   has 'z' => (isa => 'Int', is => 'rw', required => 1);
105
106 This attribute is just like B<Point>'s C<x> and C<y> attributes.
107
108 The C<after> keyword demonstrates a Moose feature called "method
109 modifiers" (or "advice" for the AOP inclined):
110
111   after 'clear' => sub {
112       my $self = shift;
113       $self->z(0);
114   };
115
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>
118 the real method.
119
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).
123
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
126 with this code:
127
128   sub clear {
129       my $self = shift;
130       $self->SUPER::clear();
131       $self->z(0);
132   }
133
134 You could also use another Moose method modifier, C<override>:
135
136   override 'clear' => sub {
137       my $self = shift;
138       super();
139       $self->z(0);
140   };
141
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.
144
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.
147
148 Since B<Point> inherits from L<Moose::Object>, it will also inherit
149 the default L<Moose::Object> constructor:
150
151   my $point1 = Point->new(x => 5, y => 7);
152   my $point2 = Point->new({x => 5, y => 7});
153
154   my $point3d = Point3D->new(x => 5, y => 42, z => -5);
155
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.
160
161   my $point = Point->new( x => 5 ); # no y, kaboom!
162
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.
167
168 =head2 Moose Objects are Just Hashrefs
169
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.
174
175 You could even poke around inside the object's data structure, but
176 that is strongly discouraged.
177
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>.
182
183 =head1 CONCLUSION
184
185 This recipe demonstrates some basic Moose concepts, attributes,
186 subclassing, and a simple method modifier.
187
188 =head1 FOOTNOTES
189
190 =over 4
191
192 =item (1)
193
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>.
197
198 =item (2)
199
200 The C<extends> keyword support multiple inheritance. Simply pass all
201 of your superclasses to C<extends> as a list:
202
203   extends 'Foo', 'Bar', 'Baz';
204
205 =item (3)
206
207 Moose supports using instance structures other than blessed hash
208 references (such as in a glob reference - see
209 L<MooseX::GlobRef::Object>).
210
211 =back
212
213 =head1 SEE ALSO
214
215 =over 4
216
217 =item Method Modifiers
218
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.
221
222 L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
223
224 =back
225
226 =head1 AUTHORS
227
228 Stevan Little E<lt>stevan@iinteractive.comE<gt>
229
230 Dave Rolsky E<lt>autarch@urth.orgE<gt>
231
232 =head1 COPYRIGHT AND LICENSE
233
234 Copyright 2006-2009 by Infinity Interactive, Inc.
235
236 L<http://www.iinteractive.com>
237
238 This library is free software; you can redistribute it and/or modify
239 it under the same terms as Perl itself.
240
241 =begin testing
242
243 my $point = Point->new( x => 1, y => 2 );
244 isa_ok( $point, 'Point' );
245 isa_ok( $point, 'Moose::Object' );
246
247 is( $point->x, 1, '... got the right value for x' );
248 is( $point->y, 2, '... got the right value for y' );
249
250 $point->y(10);
251 is( $point->y, 10, '... got the right (changed) value for y' );
252
253 dies_ok {
254     $point->y('Foo');
255 }
256 '... cannot assign a non-Int to y';
257
258 dies_ok {
259     Point->new();
260 }
261 '... must provide required attributes to new';
262
263 $point->clear();
264
265 is( $point->x, 0, '... got the right (cleared) value for x' );
266 is( $point->y, 0, '... got the right (cleared) value for y' );
267
268 # check the type constraints on the constructor
269
270 lives_ok {
271     Point->new( x => 0, y => 0 );
272 }
273 '... can assign a 0 to x and y';
274
275 dies_ok {
276     Point->new( x => 10, y => 'Foo' );
277 }
278 '... cannot assign a non-Int to y';
279
280 dies_ok {
281     Point->new( x => 'Foo', y => 10 );
282 }
283 '... cannot assign a non-Int to x';
284
285 # Point3D
286
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' );
291
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' );
295
296 $point3d->clear();
297
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' );
301
302 dies_ok {
303     Point3D->new( x => 10, y => 'Foo', z => 3 );
304 }
305 '... cannot assign a non-Int to y';
306
307 dies_ok {
308     Point3D->new( x => 'Foo', y => 10, z => 3 );
309 }
310 '... cannot assign a non-Int to x';
311
312 dies_ok {
313     Point3D->new( x => 0, y => 10, z => 'Bar' );
314 }
315 '... cannot assign a non-Int to z';
316
317 dies_ok {
318     Point3D->new( x => 10, y => 3 );
319 }
320 '... z is a required attribute for Point3D';
321
322 # test some class introspection
323
324 can_ok( 'Point', 'meta' );
325 isa_ok( Point->meta, 'Moose::Meta::Class' );
326
327 can_ok( 'Point3D', 'meta' );
328 isa_ok( Point3D->meta, 'Moose::Meta::Class' );
329
330 isnt( Point->meta, Point3D->meta,
331     '... they are different metaclasses as well' );
332
333 # poke at Point
334
335 is_deeply(
336     [ Point->meta->superclasses ],
337     ['Moose::Object'],
338     '... Point got the automagic base class'
339 );
340
341 my @Point_methods = qw(meta x y clear);
342 my @Point_attrs = ( 'x', 'y' );
343
344 is_deeply(
345     [ sort @Point_methods ],
346     [ sort Point->meta->get_method_list() ],
347     '... we match the method list for Point'
348 );
349
350 is_deeply(
351     [ sort @Point_attrs ],
352     [ sort Point->meta->get_attribute_list() ],
353     '... we match the attribute list for Point'
354 );
355
356 foreach my $method (@Point_methods) {
357     ok( Point->meta->has_method($method),
358         '... Point has the method "' . $method . '"' );
359 }
360
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' );
370 }
371
372 # poke at Point3D
373
374 is_deeply(
375     [ Point3D->meta->superclasses ],
376     ['Point'],
377     '... Point3D gets the parent given to it'
378 );
379
380 my @Point3D_methods = qw( meta z clear );
381 my @Point3D_attrs   = ('z');
382
383 is_deeply(
384     [ sort @Point3D_methods ],
385     [ sort Point3D->meta->get_method_list() ],
386     '... we match the method list for Point3D'
387 );
388
389 is_deeply(
390     [ sort @Point3D_attrs ],
391     [ sort Point3D->meta->get_attribute_list() ],
392     '... we match the attribute list for Point3D'
393 );
394
395 foreach my $method (@Point3D_methods) {
396     ok( Point3D->meta->has_method($method),
397         '... Point3D has the method "' . $method . '"' );
398 }
399
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' );
409 }
410
411 =end testing
412
413 =cut