1Moose::Cookbook::Meta::ULsaebrelCeodn_tMArotiotbsrueit:be:udCtoePoTekrrbaloiotDk(o:3c:)uMmeetnat:a:tLiaobneled_AttributeTrait(3)
2
3
4

NAME

6       Moose::Cookbook::Meta::Labeled_AttributeTrait - Labels implemented via
7       attribute traits
8

VERSION

10       version 2.2015
11

SYNOPSIS

13         package MyApp::Meta::Attribute::Trait::Labeled;
14         use Moose::Role;
15         Moose::Util::meta_attribute_alias('Labeled');
16
17         has label => (
18             is        => 'rw',
19             isa       => 'Str',
20             predicate => 'has_label',
21         );
22
23         package MyApp::Website;
24         use Moose;
25
26         has url => (
27             traits => [qw/Labeled/],
28             is     => 'rw',
29             isa    => 'Str',
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             my $meta = $self->meta;
42
43             my $dump = '';
44
45             for my $attribute ( map { $meta->get_attribute($_) }
46                 sort $meta->get_attribute_list ) {
47
48                 if (   $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
49                     && $attribute->has_label ) {
50                     $dump .= $attribute->label;
51                 }
52                 else {
53                     $dump .= $attribute->name;
54                 }
55
56                 my $reader = $attribute->get_read_method;
57                 $dump .= ": " . $self->$reader . "\n";
58             }
59
60             return $dump;
61         }
62
63         package main;
64
65         my $app = MyApp::Website->new( url => "http://google.com", name => "Google" );
66

SUMMARY

68       In this recipe, we begin to delve into the wonder of meta-programming.
69       Some readers may scoff and claim that this is the arena of only the
70       most twisted Moose developers. Absolutely not! Any sufficiently twisted
71       developer can benefit greatly from going more meta.
72
73       Our goal is to allow each attribute to have a human-readable "label"
74       attached to it. Such labels would be used when showing data to an end
75       user. In this recipe we label the "url" attribute with "The site's URL"
76       and create a simple method showing how to use that label.
77

META-ATTRIBUTE OBJECTS

79       All the attributes of a Moose-based object are actually objects
80       themselves.  These objects have methods and attributes. Let's look at a
81       concrete example.
82
83         has 'x' => ( isa => 'Int', is => 'ro' );
84         has 'y' => ( isa => 'Int', is => 'rw' );
85
86       Internally, the metaclass for "Point" has two Moose::Meta::Attribute
87       objects. There are several methods for getting meta-attributes out of a
88       metaclass, one of which is "get_attribute_list". This method is called
89       on the metaclass object.
90
91       The "get_attribute_list" method returns a list of attribute names. You
92       can then use "get_attribute" to get the Moose::Meta::Attribute object
93       itself.
94
95       Once you have this meta-attribute object, you can call methods on it
96       like this:
97
98         print $point->meta->get_attribute('x')->type_constraint;
99            => Int
100
101       To add a label to our attributes there are two steps. First, we need a
102       new attribute metaclass trait that can store a label for an attribute.
103       Second, we need to apply that trait to our attributes.
104

TRAITS

106       Roles that apply to metaclasses have a special name: traits. Don't let
107       the change in nomenclature fool you, traits are just roles.
108
109       "has" in Moose allows you to pass a "traits" parameter for an
110       attribute. This parameter takes a list of trait names which are
111       composed into an anonymous metaclass, and that anonymous metaclass is
112       used for the attribute.
113
114       Yes, we still have lots of metaclasses in the background, but they're
115       managed by Moose for you.
116
117       Traits can do anything roles can do. They can add or refine attributes,
118       wrap methods, provide more methods, define an interface, etc. The only
119       difference is that you're now changing the attribute metaclass instead
120       of a user-level class.
121

DISSECTION

