1Moose::Cookbook::LegacyU:s:eMLroaobCseoeln:et:drC_ioAbotukttbreoidobkuP:te:erLMleegtDaaocccylu:am:seLsna(tb3ae)tlieodn_AttributeMetaclass(3)
2
3
4

NAME

6       Moose::Cookbook::Legacy::Labeled_AttributeMetaclass - A meta-attribute,
7       attributes with labels
8

VERSION

10       version 2.2011
11

SYNOPSIS

13         package MyApp::Meta::Attribute::Labeled;
14         use Moose;
15         extends 'Moose::Meta::Attribute';
16
17         has label => (
18             is        => 'rw',
19             isa       => 'Str',
20             predicate => 'has_label',
21         );
22
23         package Moose::Meta::Attribute::Custom::Labeled;
24         sub register_implementation {'MyApp::Meta::Attribute::Labeled'}
25
26         package MyApp::Website;
27         use Moose;
28
29         has url => (
30             metaclass => 'Labeled',
31             is        => 'rw',
32             isa       => 'Str',
33             label     => "The site's URL",
34         );
35
36         has name => (
37             is  => 'rw',
38             isa => 'Str',
39         );
40
41         sub dump {
42             my $self = shift;
43
44             my $meta = $self->meta;
45
46             my $dump = '';
47
48             for my $attribute ( map { $meta->get_attribute($_) }
49                 sort $meta->get_attribute_list ) {
50
51                 if (   $attribute->isa('MyApp::Meta::Attribute::Labeled')
52                     && $attribute->has_label ) {
53                     $dump .= $attribute->label;
54                 }
55                 else {
56                     $dump .= $attribute->name;
57                 }
58
59                 my $reader = $attribute->get_read_method;
60                 $dump .= ": " . $self->$reader . "\n";
61             }
62
63             return $dump;
64         }
65
66         package main;
67
68         my $app = MyApp::Website->new( url => "http://google.com", name => "Google" );
69

SUMMARY

71       WARNING: Subclassing metaclasses (as opposed to providing metaclass
72       traits) is strongly discouraged. This recipe is provided solely for
73       reference when encountering older code that does this.
74
75       In this recipe, we begin to delve into the wonder of meta-programming.
76       Some readers may scoff and claim that this is the arena of only the
77       most twisted Moose developers. Absolutely not! Any sufficiently twisted
78       developer can benefit greatly from going more meta.
79
80       Our goal is to allow each attribute to have a human-readable "label"
81       attached to it. Such labels would be used when showing data to an end
82       user. In this recipe we label the "url" attribute with "The site's URL"
83       and create a simple method showing how to use that label.
84
85       The proper, modern way to extend attributes (using a role instead of a
86       subclass) is described in Moose::Cookbook::Meta::Recipe3, but that
87       recipe assumes you've read and at least tried to understand this one.
88

META-ATTRIBUTE OBJECTS

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

RECIPE REVIEW

