Merged topic/metarole-distinguishes-role-meta (which includes topic/roles-have-real...
[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
2840a3b2 234Copyright 2006-2009 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
253dies_ok {
254 $point->y('Foo');
255}
256'... cannot assign a non-Int to y';
257
258dies_ok {
259 Point->new();
260}
261'... must provide required attributes to new';
262
263$point->clear();
264
265is( $point->x, 0, '... got the right (cleared) value for x' );
266is( $point->y, 0, '... got the right (cleared) value for y' );
267
268# check the type constraints on the constructor
269
270lives_ok {
271 Point->new( x => 0, y => 0 );
272}
273'... can assign a 0 to x and y';
274
275dies_ok {
276 Point->new( x => 10, y => 'Foo' );
277}
278'... cannot assign a non-Int to y';
279
280dies_ok {
281 Point->new( x => 'Foo', y => 10 );
282}
283'... cannot assign a non-Int to x';
284
285# Point3D
286
287my $point3d = Point3D->new( { x => 10, y => 15, z => 3 } );
288isa_ok( $point3d, 'Point3D' );
289isa_ok( $point3d, 'Point' );
290isa_ok( $point3d, 'Moose::Object' );
291
292is( $point3d->x, 10, '... got the right value for x' );
293is( $point3d->y, 15, '... got the right value for y' );
294is( $point3d->{'z'}, 3, '... got the right value for z' );
295
296$point3d->clear();
297
298is( $point3d->x, 0, '... got the right (cleared) value for x' );
299is( $point3d->y, 0, '... got the right (cleared) value for y' );
300is( $point3d->z, 0, '... got the right (cleared) value for z' );
301
302dies_ok {
303 Point3D->new( x => 10, y => 'Foo', z => 3 );
304}
305'... cannot assign a non-Int to y';
306
307dies_ok {
308 Point3D->new( x => 'Foo', y => 10, z => 3 );
309}
310'... cannot assign a non-Int to x';
311
312dies_ok {
313 Point3D->new( x => 0, y => 10, z => 'Bar' );
314}
315'... cannot assign a non-Int to z';
316
317dies_ok {
318 Point3D->new( x => 10, y => 3 );
319}
320'... z is a required attribute for Point3D';
321
322# test some class introspection
323
324can_ok( 'Point', 'meta' );
325isa_ok( Point->meta, 'Moose::Meta::Class' );
326
327can_ok( 'Point3D', 'meta' );
328isa_ok( Point3D->meta, 'Moose::Meta::Class' );
329
330isnt( Point->meta, Point3D->meta,
331 '... they are different metaclasses as well' );
332
333# poke at Point
334
335is_deeply(
336 [ Point->meta->superclasses ],
337 ['Moose::Object'],
338 '... Point got the automagic base class'
339);
340
341my @Point_methods = qw(meta x y clear);
342my @Point_attrs = ( 'x', 'y' );
343
344is_deeply(
345 [ sort @Point_methods ],
346 [ sort Point->meta->get_method_list() ],
347 '... we match the method list for Point'
348);
349
350is_deeply(
351 [ sort @Point_attrs ],
352 [ sort Point->meta->get_attribute_list() ],
353 '... we match the attribute list for Point'
354);
355
356foreach my $method (@Point_methods) {
357 ok( Point->meta->has_method($method),
358 '... Point has the method "' . $method . '"' );
359}
360
361foreach 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
374is_deeply(
375 [ Point3D->meta->superclasses ],
376 ['Point'],
377 '... Point3D gets the parent given to it'
378);
379
380my @Point3D_methods = qw( meta z clear );
381my @Point3D_attrs = ('z');
382
383is_deeply(
384 [ sort @Point3D_methods ],
385 [ sort Point3D->meta->get_method_list() ],
386 '... we match the method list for Point3D'
387);
388
389is_deeply(
390 [ sort @Point3D_attrs ],
391 [ sort Point3D->meta->get_attribute_list() ],
392 '... we match the attribute list for Point3D'
393);
394
395foreach my $method (@Point3D_methods) {
396 ok( Point3D->meta->has_method($method),
397 '... Point3D has the method "' . $method . '"' );
398}
399
400foreach 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
a34602e7 413=cut