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