really update the manifest by re-running make manifest
[gitmo/Moose.git] / lib / Moose / Cookbook / Recipe22.pod
CommitLineData
aff0421c 1
2=pod
3
4=head1 NAME
5
6Moose::Cookbook::Recipe22 - The attribute trait example
7
8=head1 SYNOPSIS
9
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;
24 use MyApp::Meta::Attribute::Trait::Labeled;
25
26 has url => (
27 traits => [qw/Labeled/],
28 isa => 'Str',
29 is => 'rw',
30 label => "The site's URL",
31 );
32
33 has name => (
34 is => 'rw',
35 isa => 'Str',
36 );
37
38 sub dump {
39 my $self = shift;
40
41 # iterate over all the attributes in $self
42 my %attributes = %{ $self->meta->get_attribute_map };
43 while (my ($name, $attribute) = each %attributes) {
44
45 # print the label if available
46 if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
47 && $attribute->has_label) {
48 print $attribute->label;
49 }
50 # otherwise print the name
51 else {
52 print $name;
53 }
54
55 # print the attribute's value
56 my $reader = $attribute->get_read_method;
57 print ": " . $self->$reader . "\n";
58 }
59 }
60
61 package main;
62 my $app = MyApp::Website->new(url => "http://google.com", name => "Google");
63 $app->dump;
64
65=head1 BUT FIRST
66
67This recipe is a continuation of L<Moose::Cookbook::Recipe21>. Please read that
68first.
69
70=head1 MOTIVATION
71
72In Recipe 21, we created an attribute metaclass that gives attributes a "label"
73that can be set in L<Moose/has>. That works well until you want a second
74meta-attribute, or until you want to adjust the behavior of the attribute. You
75could have a specialized attribute metaclass for your application that does it
76all. However, you may want different attributes to have different behaviors. So
77you may end up with a unique attribute metaclass for B<every single attribute>,
78with a lot of code copying and pasting.
79
80Or, if you've been drinking deeply of the Moose kool-aid, you'll have a role
81for each of the behaviors. One role would give a label meta-attribute. Another
82role would signify that this attribute is not directly modifiable via the
83REST interface. Another role would write to a logfile when this attribute
84was read.
85
86Unfortunately, you'd still be left with a bunch of attribute metaclasses that
87do nothing but compose a bunch of roles. If only there were some way to specify
88in L<Moose/has> a list of roles to apply to the attribute metaclass...
89
90=head1 TRAITS
91
92Roles that apply to metaclasses have a special name: traits. Don't let the
93change in nomenclature fool you, B<traits are just roles>.
94
95L<Moose/has> provides a C<traits> option. It takes a list of trait names to
96compose into an anonymous metaclass. So you do still have a bunch of attribute
97metaclasses that do nothing but compose a bunch of roles, but they're managed
98automatically by Moose. You don't need to declare them in advance, or worry
99whether changing one will affect some other attribute.
100
101What can traits do? Anything roles can do. They can add or refine attributes,
102wrap methods, provide more methods, define an interface, etc. The only
103difference is that you're now changing the attribute metaclass instead of a
104user-level class.
105
106=head1 DISSECTION
107
108A side-by-side look of the code examples in this recipe and recipe 21 should
109indicate that defining and using a trait is very similar to defining and using
110a new attribute metaclass. Only a few lines have changed.
111
112 package MyApp::Meta::Attribute::Trait::Labeled;
113 use Moose::Role;
114
115 has label => (
116 is => 'rw',
117 isa => 'Str',
118 predicate => 'has_label',
119 );
120
121Instead of subclassing L<Moose::Meta::Attribute>, we define a role. Traits
122don't need any special methods or attributes. You just focus on whatever it is
123you actually need to get done. Here we're adding a new meta-attribute for use
124in our application.
125
126 package Moose::Meta::Attribute::Custom::Trait::Labeled;
127 sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
128
129Much like when we define a new attribute metaclass, we can provide a shortcut
130name for the trait. Moose looks at the C<register_implementation> method in
131C<Moose::Meta::Attribute::Custom::Trait::$TRAIT_NAME> to find the full
132name of the trait.
133
134Now we begin writing our application logic. I'll only cover what has changed
135between recipe 21 and this.
136
137 has url => (
138 traits => [qw/Labeled/],
139 isa => 'Str',
140 is => 'rw',
141 label => "The site's URL",
142 );
143
144L<Moose/has> provides a C<traits> option. Just pass the list of trait names and
145it will compose them together to form the (anonymous) attribute metaclass used
146by this attribute.
147
148 # print the label if available
149 if ($attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
150 && $attribute->has_label) {
151 print $attribute->label;
152 }
153
154Previously, this asked the question "Does this attribute use our attribute
155metaclass?" But since we're now using a trait, we ask "Does this attribute's
156metaclass do the C<Labeled> role?" If not, it won't have the C<has_label>
157method, and so it would be an error to blindly call
158C<< $attribute->has_label >>.
159
160That's all. Everything else is the same!
161
162=head1 CONCLUSION
163
164If you're extending your attributes, it's easier and more flexible to provide
165composable bits of behavior, rather than subclassing L<Moose::Meta::Attribute>.
166Using traits (which are just roles applied to a metaclass!) let you choose
167exactly which behaviors each attribute will have. Moose makes it easy to create
168attribute metaclasses on the fly by providing a list of trait names to
169L<Moose/has>.
170
171=head1 AUTHOR
172
173Shawn M Moore E<lt>sartak@gmail.comE<gt>
174
175=head1 COPYRIGHT AND LICENSE
176
177Copyright 2006-2008 by Infinity Interactive, Inc.
178
179L<http://www.iinteractive.com>
180
181This library is free software; you can redistribute it and/or modify
182it under the same terms as Perl itself.
183
184=cut
185
186