version bump for 1.9903
[gitmo/Moose.git] / lib / Moose / Cookbook / Basics / Recipe1.pod
CommitLineData
daa0fd7d 1package Moose::Cookbook::Basics::Recipe1;
471c4f09 2
daa0fd7d 3# ABSTRACT: The (always classic) B<Point> example.
4
5__END__
471c4f09 6
471c4f09 7
daa0fd7d 8=pod
471c4f09 9
10=head1 SYNOPSIS
11
12 package Point;
471c4f09 13 use Moose;
a34602e7 14
15 has 'x' => (isa => 'Int', is => 'rw', required => 1);
16 has 'y' => (isa => 'Int', is => 'rw', required => 1);
17
471c4f09 18 sub clear {
19 my $self = shift;
a34602e7 20 $self->x(0);
21 $self->y(0);
471c4f09 22 }
a34602e7 23
471c4f09 24 package Point3D;
471c4f09 25 use Moose;
a34602e7 26
471c4f09 27 extends 'Point';
a34602e7 28
29 has 'z' => (isa => 'Int', is => 'rw', required => 1);
30
471c4f09 31 after 'clear' => sub {
32 my $self = shift;
a34602e7 33 $self->z(0);
471c4f09 34 };
35
c79239a2 36 package main;
d68bf053 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
471c4f09 44=head1 DESCRIPTION
45
a34602e7 46This is the classic Point example. It is taken directly from the Perl
476 Apocalypse 12 document, and is similar to the example found in the
48classic K&R C book as well.
cdcae970 49
a34602e7 50As with all Perl 5 classes, a Moose class is defined in a package.
51Moose handles turning on C<strict> and C<warnings> for us, so all we
52need to do is say C<use Moose>, and no kittens will die.
cdcae970 53
a34602e7 54When Moose is loaded, it exports a set of sugar functions into our
55package. This means that we import some functions which serve as Moose
56"keywords". These aren't real language keywords, they're just Perl
57functions exported into our package.
cdcae970 58
a34602e7 59Moose automatically makes our package a subclass of L<Moose::Object>.
60The L<Moose::Object> class provides us with a constructor that
61respects our attributes, as well other features. See L<Moose::Object>
62for details.
cdcae970 63
a34602e7 64Now, onto the keywords. The first one we see here is C<has>, which
65defines an instance attribute in our class:
cdcae970 66
a34602e7 67 has 'x' => (isa => 'Int', is => 'rw', required => 1);
cdcae970 68
a34602e7 69This will create an attribute named C<x>. The C<isa> parameter says
70that we expect the value stored in this attribute to pass the type
71constraint for C<Int> (1). The accessor generated for this attribute
72will be read-write.
cdcae970 73
a34602e7 74The C<< requires => 1 >> parameter means that this attribute must be
75provided when a new object is created. A point object without
76coordinates doesn't make much sense, so we don't allow it.
cdcae970 77
a34602e7 78We have defined our attributes; next we define our methods. In Moose,
79as with regular Perl 5 OO, a method is just a subroutine defined
80within the package:
cdcae970 81
82 sub clear {
83 my $self = shift;
a34602e7 84 $self->x(0);
85 $self->y(0);
cdcae970 86 }
87
a34602e7 88That concludes the B<Point> class.
cdcae970 89
a34602e7 90Next we have a subclass of B<Point>, B<Point3D>. To declare our
91superclass, we use the Moose keyword C<extends>:
cdcae970 92
93 extends 'Point';
94
a34602e7 95The C<extends> keyword works much like C<use base>. First, it will
96attempt to load your class if needed. However, unlike C<base>, the
97C<extends> keyword will I<overwrite> any previous values in your
98package's C<@ISA>, where C<use base> will C<push> values onto the
99package's C<@ISA>.
cdcae970 100
a34602e7 101It is my opinion that the behavior of C<extends> is more intuitive.
102(2).
cdcae970 103
a34602e7 104Next we create a new attribute for B<Point3D> called C<z>.
cdcae970 105
a34602e7 106 has 'z' => (isa => 'Int', is => 'rw', required => 1);
cdcae970 107
a34602e7 108This attribute is just like B<Point>'s C<x> and C<y> attributes.
cdcae970 109
a34602e7 110The C<after> keyword demonstrates a Moose feature called "method
111modifiers" (or "advice" for the AOP inclined):
cdcae970 112
113 after 'clear' => sub {
114 my $self = shift;
a34602e7 115 $self->z(0);
cdcae970 116 };
117
a34602e7 118When C<clear> is called on a B<Point3D> object, our modifier method
119gets called as well. Unsurprisingly, the modifier is called I<after>
120the real method.
121
122In this case, the real C<clear> method is inherited from B<Point>. Our
123modifier method receives the same arguments as those passed to the
124modified method (just C<$self> here).
cdcae970 125
a34602e7 126Of course, using the C<after> modifier is not the only way to
127accomplish this. This B<is> Perl, right? You can get the same results
128with this code:
cdcae970 129
130 sub clear {
131 my $self = shift;
132 $self->SUPER::clear();
a34602e7 133 $self->z(0);
cdcae970 134 }
135
a34602e7 136You could also use another Moose method modifier, C<override>:
cdcae970 137
138 override 'clear' => sub {
139 my $self = shift;
140 super();
a34602e7 141 $self->z(0);
cdcae970 142 };
cdcae970 143
a34602e7 144The C<override> modifier allows you to use the C<super> keyword to
145dispatch to the superclass's method in a very Ruby-ish style.
cdcae970 146
a34602e7 147The choice of whether to use a method modifier, and which one to use,
148is often a question of style as much as functionality.
149
150Since B<Point> inherits from L<Moose::Object>, it will also inherit
151the default L<Moose::Object> constructor:
152
d68bf053 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);
cdcae970 157
a34602e7 158The C<new> constructor accepts a named argument pair for each
d68bf053 159attribute defined by the class, which you can provide as a hash or
160hash reference. In this particular example, the attributes are
161required, and calling C<new> without them will throw an error.
162
163 my $point = Point->new( x => 5 ); # no y, kaboom!
a34602e7 164
165From here on, we can use C<$point> and C<$point3d> just as you would
166any other Perl 5 object. For a more detailed example of what can be
c79239a2 167done, you can refer to the
168F<t/000_recipes/moose_cookbook_basics_recipe1.t> test file.
a34602e7 169
170=head2 Moose Objects are Just Hashrefs
171
172While this all may appear rather magical, it's important to realize
173that Moose objects are just hash references under the hood (3). For
174example, you could pass C<$self> to C<Data::Dumper> and you'd get
175exactly what you'd expect.
cdcae970 176
a34602e7 177You could even poke around inside the object's data structure, but
178that is strongly discouraged.
179
180The fact that Moose objects are hashrefs means it is easy to use Moose
181to extend non-Moose classes, as long as they too are hash
182references. If you want to extend a non-hashref class, check out
183C<MooseX::InsideOut>.
cdcae970 184
185=head1 CONCLUSION
186
ec6df2e6 187This recipe demonstrates some basic Moose concepts, attributes,
188subclassing, and a simple method modifier.
cdcae970 189
190=head1 FOOTNOTES
191
192=over 4
193
194=item (1)
195
a34602e7 196Moose provides a number of builtin type constraints are provided by,
197of which C<Int> is one. For more information on the type constraint
198system, see L<Moose::Util::TypeConstraints>.
cdcae970 199
200=item (2)
201
a34602e7 202The C<extends> keyword support multiple inheritance. Simply pass all
203of your superclasses to C<extends> as a list:
204
205 extends 'Foo', 'Bar', 'Baz';
206
207=item (3)
208
1eca36fc 209Moose supports using instance structures other than blessed hash
a34602e7 210references (such as in a glob reference - see
211L<MooseX::GlobRef::Object>).
cdcae970 212
213=back
214
215=head1 SEE ALSO
216
217=over 4
218
219=item Method Modifiers
220
d03bd989 221The concept of method modifiers is directly ripped off from CLOS. A
4711f5f7 222great explanation of them can be found by following this link.
cdcae970 223
224L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>
225
226=back
227
c79239a2 228=begin testing
229
230my $point = Point->new( x => 1, y => 2 );
231isa_ok( $point, 'Point' );
232isa_ok( $point, 'Moose::Object' );
233
234is( $point->x, 1, '... got the right value for x' );
235is( $point->y, 2, '... got the right value for y' );
236
237$point->y(10);
238is( $point->y, 10, '... got the right (changed) value for y' );
239
b10dde3a 240isnt(
241 exception {
242 $point->y('Foo');
243 },
244 undef,
245 '... cannot assign a non-Int to y'
246);
c79239a2 247
b10dde3a 248isnt(
249 exception {
250 Point->new();
251 },
252 undef,
253 '... must provide required attributes to new'
254);
c79239a2 255
256$point->clear();
257
258is( $point->x, 0, '... got the right (cleared) value for x' );
259is( $point->y, 0, '... got the right (cleared) value for y' );
260
261# check the type constraints on the constructor
262
b10dde3a 263is(
264 exception {
265 Point->new( x => 0, y => 0 );
266 },
267 undef,
268 '... can assign a 0 to x and y'
269);
c79239a2 270
b10dde3a 271isnt(
272 exception {
273 Point->new( x => 10, y => 'Foo' );
274 },
275 undef,
276 '... cannot assign a non-Int to y'
277);
c79239a2 278
b10dde3a 279isnt(
280 exception {
281 Point->new( x => 'Foo', y => 10 );
282 },
283 undef,
284 '... cannot assign a non-Int to x'
285);
c79239a2 286
287# Point3D
288
289my $point3d = Point3D->new( { x => 10, y => 15, z => 3 } );
290isa_ok( $point3d, 'Point3D' );
291isa_ok( $point3d, 'Point' );
292isa_ok( $point3d, 'Moose::Object' );
293
294is( $point3d->x, 10, '... got the right value for x' );
295is( $point3d->y, 15, '... got the right value for y' );
296is( $point3d->{'z'}, 3, '... got the right value for z' );
297
298$point3d->clear();
299
300is( $point3d->x, 0, '... got the right (cleared) value for x' );
301is( $point3d->y, 0, '... got the right (cleared) value for y' );
302is( $point3d->z, 0, '... got the right (cleared) value for z' );
303
b10dde3a 304isnt(
305 exception {
306 Point3D->new( x => 10, y => 'Foo', z => 3 );
307 },
308 undef,
309 '... cannot assign a non-Int to y'
310);
c79239a2 311
b10dde3a 312isnt(
313 exception {
314 Point3D->new( x => 'Foo', y => 10, z => 3 );
315 },
316 undef,
317 '... cannot assign a non-Int to x'
318);
c79239a2 319
b10dde3a 320isnt(
321 exception {
322 Point3D->new( x => 0, y => 10, z => 'Bar' );
323 },
324 undef,
325 '... cannot assign a non-Int to z'
326);
c79239a2 327
b10dde3a 328isnt(
329 exception {
330 Point3D->new( x => 10, y => 3 );
331 },
332 undef,
333 '... z is a required attribute for Point3D'
334);
c79239a2 335
336# test some class introspection
337
338can_ok( 'Point', 'meta' );
339isa_ok( Point->meta, 'Moose::Meta::Class' );
340
341can_ok( 'Point3D', 'meta' );
342isa_ok( Point3D->meta, 'Moose::Meta::Class' );
343
b10dde3a 344isnt(
345 Point->meta, Point3D->meta,
346 '... they are different metaclasses as well'
347);
c79239a2 348
349# poke at Point
350
351is_deeply(
352 [ Point->meta->superclasses ],
353 ['Moose::Object'],
354 '... Point got the automagic base class'
355);
356
357my @Point_methods = qw(meta x y clear);
358my @Point_attrs = ( 'x', 'y' );
359
360is_deeply(
361 [ sort @Point_methods ],
362 [ sort Point->meta->get_method_list() ],
363 '... we match the method list for Point'
364);
365
366is_deeply(
367 [ sort @Point_attrs ],
368 [ sort Point->meta->get_attribute_list() ],
369 '... we match the attribute list for Point'
370);
371
372foreach my $method (@Point_methods) {
373 ok( Point->meta->has_method($method),
374 '... Point has the method "' . $method . '"' );
375}
376
377foreach 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
390is_deeply(
391 [ Point3D->meta->superclasses ],
392 ['Point'],
393 '... Point3D gets the parent given to it'
394);
395
396my @Point3D_methods = qw( meta z clear );
397my @Point3D_attrs = ('z');
398
399is_deeply(
400 [ sort @Point3D_methods ],
401 [ sort Point3D->meta->get_method_list() ],
402 '... we match the method list for Point3D'
403);
404
405is_deeply(
406 [ sort @Point3D_attrs ],
407 [ sort Point3D->meta->get_attribute_list() ],
408 '... we match the attribute list for Point3D'
409);
410
411foreach my $method (@Point3D_methods) {
412 ok( Point3D->meta->has_method($method),
413 '... Point3D has the method "' . $method . '"' );
414}
415
416foreach 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
a34602e7 429=cut