Remove alias_method (it's been deprecated since 0.73_01)
[gitmo/Moose.git] / lib / Moose / Cookbook / Meta / Recipe3.pod
CommitLineData
aff0421c 1
2=pod
3
4=head1 NAME
5
43aa5bf9 6Moose::Cookbook::Meta::Recipe3 - Labels implemented via attribute traits
aff0421c 7
8=head1 SYNOPSIS
9
6a7e3999 10 package MyApp::Meta::Attribute::Trait::Labeled;
11 use Moose::Role;
12
13 has label => (
14 is => 'rw',
15 isa => 'Str',
16 predicate => 'has_label',
17 );
18
19 package Moose::Meta::Attribute::Custom::Trait::Labeled;
20 sub register_implementation {'MyApp::Meta::Attribute::Trait::Labeled'}
21
22 package MyApp::Website;
23 use Moose;
6a7e3999 24
25 has url => (
26 traits => [qw/Labeled/],
27 is => 'rw',
28 isa => 'Str',
29 label => "The site's URL",
30 );
31
32 has name => (
33 is => 'rw',
34 isa => 'Str',
35 );
36
37 sub dump {
38 my $self = shift;
39
ce444596 40 my $meta = $self->meta;
41
c79239a2 42 my $dump = '';
43
ce444596 44 for my $attribute ( map { $meta->get_attribute($_) }
45 sort $meta->get_attribute_list ) {
6a7e3999 46
6a7e3999 47 if ( $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
48 && $attribute->has_label ) {
c79239a2 49 $dump .= $attribute->label;
6a7e3999 50 }
6a7e3999 51 else {
ce444596 52 $dump .= $attribute->name;
6a7e3999 53 }
54
6a7e3999 55 my $reader = $attribute->get_read_method;
c79239a2 56 $dump .= ": " . $self->$reader . "\n";
6a7e3999 57 }
c79239a2 58
59 return $dump;
6a7e3999 60 }
61
62 package main;
c79239a2 63
6a7e3999 64 my $app = MyApp::Website->new( url => "http://google.com", name => "Google" );
aff0421c 65
66=head1 BUT FIRST
67
4515e88e 68This recipe is a variation on
09412052 69L<Moose::Cookbook::Meta::Recipe2>. Please read that recipe first.
aff0421c 70
71=head1 MOTIVATION
72
09412052 73In L<Moose::Cookbook::Meta::Recipe2>, we created an attribute
4515e88e 74metaclass which lets you provide a label for attributes.
75
19320607 76Using a metaclass works fine until you realize you want to add a label
4515e88e 77I<and> an expiration, or some other combination of new behaviors. You
78could create yet another metaclass which subclasses those two, but
79that makes a mess, especially if you want to mix and match behaviors
80across many attributes.
81
82Fortunately, Moose provides a much saner alternative, which is to
83encapsulate each extension as a role, not a class. We can make a role
84which adds a label to an attribute, and could make another to
85implement expiration.
aff0421c 86
87=head1 TRAITS
88
4515e88e 89Roles that apply to metaclasses have a special name: traits. Don't let
90the change in nomenclature fool you, B<traits are just roles>.
aff0421c 91
4515e88e 92L<Moose/has> allows you to pass a C<traits> parameter for an
93attribute. This parameter takes a list of trait names which are
94composed into an anonymous metaclass, and that anonymous metaclass is
95used for the attribute.
aff0421c 96
4515e88e 97Yes, we still have lots of metaclasses in the background, but they're
98managed by Moose for you.
99
100Traits can do anything roles can do. They can add or refine
101attributes, wrap methods, provide more methods, define an interface,
102etc. The only difference is that you're now changing the attribute
103metaclass instead of a user-level class.
aff0421c 104
105=head1 DISSECTION
106
4515e88e 107A side-by-side look of the code examples in this recipe and recipe 2
108show that defining and using a trait is very similar to a full-blown
109metaclass.
aff0421c 110
6a7e3999 111 package MyApp::Meta::Attribute::Trait::Labeled;
112 use Moose::Role;
aff0421c 113
6a7e3999 114 has label => (
115 is => 'rw',
116 isa => 'Str',
117 predicate => 'has_label',
118 );
aff0421c 119
4515e88e 120Instead of subclassing L<Moose::Meta::Attribute>, we define a role. As
121with our metaclass in L<recipe 2|Moose::Cookbook::Meta::Recipe2>,
122registering our role allows us to refer to it by a short name.
aff0421c 123
6a7e3999 124 package Moose::Meta::Attribute::Custom::Trait::Labeled;
125 sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
aff0421c 126
4515e88e 127Moose looks for the C<register_implementation> method in
aff0421c 128C<Moose::Meta::Attribute::Custom::Trait::$TRAIT_NAME> to find the full
129name of the trait.
130
4515e88e 131For the rest of the code, we will only cover what is I<different> from
132L<recipe 2|Moose::Cookbook::Meta::Recipe2>.
aff0421c 133
6a7e3999 134 has url => (
135 traits => [qw/Labeled/],
136 is => 'rw',
137 isa => 'Str',
138 label => "The site's URL",
139 );
aff0421c 140
4515e88e 141Instead of passing a C<metaclass> parameter, this time we pass
142C<traits>. This contains a list of trait names. Moose will build an
143anonymous attribute metaclass from these traits and use it for this
144attribute. Passing a C<label> parameter works just as it did with the
145metaclass example.
aff0421c 146
c79239a2 147 if ( $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
148 && $attribute->has_label ) {
149 $dump .= $attribute->label;
150 }
aff0421c 151
4515e88e 152In the metaclass example, we used C<< $attribute->isa >>. With a role,
153we instead ask if the meta-attribute object C<does> the required
154role. If it does not do this role, the attribute meta object won't
155have the C<has_label> method.
aff0421c 156
157That's all. Everything else is the same!
158
4515e88e 159=head1 TURNING A METACLASS INTO A TRAIT
d9a8643f 160
161"But wait!" you protest. "I've already written all of my extensions as
162attribute metaclasses. I don't want to break all that code out there."
163
4515e88e 164Fortunately, you can easily turn a metaclass into a trait and still
165provide the original metaclass:
d9a8643f 166
6a7e3999 167 package MyApp::Meta::Attribute::Labeled;
168 use Moose;
169 extends 'Moose::Meta::Attribute';
170 with 'MyApp::Meta::Attribute::Trait::Labeled';
d9a8643f 171
6a7e3999 172 package Moose::Meta::Attribute::Custom::Labeled;
173 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
d9a8643f 174
4515e88e 175Unfortunately, going the other way (providing a trait created from a
176metaclass) is more tricky.
d9a8643f 177
aff0421c 178=head1 CONCLUSION
179
4515e88e 180If you're extending your attributes, it's easier and more flexible to
181provide composable bits of behavior than to subclass
182L<Moose::Meta::Attribute>. Using traits lets you cooperate with other
183extensions, either from CPAN or that you might write in the
184future. Moose makes it easy to create attribute metaclasses on the fly
185by providing a list of trait names to L<Moose/has>.
aff0421c 186
187=head1 AUTHOR
188
189Shawn M Moore E<lt>sartak@gmail.comE<gt>
190
4515e88e 191Dave Rolsky E<lt>autarch@urth.org<gt>
192
aff0421c 193=head1 COPYRIGHT AND LICENSE
194
7e0492d3 195Copyright 2006-2010 by Infinity Interactive, Inc.
aff0421c 196
197L<http://www.iinteractive.com>
198
199This library is free software; you can redistribute it and/or modify
200it under the same terms as Perl itself.
201
c79239a2 202=begin testing
aff0421c 203
c79239a2 204my $app2
205 = MyApp::Website->new( url => "http://google.com", name => "Google" );
206is(
207 $app2->dump, q{name: Google
208The site's URL: http://google.com
209}, '... got the expected dump value'
210);
aff0421c 211
c79239a2 212
213=end testing
214
215=cut