File Coverage

blib/lib/Class/Meta/AccessorBuilder/Affordance.pm
Criterion Covered Total %
statement 72 74 97.3
branch 34 42 80.9
condition 2 3 66.6
subroutine 15 16 93.7
pod 3 3 100.0
total 126 138 91.3


line stmt bran cond sub pod time code
1             package Class::Meta::AccessorBuilder::Affordance;
2              
3             =head1 NAME
4              
5             Class::Meta::AccessorBuilder::Affordance - Affordance style accessor generation
6              
7             =head1 SYNOPSIS
8              
9             package MyApp::TypeDef;
10              
11             use strict;
12             use Class::Meta::Type;
13             use IO::Socket;
14              
15             my $type = Class::Meta::Type->add(
16             key => 'io_socket',
17             builder => 'affordance',
18             desc => 'IO::Socket object',
19             name => 'IO::Socket Object'
20             );
21              
22             =head1 DESCRIPTION
23              
24             This module provides the an affordance style accessor builder for Class::Meta.
25             Affordance accessors are attribute accessor methods that separate the getting
26             and setting of an attribute value into distinct methods. The approach both
27             eliminates the overhead of checking to see whether an accessor is called as a
28             getter or a setter, which is common for Perl style accessors, while also
29             creating a psychological barrier to accidentally misusing an attribute.
30              
31             =head2 Accessors
32              
33             Class::Meta::AccessorBuilder::Affordance create two different types of
34             accessors: getters and setters. The type of accessors created depends on the
35             value of the C attribute of the Class::Meta::Attribute for which the
36             accessor is being created.
37              
38             For example, if the C is Class::Meta::RDWR, then two accessor methods
39             will be created:
40              
41             my $value = $obj->get_io_socket;
42             $obj->set_io_socket($value);
43              
44             If the value of C is Class::Meta::READ, then only the get method
45             will be created:
46              
47             my $value = $obj->io_socket;
48              
49             And finally, if the value of C is Class::Meta::WRITE, then only the set
50             method will be created (why anyone would want this is beyond me, but I provide
51             for the sake of completeness):
52              
53             my $value = $obj->io_socket;
54              
55             =head2 Data Type Validation
56              
57             Class::Meta::AccessorBuilder::Affordance uses all of the validation checks
58             passed to it to validate new values before assigning them to an attribute. It
59             also checks to see if the attribute is required, and if so, adds a check to
60             ensure that its value is never undefined. It does not currently check to
61             ensure that private and protected methods are used only in their appropriate
62             contexts, but may do so in a future release.
63              
64             =head2 Class Attributes
65              
66             If the C attribute of the attribute object for which accessors are to
67             be built is C, Class::Meta::AccessorBuilder will build
68             accessors for a class attribute instead of an object attribute. Of course,
69             this means that if you change the value of the class attribute in any
70             context--whether via a an object, the class name, or an an inherited class
71             name or object, the value will be changed everywhere.
72              
73             For example, for a class attribute "count", you can expect the following to
74             work:
75              
76             MyApp::Custom->set_count(10);
77             my $count = MyApp::Custom->get_count; # Returns 10.
78             my $obj = MyApp::Custom->new;
79             $count = $obj->get_count; # Returns 10.
80              
81             $obj->set_count(22);
82             $count = $obj->get_count; # Returns 22.
83             my $count = MyApp::Custom->get_count; # Returns 22.
84              
85             MyApp::Custom->set_count(35);
86             $count = $obj->get_count; # Returns 35.
87             my $count = MyApp::Custom->get_count; # Returns 35.
88              
89             Currently, class attribute accessors are not designed to be inheritable in the
90             way designed by Class::Data::Inheritable, although this might be changed in a
91             future release. For now, I expect that the current simple approach will cover
92             the vast majority of circumstances.
93              
94             B Class attribute accessors will not work accurately in multiprocess
95             environments such as mod_perl. If you change a class attribute's value in one
96             process, it will not be changed in any of the others. Furthermore, class
97             attributes are not currently shared across threads. So if you're using
98             Class::Meta class attributes in a multi-threaded environment (such as iThreads
99             in Perl 5.8.0 and later) the changes to a class attribute in one thread will
100             not be reflected in other threads.
101              
102             =head1 Private and Protected Attributes
103              
104             Any attributes that have their C attribute set to Class::Meta::Private
105             or Class::Meta::Protected get additional validation installed to ensure that
106             they're truly private and protected. This includes when they are set via
107             parameters to constructors generated by Class::Meta. The validation is
108             performed by checking the caller of the accessors, and throwing an exception
109             when the caller isn't the class that owns the attribute (for private
110             attributes) or when it doesn't inherit from the class that owns the attribute
111             (for protected attributes).
112              
113             As an implementation note, this validation is performed for parameters passed
114             to constructors created by Class::Meta by ignoring looking for the first
115             caller that isn't Class::Meta::Constructor:
116              
117             my $caller = caller;
118             # Circumvent generated constructors.
119             for (my $i = 1; $caller eq 'Class::Meta::Constructor'; $i++) {
120             $caller = caller($i);
121             }
122              
123             This works because Class::Meta::Constructor installs the closures that become
124             constructors, and thus, when those closures call accessors to set new values
125             for attributes, the caller is Class::Meta::Constructor. By going up the stack
126             until we find another package, we correctly check to see what context is
127             setting attribute values via a constructor, rather than the constructor method
128             itself being the context.
129              
130             This is a bit of a hack, but since Perl uses call stacks for checking security
131             in this way, it's the best I could come up with. Other suggestions welcome. Or
132             see L to
133             create your own accessor generation code
134              
135             =head1 INTERFACE
136              
137             The following functions must be implemented by any Class::Meta accessor
138             generation module.
139              
140             =head2 Functions
141              
142             =head3 build_attr_get
143              
144             my $code = Class::Meta::AccessorBuilder::Affordance::build_attr_get();
145              
146             This function is called by C and returns a
147             code reference that can be used by the C method of
148             Class::Meta::Attribute to return the value stored for that attribute for the
149             object passed to the code reference.
150              
151             =head3 build_attr_set
152              
153             my $code = Class::Meta::AccessorBuilder::Affordance::build_attr_set();
154              
155             This function is called by C and returns a
156             code reference that can be used by the C method of
157             Class::Meta::Attribute to set the value stored for that attribute for the
158             object passed to the code reference.
159              
160             =head3 build
161              
162             Class::Meta::AccessorBuilder::Affordance::build(
163             $pkg, $attribute, $create, @checks
164             );
165              
166             This method is called by the C method of Class::Meta::Type, and does
167             the work of actually generating the accessors for an attribute object. The
168             arguments passed to it are:
169              
170             =over 4
171              
172             =item $pkg
173              
174             The name of the class to which the accessors will be added.
175              
176             =item $attribute
177              
178             The Class::Meta::Attribute object that specifies the attribute for which the
179             accessors will be created.
180              
181             =item $create
182              
183             The value of the C attribute of the Class::Meta::Attribute object,
184             which determines what accessors, if any, are to be created.
185              
186             =item @checks
187              
188             A list of code references that validate the value of an attribute. These will
189             be used in the set accessor (mutator) to validate new attribute values.
190              
191             =back
192              
193             =cut
194              
195 11     39   61 use strict;
  11         21  
  11         397  
