File Coverage

blib/lib/Class/Meta/AccessorBuilder/SemiAffordance.pm
Criterion Covered Total %
statement 18 18 100.0
branch 2 4 50.0
condition n/a
subroutine 6 6 100.0
pod 2 2 100.0
total 28 30 93.3


line stmt bran cond sub pod time code
1             package Class::Meta::AccessorBuilder::SemiAffordance;
2              
3             =head1 NAME
4              
5             Class::Meta::AccessorBuilder::SemiAffordance - Semi-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 => 'semi-affordance',
18             desc => 'IO::Socket object',
19             name => 'IO::Socket Object'
20             );
21              
22             =head1 DESCRIPTION
23              
24             This module provides a semi-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              
32             =head2 Accessors
33              
34             Class::Meta::AccessorBuilder::SemiAffordance create two different types of
35             accessors: getters and setters. What makes the accessors generated by this
36             class "semi-affordance" rather than "affordance" accessors is that the getter
37             is simply named for the attribute, while the setter is prepended by C.
38             This approach differs from that of affordance accessors, where the getter is
39             prepended by C.
40              
41             The type of accessors created depends on the value of the C attribute
42             of the Class::Meta::Attribute for which the accessor is being created.
43              
44             For example, if the C is Class::Meta::RDWR, then two accessor methods
45             will be created:
46              
47             my $value = $obj->io_socket;
48             $obj->set_io_socket($value);
49              
50             If the value of C is Class::Meta::READ, then only the get method
51             will be created:
52              
53             my $value = $obj->io_socket;
54              
55             And finally, if the value of C is Class::Meta::WRITE, then only the set
56             method will be created (why anyone would want this is beyond me, but I provide
57             for the sake of completeness):
58              
59             my $value = $obj->io_socket;
60              
61             =head2 Data Type Validation
62              
63             Class::Meta::AccessorBuilder::SemiAffordance uses all of the validation checks
64             passed to it to validate new values before assigning them to an attribute. It
65             also checks to see if the attribute is required, and if so, adds a check to
66             ensure that its value is never undefined. It does not currently check to
67             ensure that private and protected methods are used only in their appropriate
68             contexts, but may do so in a future release.
69              
70             =head2 Class Attributes
71              
72             If the C attribute of the attribute object for which accessors are to
73             be built is C, Class::Meta::AccessorBuilder will build
74             accessors for a class attribute instead of an object attribute. Of course,
75             this means that if you change the value of the class attribute in any
76             context--whether via a an object, the class name, or an an inherited class
77             name or object, the value will be changed everywhere.
78              
79             For example, for a class attribute "count", you can expect the following to
80             work:
81              
82             MyApp::Custom->set_count(10);
83             my $count = MyApp::Custom->count; # Returns 10.
84             my $obj = MyApp::Custom->new;
85             $count = $obj->count; # Returns 10.
86              
87             $obj->set_count(22);
88             $count = $obj->count; # Returns 22.
89             my $count = MyApp::Custom->count; # Returns 22.
90              
91             MyApp::Custom->set_count(35);
92             $count = $obj->count; # Returns 35.
93             my $count = MyApp::Custom->count; # Returns 35.
94              
95             Currently, class attribute accessors are not designed to be inheritable in the
96             way designed by Class::Data::Inheritable, although this might be changed in a
97             future release. For now, I expect that the current simple approach will cover
98             the vast majority of circumstances.
99              
100             B Class attribute accessors will not work accurately in multiprocess
101             environments such as mod_perl. If you change a class attribute's value in one
102             process, it will not be changed in any of the others. Furthermore, class
103             attributes are not currently shared across threads. So if you're using
104             Class::Meta class attributes in a multi-threaded environment (such as iThreads
105             in Perl 5.8.0 and later) the changes to a class attribute in one thread will
106             not be reflected in other threads.
107              
108             =head1 Private and Protected Attributes
109              
110             Any attributes that have their C attribute set to Class::Meta::Private
111             or Class::Meta::Protected get additional validation installed to ensure that
112             they're truly private and protected. This includes when they are set via
113             parameters to constructors generated by Class::Meta. The validation is
114             performed by checking the caller of the accessors, and throwing an exception
115             when the caller isn't the class that owns the attribute (for private
116             attributes) or when it doesn't inherit from the class that owns the attribute
117             (for protected attributes).
118              
119             As an implementation note, this validation is performed for parameters passed
120             to constructors created by Class::Meta by ignoring looking for the first
121             caller that isn't Class::Meta::Constructor:
122              
123             my $caller = caller;
124             # Circumvent generated constructors.
125             for (my $i = 1; $caller eq 'Class::Meta::Constructor'; $i++) {
126             $caller = caller($i);
127             }
128              
129             This works because Class::Meta::Constructor installs the closures that become
130             constructors, and thus, when those closures call accessors to set new values
131             for attributes, the caller is Class::Meta::Constructor. By going up the stack
132             until we find another package, we correctly check to see what context is
133             setting attribute values via a constructor, rather than the constructor method
134             itself being the context.
135              
136             This is a bit of a hack, but since Perl uses call stacks for checking security
137             in this way, it's the best I could come up with. Other suggestions welcome. Or
138             see L to
139             create your own accessor generation code
140              
141             =head1 INTERFACE
142              
143             The following functions must be implemented by any Class::Meta accessor
144             generation module.
145              
146             =head2 Functions
147              
148             =head3 build_attr_get
149              
150             my $code = Class::Meta::AccessorBuilder::SemiAffordance::build_attr_get();
151              
152             This function is called by C and returns a
153             code reference that can be used by the C method of
154             Class::Meta::Attribute to return the value stored for that attribute for the
155             object passed to the code reference.
156              
157             =head3 build_attr_set
158              
159             my $code = Class::Meta::AccessorBuilder::SemiAffordance::build_attr_set();
160              
161             This function is called by C and returns a
162             code reference that can be used by the C method of
163             Class::Meta::Attribute to set the value stored for that attribute for the
164             object passed to the code reference.
165              
166             =head3 build
167              
168             Class::Meta::AccessorBuilder::SemiAffordance::build(
169             $pkg, $attribute, $create, @checks
170             );
171              
172             This method is called by the C method of Class::Meta::Type, and does
173             the work of actually generating the accessors for an attribute object. The
174             arguments passed to it are:
175              
176             =over 4
177              
178             =item $pkg
179              
180             The name of the class to which the accessors will be added.
181              
182             =item $attribute
183              
184             The Class::Meta::Attribute object that specifies the attribute for which the
185             accessors will be created.
186              
187             =item $create
188              
189             The value of the C attribute of the Class::Meta::Attribute object,
190             which determines what accessors, if any, are to be created.
191              
192             =item @checks
193              
194             A list of code references that validate the value of an attribute. These will
195             be used in the set accessor (mutator) to validate new attribute values.
196              
197             =back
198              
199             =cut
200              
201 4     4   22 use strict;
  4         8  
  4         159  
202 4     4   23 use Class::Meta;
  4         8  
  4         88  
203 4     4   23 use base 'Class::Meta::AccessorBuilder::Affordance';
  4         8  
  4         2960  
204             our $VERSION = '0.66';
205              
206             sub build_attr_get {
207 28     28 1 90 UNIVERSAL::can($_[0]->package, $_[0]->name);
208             }
209              
210             sub build {
211 28     28 1 175 my ($pkg, $attr, $name, $get, $set) = __PACKAGE__->_build(@_);
212             # Install the accessors.
213 4     4   26 no strict 'refs';
  4         8  
  4         334  
214 28 50       79 *{"${pkg}::$name"} = $get if $get;
  28         209  
215 28 50       77 *{"${pkg}::set_$name"} = $set if $set;
  28         193  
216             }
217              
218             1;
219             __END__