6 Moose::Cookbook::Meta::Recipe3 - Labels implemented via attribute traits
10 package MyApp::Meta::Attribute::Trait::Labeled;
16 predicate => 'has_label',
19 package Moose::Meta::Attribute::Custom::Trait::Labeled;
20 sub register_implementation {'MyApp::Meta::Attribute::Trait::Labeled'}
22 package MyApp::Website;
26 traits => [qw/Labeled/],
29 label => "The site's URL",
40 my $meta = $self->meta;
44 for my $attribute ( map { $meta->get_attribute($_) }
45 sort $meta->get_attribute_list ) {
47 if ( $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
48 && $attribute->has_label ) {
49 $dump .= $attribute->label;
52 $dump .= $attribute->name;
55 my $reader = $attribute->get_read_method;
56 $dump .= ": " . $self->$reader . "\n";
64 my $app = MyApp::Website->new( url => "http://google.com", name => "Google" );
68 This recipe is a variation on
69 L<Moose::Cookbook::Meta::Recipe2>. Please read that recipe first.
73 In L<Moose::Cookbook::Meta::Recipe2>, we created an attribute
74 metaclass which lets you provide a label for attributes.
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.
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
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>.
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.
97 Yes, we still have lots of metaclasses in the background, but they're
98 managed by Moose for you.
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.
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
111 package MyApp::Meta::Attribute::Trait::Labeled;
117 predicate => 'has_label',
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.
124 package Moose::Meta::Attribute::Custom::Trait::Labeled;
125 sub register_implementation { 'MyApp::Meta::Attribute::Trait::Labeled' }
127 Moose looks for the C<register_implementation> method in
128 C<Moose::Meta::Attribute::Custom::Trait::$TRAIT_NAME> to find the full
131 For the rest of the code, we will only cover what is I<different> from
132 L<recipe 2|Moose::Cookbook::Meta::Recipe2>.
135 traits => [qw/Labeled/],
138 label => "The site's URL",
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
147 if ( $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
148 && $attribute->has_label ) {
149 $dump .= $attribute->label;
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.
157 That's all. Everything else is the same!
159 =head1 TURNING A METACLASS INTO A TRAIT
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."
164 Fortunately, you can easily turn a metaclass into a trait and still
165 provide the original metaclass:
167 package MyApp::Meta::Attribute::Labeled;
169 extends 'Moose::Meta::Attribute';
170 with 'MyApp::Meta::Attribute::Trait::Labeled';
172 package Moose::Meta::Attribute::Custom::Labeled;
173 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
175 Unfortunately, going the other way (providing a trait created from a
176 metaclass) is more tricky.
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>.
189 Shawn M Moore E<lt>sartak@gmail.comE<gt>
191 Dave Rolsky E<lt>autarch@urth.org<gt>
193 =head1 COPYRIGHT AND LICENSE
195 Copyright 2006-2010 by Infinity Interactive, Inc.
197 L<http://www.iinteractive.com>
199 This library is free software; you can redistribute it and/or modify
200 it under the same terms as Perl itself.
205 = MyApp::Website->new( url => "http://google.com", name => "Google" );
207 $app2->dump, q{name: Google
208 The site's URL: http://google.com
209 }, '... got the expected dump value'