File: Recipe1.pod

package info (click to toggle)
libmoose-perl 0.54-1
  • links: PTS, VCS
  • area: main
  • in suites: lenny
  • size: 1,496 kB
  • ctags: 448
  • sloc: perl: 15,125; makefile: 10
file content (237 lines) | stat: -rw-r--r-- 7,723 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

=pod

=head1 NAME

Moose::Cookbook::Recipe1 - The (always classic) B<Point> example.

=head1 SYNOPSIS

  package Point;
  use Moose;
  	
  has 'x' => (isa => 'Int', is => 'ro');
  has 'y' => (isa => 'Int', is => 'rw');
  
  sub clear {
      my $self = shift;
      $self->{x} = 0;
      $self->y(0);    
  }
  
  package Point3D;
  use Moose;
  
  extends 'Point';
  
  has 'z' => (isa => 'Int');
  
  after 'clear' => sub {
      my $self = shift;
      $self->{z} = 0;
  };

=head1 DESCRIPTION

This is the classic Point example. This one in particular I took 
from the Perl 6 Apocalypse 12 document, but it is similar to the 
example found in the classic K&R C book as well, and many other 
places. And now, onto the code:

As with all Perl 5 classes, a Moose class is defined in a package. 
Moose now handles turning on C<strict> and C<warnings> for you, so 
all you need to do is say C<use Moose>, and no kittens will die.

By loading Moose, we are enabling the loading of the Moose
"environment" into our package. This means that we import some
functions which serve as Moose "keywords". These aren't anything
fancy, just plain old exported functions.

Another important thing happens at this stage as well. Moose will 
automatically set your package's superclass to be L<Moose::Object>.
The reason we do this, is so that we can be sure that your class
will inherit from L<Moose::Object> and get the benefits that
provides (such as a constructor; see L<Moose::Object> for details).
However, you don't actually I<have> to inherit from L<Moose::Object>
if you don't want to. All Moose features will still be accessible to
you.

Now, onto the keywords. The first one we see here is C<has>, which 
defines an instance attribute in your class:

  has 'x' => (isa => 'Int', is => 'ro');

This will create an attribute named C<x>, which will expect the 
value stored in the attribute to pass the type constraint C<Int> (1), 
and the accessor generated for this attribute will be read-only 
(abbreviated as C<ro>).

The next C<has> line is very similar, with only one difference:

  has 'y' => (isa => 'Int', is => 'rw');

A read/write (abbreviated as C<rw>) accessor will be generated for
the C<y> attribute.

At this point the attributes have been defined, and it is time to 
define our methods. In Moose, as with regular Perl 5 OO, a method 
is just a subroutine defined within the package. So here we create 
the C<clear> method.

  sub clear {
      my $self = shift;
      $self->{x} = 0;
      $self->y(0);    
  }

It is pretty standard, the only thing to note is that we are directly 
accessing the C<x> slot in the instance L<(2)>. This is because the 
value was created with a read-only accessor. This also shows that Moose 
objects are not anything out of the ordinary, but just regular old 
blessed HASH references. This means they are very compatible with 
other Perl 5 (non-Moose) classes as well. 

The next part of the code to review is the B<Point> subclass, 
B<Point3D>. The first item you might notice is that we do not use 
the standard C<use base> declaration here. Instead we use the Moose 
keyword C<extends> like so:

  extends 'Point';

This keyword will function very much like C<use base> does in that 
it will make an attempt to load your class if it has not already been 
loaded. However, it differs on one important point. The C<extends> 
keyword will overwrite any previous values in your package's C<@ISA>, 
where C<use base> will C<push> values onto the package's C<@ISA>. It 
is my opinion that the behavior of C<extends> is more intuitive in 
that it is more explicit about defining the superclass relationship.

A small digression here: both Moose and C<extends> support multiple 
inheritance. You simply pass all the superclasses to C<extends>, 
like so:

  extends 'Foo', 'Bar', 'Baz';
  
Now, back to our B<Point3D> class. The next thing we do is to create 
a new attribute for B<Point3D> called C<z>.

  has 'z' => (isa => 'Int');

As with B<Point>'s C<x> and C<y> attributes, this attribute has a 
type constraint of C<Int>, but it differs in that it does B<not> 
ask for any autogenerated accessors. The result being (aside from 
broken object encapsulation) that C<z> is a private attribute.

Next comes another Moose feature which we call method "modifiers" 
(or method "advice" for the AOP inclined). The modifier used here 
is the C<after> modifier, and looks like this:

  after 'clear' => sub {
      my $self = shift;
      $self->{z} = 0;
  };

This modifier tells Moose to install a C<clear> method for 
B<Point3D> that will first run the C<clear> method for the 
superclass (in this case C<Point::clear>), and then run this 
method I<after> it (passing in the same arguments as the original 
method). 

Now, of course using the C<after> modifier is not the only way to 
accomplish this. I mean, after all, this B<is> Perl right? You 
would get the same results with this code:

  sub clear {
      my $self = shift;
      $self->SUPER::clear();
      $self->{z} = 0;
  }

You could also use another Moose method modifier, C<override> here, 
and get the same results again. Here is how that would look:

  override 'clear' => sub {
      my $self = shift;
      super();
      $self->{z} = 0;
  };
  
The C<override> modifier allows you to use the C<super> keyword 
within it to dispatch to the superclass's method in a very Ruby-ish 
style.

Now, of course, what use is a class if you can't instantiate objects 
with it? Since B<Point> inherits from L<Moose::Object>, it will also
inherit the default L<Moose::Object> constructor: C<new>. Here 
are two examples of how that is used:

  my $point = Point->new(x => 1, y => 2);   
  my $point3d = Point3D->new(x => 1, y => 2, z => 3);

As you can see, C<new> accepts named argument pairs for any of the 
attributes. It does not I<require> that you pass in the all the 
attributes, and it will politely ignore any named arguments it does 
not recognize. 

From here on, you can use C<$point> and C<$point3d> just as you would 
any other Perl 5 object. For a more detailed example of what can be 
done, you can refer to the F<t/000_recipes/001_recipe.t> test file.

=head1 CONCLUSION

I hope this recipe has given you some explanation of how to use 
Moose to build your Perl 5 classes. The next recipe will build upon 
the basics shown here with more complex attributes and methods. 
Please read on :)

=head1 FOOTNOTES

=over 4

=item (1)

Several default type constraints are provided by Moose, of which 
C<Int> is one. For more information on the builtin type constraints 
and the type constraint system in general, see the 
L<Moose::Util::TypeConstraints> documentation.

=item (2)

Moose supports using instance structures other than blessed hash
references (such as in a glob reference -- see
L<MooseX::GlobRef::Object>). If you want your Moose classes to
be interchangeable, it is advisable to avoid direct instance
access, like that shown above. Moose does let you get and set
attributes directly without exposing the instance structure, but
that's an advanced topic (intrepid readers should refer to the
L<Moose::Meta::Attribute documentation>).

=back

=head1 SEE ALSO

=over 4

=item Method Modifiers

The concept of method modifiers is directly ripped off from CLOS. A 
great explanation of them can be found by following this link.

L<http://www.gigamonkeys.com/book/object-reorientation-generic-functions.html>

=back

=head1 AUTHOR

Stevan Little E<lt>stevan@iinteractive.comE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright 2006-2008 by Infinity Interactive, Inc.

L<http://www.iinteractive.com>

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut