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;
24 use MyApp::Meta::Attribute::Trait::Labeled;
27 traits => [qw/Labeled/],
30 label => "The site's URL",
41 # iterate over all the attributes in $self
42 my %attributes = %{ $self->meta->get_attribute_map };
43 while ( my ( $name, $attribute ) = each %attributes ) {
45 # print the label if available
46 if ( $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
47 && $attribute->has_label ) {
48 print $attribute->label;
51 # otherwise print the name
56 # print the attribute's value
57 my $reader = $attribute->get_read_method;
58 print ": " . $self->$reader . "\n";
63 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 # print the label if available
148 if ( $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
149 && $attribute->has_label ) {
150 print $attribute->label;
153 In the metaclass example, we used C<< $attribute->isa >>. With a role,
154 we instead ask if the meta-attribute object C<does> the required
155 role. If it does not do this role, the attribute meta object won't
156 have the C<has_label> method.
158 That's all. Everything else is the same!
160 =head1 TURNING A METACLASS INTO A TRAIT
162 "But wait!" you protest. "I've already written all of my extensions as
163 attribute metaclasses. I don't want to break all that code out there."
165 Fortunately, you can easily turn a metaclass into a trait and still
166 provide the original metaclass:
168 package MyApp::Meta::Attribute::Labeled;
170 extends 'Moose::Meta::Attribute';
171 with 'MyApp::Meta::Attribute::Trait::Labeled';
173 package Moose::Meta::Attribute::Custom::Labeled;
174 sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
176 Unfortunately, going the other way (providing a trait created from a
177 metaclass) is more tricky.
181 If you're extending your attributes, it's easier and more flexible to
182 provide composable bits of behavior than to subclass
183 L<Moose::Meta::Attribute>. Using traits lets you cooperate with other
184 extensions, either from CPAN or that you might write in the
185 future. Moose makes it easy to create attribute metaclasses on the fly
186 by providing a list of trait names to L<Moose/has>.
190 Shawn M Moore E<lt>sartak@gmail.comE<gt>
192 Dave Rolsky E<lt>autarch@urth.org<gt>
194 =head1 COPYRIGHT AND LICENSE
196 Copyright 2006-2009 by Infinity Interactive, Inc.
198 L<http://www.iinteractive.com>
200 This library is free software; you can redistribute it and/or modify
201 it under the same terms as Perl itself.