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