123       We start by creating a package for our trait.
124
125         package MyApp::Meta::Attribute::Trait::Labeled;
126         use Moose::Role;
127
128         has label => (
129             is        => 'rw',
130             isa       => 'Str',
131             predicate => 'has_label',
132         );
133
134       You can see that a trait is just a Moose::Role. In this case, our role
135       contains a single attribute, "label". Any attribute which does this
136       trait will now have a label.
137
138       We also register our trait with Moose:
139
140         Moose::Util::meta_attribute_alias('Labeled');
141
142       This allows Moose to find our trait by the short name "Labeled" when
143       passed to the "traits" attribute option, rather than requiring the full
144       package name to be specified.
145
146       Finally, we pass our trait when defining an attribute:
147
148         has url => (
149             traits => [qw/Labeled/],
150             is     => 'rw',
151             isa    => 'Str',
152             label  => "The site's URL",
153         );
154
155       The "traits" parameter contains a list of trait names. Moose will build
156       an anonymous attribute metaclass from these traits and use it for this
157       attribute.
158
159       The reason that we can pass the name "Labeled", instead of
160       "MyApp::Meta::Attribute::Trait::Labeled", is because of the
161       "register_implementation" code we touched on previously.
162
163       When you pass a metaclass to "has", it will take the name you provide
164       and prefix it with "Moose::Meta::Attribute::Custom::Trait::". Then it
165       calls "register_implementation" in the package. In this case, that
166       means Moose ends up calling
167       "Moose::Meta::Attribute::Custom::Trait::Labeled::register_implementation".
168
169       If this function exists, it should return the real trait's package
170       name. This is exactly what our code does, returning
171       "MyApp::Meta::Attribute::Trait::Labeled". This is a little convoluted,
172       and if you don't like it, you can always use the fully-qualified name.
173
174       We can access this meta-attribute and its label like this:
175
176         $website->meta->get_attribute('url')->label()
177
178         MyApp::Website->meta->get_attribute('url')->label()
179
180       We also have a regular attribute, "name":
181
182         has name => (
183             is  => 'rw',
184             isa => 'Str',
185         );
186
187       Finally, we have a "dump" method, which creates a human-readable
188       representation of a "MyApp::Website" object. It will use an attribute's
189       label if it has one.
190
191         sub dump {
192             my $self = shift;
193
194             my $meta = $self->meta;
195
196             my $dump = '';
197
198             for my $attribute ( map { $meta->get_attribute($_) }
199                 sort $meta->get_attribute_list ) {
200
201                 if (   $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
202                     && $attribute->has_label ) {
203                     $dump .= $attribute->label;
204                 }
205
206       This is a bit of defensive code. We cannot depend on every meta-
207       attribute having a label. Even if we define one for every attribute in
208       our class, a subclass may neglect to do so. Or a superclass could add
209       an attribute without a label.
210
211       We also check that the attribute has a label using the predicate we
212       defined. We could instead make the label "required". If we have a
213       label, we use it, otherwise we use the attribute name:
214
215                 else {
216                     $dump .= $attribute->name;
217                 }
218
219                 my $reader = $attribute->get_read_method;
220                 $dump .= ": " . $self->$reader . "\n";
221             }
222
223             return $dump;
224         }
225
226       The "get_read_method" is part of the Moose::Meta::Attribute API. It
227       returns the name of a method that can read the attribute's value, when
228       called on the real object (don't call this on the meta-attribute).
229

CONCLUSION

231       You might wonder why you'd bother with all this. You could just
232       hardcode "The Site's URL" in the "dump" method. But we want to avoid
233       repetition. If you need the label once, you may need it elsewhere,
234       maybe in the "as_form" method you write next.
235
236       Associating a label with an attribute just makes sense! The label is a
237       piece of information about the attribute.
238
239       It's also important to realize that this was a trivial example. You can
240       make much more powerful metaclasses that do things, as opposed to just
241       storing some more information. For example, you could implement a
242       metaclass that expires attributes after a certain amount of time:
243
244          has site_cache => (
245              traits        => ['TimedExpiry'],
246              expires_after => { hours => 1 },
247              refresh_with  => sub { get( $_[0]->url ) },
248              isa           => 'Str',
249              is            => 'ro',
250          );
251
252       The sky's the limit!
253

AUTHORS

255       •   Stevan Little <stevan@cpan.org>
256
257       •   Dave Rolsky <autarch@urth.org>
258
259       •   Jesse Luehrs <doy@cpan.org>
260
261       •   Shawn M Moore <sartak@cpan.org>
262
263       •   יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
264
265       •   Karen Etheridge <ether@cpan.org>
266
267       •   Florian Ragwitz <rafl@debian.org>
268
269       •   Hans Dieter Pearcey <hdp@cpan.org>
270
271       •   Chris Prather <chris@prather.org>
272
273       •   Matt S Trout <mstrout@cpan.org>
274
276       This software is copyright (c) 2006 by Infinity Interactive, Inc.
277
278       This is free software; you can redistribute it and/or modify it under
279       the same terms as the Perl 5 programming language system itself.
280
281
282
283perl v5.34.0                  Moos2e0:2:1C-o0o7k-b2o2ok::Meta::Labeled_AttributeTrait(3)
Impressum