File Coverage

blib/lib/MooseX/Emulate/Class/Accessor/Fast.pm
Criterion Covered Total %
statement 95 96 98.9
branch 37 56 66.0
condition 7 21 33.3
subroutine 21 21 100.0
pod 7 12 58.3
total 167 206 81.0


line stmt bran cond sub pod time code
1             package MooseX::Emulate::Class::Accessor::Fast;
2              
3 9     9   14419 use Moose::Role;
  9         38368  
  9         48  
4 9     9   46831 use Class::MOP ();
  9         20  
  9         145  
5 9     9   41 use Scalar::Util ();
  9         14  
  9         155  
6              
7 9     9   6537 use MooseX::Emulate::Class::Accessor::Fast::Meta::Accessor ();
  9         26  
  9         13266  
8              
9             our $VERSION = '0.00903';
10              
11             =head1 NAME
12              
13             MooseX::Emulate::Class::Accessor::Fast - Emulate Class::Accessor::Fast behavior using Moose attributes
14              
15             =head1 SYNOPSYS
16              
17             package MyClass;
18             use Moose;
19              
20             with 'MooseX::Emulate::Class::Accessor::Fast';
21              
22              
23             #fields with readers and writers
24             __PACKAGE__->mk_accessors(qw/field1 field2/);
25             #fields with readers only
26             __PACKAGE__->mk_ro_accessors(qw/field3 field4/);
27             #fields with writers only
28             __PACKAGE__->mk_wo_accessors(qw/field5 field6/);
29              
30              
31             =head1 DESCRIPTION
32              
33             This module attempts to emulate the behavior of L<Class::Accessor::Fast> as
34             accurately as possible using the Moose attribute system. The public API of
35             C<Class::Accessor::Fast> is wholly supported, but the private methods are not.
36             If you are only using the public methods (as you should) migration should be a
37             matter of switching your C<use base> line to a C<with> line.
38              
39             While I have attempted to emulate the behavior of Class::Accessor::Fast as closely
40             as possible bugs may still be lurking in edge-cases.
41              
42             =head1 BEHAVIOR
43              
44             Simple documentation is provided here for your convenience, but for more thorough
45             documentation please see L<Class::Accessor::Fast> and L<Class::Accessor>.
46              
47             =head2 A note about introspection
48              
49             Please note that, at this time, the C<is> flag attribute is not being set. To
50             determine the C<reader> and C<writer> methods using introspection in later versions
51             of L<Class::MOP> ( > 0.38) please use the C<get_read_method> and C<get_write_method>
52             methods in L<Class::MOP::Attribute>. Example
53              
54             # with Class::MOP <= 0.38
55             my $attr = $self->meta->find_attribute_by_name($field_name);
56             my $reader_method = $attr->reader || $attr->accessor;
57             my $writer_method = $attr->writer || $attr->accessor;
58              
59             # with Class::MOP > 0.38
60             my $attr = $self->meta->find_attribute_by_name($field_name);
61             my $reader_method = $attr->get_read_method;
62             my $writer_method = $attr->get_write_method;
63              
64             =head1 METHODS
65              
66             =head2 BUILD $self %args
67              
68             Change the default Moose class building to emulate the behavior of C::A::F and
69             store arguments in the instance hashref.
70              
71             =cut
72              
73             my $locate_metaclass = sub {
74             my $class = Scalar::Util::blessed($_[0]) || $_[0];
75             return Class::MOP::get_metaclass_by_name($class)
76             || Moose::Meta::Class->initialize($class);
77             };
78              
79 17     17 1 44 sub BUILD { }
80              
81             around 'BUILD' => sub {
82             my $orig = shift;
83             my $self = shift;
84             my %args = %{ $_[0] };
85             $self->$orig(\%args);
86             my @extra = grep { !exists($self->{$_}) } keys %args;
87             @{$self}{@extra} = @args{@extra};
88             return $self;
89             };
90              
91             =head2 mk_accessors @field_names
92              
93             Create read-write accessors. An attribute named C<$field_name> will be created.
94             The name of the c<reader> and C<writer> methods will be determined by the return
95             value of C<accessor_name_for> and C<mutator_name_for>, which by default return the
96             name passed unchanged. If the accessor and mutator names are equal the C<accessor>
97             attribute will be passes to Moose, otherwise the C<reader> and C<writer> attributes
98             will be passed. Please see L<Class::MOP::Attribute> for more information.
99              
100             =cut
101              
102             sub mk_accessors {
103 9     9 1 19905 my $self = shift;
104 9         30 my $meta = $locate_metaclass->($self);
105 9         11987 my $class = $meta->name;
106 9 50       34 confess("You are trying to modify ${class}, which has been made immutable, this is ".
107             "not supported. Try subclassing ${class}, rather than monkeypatching it")
108             if $meta->is_immutable;
109              
110 9         69 for my $attr_name (@_){
111 15 100       772 $meta->remove_attribute($attr_name)
112             if $meta->find_attribute_by_name($attr_name);
113 15         1305 my $reader = $self->accessor_name_for($attr_name);
114 15         75 my $writer = $self->mutator_name_for( $attr_name);
115              
116             #dont overwrite existing methods
117 15 100       53 if($reader eq $writer){
118 14 100       111 my %opts = ( $meta->has_method($reader) ? ( is => 'bare' ) : (accessor => $reader) );
119 14   33     403 my $attr = $meta->find_attribute_by_name($attr_name) || $meta->add_attribute($attr_name, %opts,
120             traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute']
121             );
122 14 50       8065 if($attr_name eq $reader){
123 14         41 my $alias = "_${attr_name}_accessor";
124 14 100       60 next if $meta->has_method($alias);
125 13         395 $meta->add_method($alias => $attr->get_read_method_ref);
126             }
127             } else {
128 1 50       5 my @opts = ( $meta->has_method($writer) ? () : (writer => $writer) );
129 1 50       26 push(@opts, (reader => $reader)) unless $meta->has_method($reader);
130 1   33     27 my $attr = $meta->find_attribute_by_name($attr_name) || $meta->add_attribute($attr_name, @opts,
131             traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute']
132             );
133             }
134             }
135             }
136              
137             =head2 mk_ro_accessors @field_names
138              
139             Create read-only accessors.
140              
141             =cut
142              
143             sub mk_ro_accessors {
144 4     4 1 348 my $self = shift;
145 4         13 my $meta = $locate_metaclass->($self);
146 4         30 my $class = $meta->name;
147 4 50       23 confess("You are trying to modify ${class}, which has been made immutable, this is ".
148             "not supported. Try subclassing ${class}, rather than monkeypatching it")
149             if $meta->is_immutable;
150 4         25 for my $attr_name (@_){
151 5 100       168 $meta->remove_attribute($attr_name)
152             if $meta->find_attribute_by_name($attr_name);
153 5         415 my $reader = $self->accessor_name_for($attr_name);
154 5 50       72 my @opts = ($meta->has_method($reader) ? (is => 'bare') : (reader => $reader) );
155 5 50       143 my $attr = $meta->add_attribute($attr_name, @opts,
156             traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute']
157             ) if scalar(@opts);
158 5 50 33     9365 if($reader eq $attr_name && $reader eq $self->mutator_name_for($attr_name)){
159 5 100       30 $meta->add_method("_${attr_name}_accessor" => $attr->get_read_method_ref)
160             unless $meta->has_method("_${attr_name}_accessor");
161             }
162             }
163             }
164              
165             =head2 mk_ro_accessors @field_names
166              
167             Create write-only accessors.
168              
169             =cut
170              
171             #this is retarded.. but we need it for compatibility or whatever.
172             sub mk_wo_accessors {
173 4     4 0 302 my $self = shift;
174 4         10 my $meta = $locate_metaclass->($self);
175 4         29 my $class = $meta->name;
176 4 50       16 confess("You are trying to modify ${class}, which has been made immutable, this is ".
177             "not supported. Try subclassing ${class}, rather than monkeypatching it")
178             if $meta->is_immutable;
179 4         21 for my $attr_name (@_){
180 5 100       130 $meta->remove_attribute($attr_name)
181             if $meta->find_attribute_by_name($attr_name);
182 5         383 my $writer = $self->mutator_name_for($attr_name);
183 5 50       17 my @opts = ($meta->has_method($writer) ? () : (writer => $writer) );
184 5 50       195 my $attr = $meta->add_attribute($attr_name, @opts,
185             traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute']
186             ) if scalar(@opts);
187 5 50 33     790 if($writer eq $attr_name && $writer eq $self->accessor_name_for($attr_name)){
188 5 100       26 $meta->add_method("_${attr_name}_accessor" => $attr->get_write_method_ref)
189             unless $meta->has_method("_${attr_name}_accessor");
190             }
191             }
192             }
193              
194             =head2 follow_best_practices
195              
196             Preface readers with 'get_' and writers with 'set_'.
197             See original L<Class::Accessor> documentation for more information.
198              
199             =cut
200              
201             sub follow_best_practice {
202 1     1 0 105 my $self = shift;
203 1         5 my $meta = $locate_metaclass->($self);
204              
205 1         24 $meta->remove_method('mutator_name_for');
206 1         55 $meta->remove_method('accessor_name_for');
207 1     1   29 $meta->add_method('mutator_name_for', sub{ return "set_".$_[1] });
  1     1   3  
208 1     1   43 $meta->add_method('accessor_name_for', sub{ return "get_".$_[1] });
  1     1   5  
209             }
210              
211             =head2 mutator_name_for
212              
213             =head2 accessor_name_for
214              
215             See original L<Class::Accessor> documentation for more information.
216              
217             =cut
218              
219 24     24 1 76 sub mutator_name_for { return $_[1] }
220 24     24 1 68 sub accessor_name_for { return $_[1] }
221              
222             =head2 set
223              
224             See original L<Class::Accessor> documentation for more information.
225              
226             =cut
227              
228             sub set {
229 1     1 1 532 my $self = shift;
230 1         1 my $k = shift;
231 1 50       3 confess "Wrong number of arguments received" unless scalar @_;
232 1         3 my $meta = $locate_metaclass->($self);
233              
234 1 50       8 confess "No such attribute '$k'"
235             unless ( my $attr = $meta->find_attribute_by_name($k) );
236 1         34 my $writer = $attr->get_write_method;
237 1 50       18 $self->$writer(@_ > 1 ? [@_] : @_);
238             }
239              
240             =head2 get
241              
242             See original L<Class::Accessor> documentation for more information.
243              
244             =cut
245              
246             sub get {
247 2     2 1 98 my $self = shift;
248 2 50       5 confess "Wrong number of arguments received" unless scalar @_;
249 2         5 my $meta = $locate_metaclass->($self);
250 2         8 my @values;
251              
252 2         4 for( @_ ){
253 2 50       5 confess "No such attribute '$_'"
254             unless ( my $attr = $meta->find_attribute_by_name($_) );
255 2         69 my $reader = $attr->get_read_method;
256 2 50       20 @_ > 1 ? push(@values, $self->$reader) : return $self->$reader;
257             }
258              
259 0         0 return @values;
260             }
261              
262             sub make_accessor {
263 1     1 0 92 my($class, $field) = @_;
264 1         3 my $meta = $locate_metaclass->($class);
265 1   33     10 my $attr = $meta->find_attribute_by_name($field) || $meta->add_attribute($field,
266             traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute'],
267             is => 'bare',
268             );
269 1         1063 my $reader = $attr->get_read_method_ref;
270 1         74 my $writer = $attr->get_write_method_ref;
271             return sub {
272 3     3   979 my $self = shift;
273 3 100       14 return $reader->($self) unless @_;
274 1 50       7 return $writer->($self,(@_ > 1 ? [@_] : @_));
275             }
276 1         72 }
277              
278              
279             sub make_ro_accessor {
280 1     1 0 494 my($class, $field) = @_;
281 1         3 my $meta = $locate_metaclass->($class);
282 1   33     1192 my $attr = $meta->find_attribute_by_name($field) || $meta->add_attribute($field,
283             traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute'],
284             is => 'bare',
285             );
286 1         1183 return $attr->get_read_method_ref;
287             }
288              
289              
290             sub make_wo_accessor {
291 1     1 0 96 my($class, $field) = @_;
292 1         3 my $meta = $locate_metaclass->($class);
293 1   33     7 my $attr = $meta->find_attribute_by_name($field) || $meta->add_attribute($field,
294             traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute'],
295             is => 'bare',
296             );
297 1         1081 return $attr->get_write_method_ref;
298             }
299              
300             1;
301              
302             =head2 meta
303              
304             See L<Moose::Meta::Class>.
305              
306             =cut
307              
308             =head1 SEE ALSO
309              
310             L<Moose>, L<Moose::Meta::Attribute>, L<Class::Accessor>, L<Class::Accessor::Fast>,
311             L<Class::MOP::Attribute>, L<MooseX::Adopt::Class::Accessor::Fast>
312              
313             =head1 AUTHORS
314              
315             Guillermo Roditi (groditi) E<lt>groditi@cpan.orgE<gt>
316              
317             With contributions from:
318              
319             =over 4
320              
321             =item Tomas Doran (t0m) E<lt>bobtfish@bobtfish.netE<gt>
322              
323             =item Florian Ragwitz (rafl) E<lt>rafl@debian.orgE<gt>
324              
325             =back
326              
327             =head1 LICENSE
328              
329             You may distribute this code under the same terms as Perl itself.
330              
331             =cut