196 11     11   64 use Class::Meta;
  11         20  
  11         3148  
197             our $VERSION = '0.66';
198              
199             sub build_attr_get {
200 36     36 1 167 UNIVERSAL::can($_[0]->package, 'get_' . $_[0]->name);
201             }
202              
203             sub build_attr_set {
204 63     63 1 322 UNIVERSAL::can($_[0]->package, 'set_' . $_[0]->name);
205             }
206              
207             my $req_chk = sub {
208             $_[2]->class->handle_error("Attribute ", $_[2]->name, " must be defined")
209             unless defined $_[0];
210             };
211              
212             my $once_chk = sub {
213             $_[2]->class->handle_error("Attribute ", $_[2]->name,
214             " can only be set once")
215             if defined $_[1]->{$_[2]->name};
216             };
217              
218             sub build {
219 36     36 1 163 my ($pkg, $attr, $name, $get, $set) = __PACKAGE__->_build(@_);
220             # Install the accessors.
221 11     11   74 no strict 'refs';
  11         21  
  11         11514  
222 36 50       93 *{"${pkg}::get_$name"} = $get if $get;
  36         257  
223 36 100       98 *{"${pkg}::set_$name"} = $set if $set;
  35         313  
224             }
225              
226             sub _build {
227 64     64   96 shift;
228 64         163 my ($pkg, $attr, $create, @checks) = @_;
229 64         214 my $name = $attr->name;
230              
231             # Add the required check, if needed.
232 64 100       265 unshift @checks, $req_chk if $attr->required;
233              
234             # Add a once check, if needed.
235 64 100       216 unshift @checks, $once_chk if $attr->once;
236              
237 64         100 my ($get, $set);
238 64 100       403 if ($attr->context == Class::Meta::CLASS) {
239             # Create class attribute accessors by creating a closure tha
240             # references this variable.
241 1         8 my $data = $attr->default;
242              
243 1 50       3 if ($create >= Class::Meta::GET) {
244             # Create GET accessor.
245 1     16   5 $get = sub { $data };
  16         78  
246             }
247              
248 1 50       3 if ($create >= Class::Meta::SET) {
249             # Create SET accessor.
250 1 50       11 if (@checks) {
251             $set = sub {
252             # Check the value passed in.
253             $_->($_[1], { $name => $data,
254             __pkg => ref $_[0] || $_[0] },
255 4   66 4   44 $attr) for @checks;
256             # Assign the value.
257 4         70 $data = $_[1];
258 1         4 };
259             } else {
260             $set = sub {
261             # Assign the value.
262 0     0   0 $data = $_[1];
263 0         0 };
264             }
265             }
266             } else {
267             # Create object attribute accessors.
268 63 50       201 if ($create >= Class::Meta::GET) {
269             # Create GET accessor.
270 63     216   282 $get = sub { $_[0]->{$name} };
  216         4252  
271             }
272              
273 63 100       214 if ($create >= Class::Meta::SET) {
274             # Create SET accessor.
275 62 100       140 if (@checks) {
276             $set = sub {
277             # Check the value passed in.
278 204     204   43044 $_->($_[1], $_[0], $attr) for @checks;
279             # Assign the value.
280 156         2160 $_[0]->{$name} = $_[1];
281 59         391 };
282             } else {
283             $set = sub {
284             # Assign the value.
285 6     6   64 $_[0]->{$name} = $_[1];
286 3         14 };
287             }
288             }
289             }
290              
291             # Add public and private checks, if required.
292 64 100       258 if ($attr->view == Class::Meta::PROTECTED) {
    100          
    100          
293 2         5 for ($get, $set) {
294 4 50       21 my $real_sub = $_ or next;
295             $_ = sub {
296 64     64   4897 my $caller = caller;
297             # Circumvent generated constructors.
298 64         233 for (my $i = 1; $caller eq 'Class::Meta::Constructor'; $i++) {
299 16         66 $caller = caller($i);
300             }
301 64 100       475 $attr->class->handle_error("$name is a protected attribute "
302             . "of $pkg")
303             unless UNIVERSAL::isa($caller, $pkg);
304 40         121 goto &$real_sub;
305 4         20 };
306             }
307             } elsif ($attr->view == Class::Meta::PRIVATE) {
308 2         5 for ($get, $set) {
309 4 50       10 my $real_sub = $_ or next;
310             $_ = sub {
311 56     56   20297 my $caller = caller;
312             # Circumvent generated constructors.
313 56         228 for (my $i = 1; $caller eq 'Class::Meta::Constructor'; $i++) {
314 16         69 $caller = caller($i);
315             }
316 56 100       259 $attr->class->handle_error("$name is a private attribute of $pkg")
317             unless $caller eq $pkg;
318 20         52 goto &$real_sub;
319 4         17 };
320             }
321             } elsif ($attr->view == Class::Meta::TRUSTED) {
322 2         10 my $trusted = $attr->class->trusted;
323 2         5 for ($get, $set) {
324 4 50       12 my $real_sub = $_ or next;
325             $_ = sub {
326 72     72   10256 my $caller = caller;
327             # Circumvent generated constructors.
328 72         260 for (my $i = 1; $caller eq 'Class::Meta::Constructor'; $i++) {
329 12         48 $caller = caller($i);
330             }
331 72 100       202 goto &$real_sub if $caller eq $pkg;
332 52         80 for my $pack (@{$trusted}) {
  52         123  
333 52 100       626 goto &$real_sub if UNIVERSAL::isa($caller, $pack);
334             }
335 20         82 $attr->class->handle_error("$name is a trusted attribute of $pkg");
336 4         36 };
337             }
338             }
339 64         283 return ($pkg, $attr, $name, $get, $set);
340             }
341              
342             1;
343             __END__