117       We start by creating a new attribute metaclass.
118
119         package MyApp::Meta::Attribute::Labeled;
120         use Moose;
121         extends 'Moose::Meta::Attribute';
122
123       We can subclass a Moose metaclass in the same way that we subclass
124       anything else.
125
126         has label => (
127             is        => 'rw',
128             isa       => 'Str',
129             predicate => 'has_label',
130         );
131
132       Again, this is standard Moose code.
133
134       Then we need to register our metaclass with Moose:
135
136         package Moose::Meta::Attribute::Custom::Labeled;
137         sub register_implementation { 'MyApp::Meta::Attribute::Labeled' }
138
139       This is a bit of magic that lets us use a short name, "Labeled", when
140       referring to our new metaclass.
141
142       That was the whole attribute metaclass.
143
144       Now we start using it.
145
146         package MyApp::Website;
147         use Moose;
148         use MyApp::Meta::Attribute::Labeled;
149
150       We have to load the metaclass to use it, just like any Perl class.
151
152       Finally, we use it for an attribute:
153
154         has url => (
155             metaclass => 'Labeled',
156             is        => 'rw',
157             isa       => 'Str',
158             label     => "The site's URL",
159         );
160
161       This looks like a normal attribute declaration, except for two things,
162       the "metaclass" and "label" parameters. The "metaclass" parameter tells
163       Moose we want to use a custom metaclass for this (one) attribute. The
164       "label" parameter will be stored in the meta-attribute object.
165
166       The reason that we can pass the name "Labeled", instead of
167       "MyApp::Meta::Attribute::Labeled", is because of the
168       "register_implementation" code we touched on previously.
169
170       When you pass a metaclass to "has", it will take the name you provide
171       and prefix it with "Moose::Meta::Attribute::Custom::". Then it calls
172       "register_implementation" in the package. In this case, that means
173       Moose ends up calling
174       "Moose::Meta::Attribute::Custom::Labeled::register_implementation".
175
176       If this function exists, it should return the real metaclass package
177       name. This is exactly what our code does, returning
178       "MyApp::Meta::Attribute::Labeled". This is a little convoluted, and if
179       you don't like it, you can always use the fully-qualified name.
180
181       We can access this meta-attribute and its label like this:
182
183         $website->meta->get_attribute('url')->label()
184
185         MyApp::Website->meta->get_attribute('url')->label()
186
187       We also have a regular attribute, "name":
188
189         has name => (
190             is  => 'rw',
191             isa => 'Str',
192         );
193
194       This is a regular Moose attribute, because we have not specified a new
195       metaclass.
196
197       Finally, we have a "dump" method, which creates a human-readable
198       representation of a "MyApp::Website" object. It will use an attribute's
199       label if it has one.
200
201         sub dump {
202             my $self = shift;
203
204             my $meta = $self->meta;
205
206             my $dump = '';
207
208             for my $attribute ( map { $meta->get_attribute($_) }
209                 sort $meta->get_attribute_list ) {
210
211                 if (   $attribute->isa('MyApp::Meta::Attribute::Labeled')
212                     && $attribute->has_label ) {
213                     $dump .= $attribute->label;
214                 }
215
216       This is a bit of defensive code. We cannot depend on every meta-
217       attribute having a label. Even if we define one for every attribute in
218       our class, a subclass may neglect to do so. Or a superclass could add
219       an attribute without a label.
220
221       We also check that the attribute has a label using the predicate we
222       defined. We could instead make the label "required". If we have a
223       label, we use it, otherwise we use the attribute name:
224
225                 else {
226                     $dump .= $attribute->name;
227                 }
228
229                 my $reader = $attribute->get_read_method;
230                 $dump .= ": " . $self->$reader . "\n";
231             }
232
233             return $dump;
234         }
235
236       The "get_read_method" is part of the Moose::Meta::Attribute API. It
237       returns the name of a method that can read the attribute's value, when
238       called on the real object (don't call this on the meta-attribute).
239

CONCLUSION

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

AUTHORS

265       ·   Stevan Little <stevan.little@iinteractive.com>
266
267       ·   Dave Rolsky <autarch@urth.org>
268
269       ·   Jesse Luehrs <doy@tozt.net>
270
271       ·   Shawn M Moore <code@sartak.org>
272
273       ·   יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
274
275       ·   Karen Etheridge <ether@cpan.org>
276
277       ·   Florian Ragwitz <rafl@debian.org>
278
279       ·   Hans Dieter Pearcey <hdp@weftsoar.net>
280
281       ·   Chris Prather <chris@prather.org>
282
283       ·   Matt S Trout <mst@shadowcat.co.uk>
284
286       This software is copyright (c) 2006 by Infinity Interactive, Inc.
287
288       This is free software; you can redistribute it and/or modify it under
289       the same terms as the Perl 5 programming language system itself.
290
291
292
293perl v5.28.1            Moose::Coo2k0b1o8o-k0:5:-L1e6gacy::Labeled_AttributeMetaclass(3)
Impressum