File Coverage

blib/lib/MooX/Emulate/Class/Accessor/Fast.pm
Criterion Covered Total %
statement 94 95 98.9
branch 25 28 89.2
condition 14 15 93.3
subroutine 27 27 100.0
pod 11 12 91.6
total 171 177 96.6


line stmt bran cond sub pod time code
1             package MooX::Emulate::Class::Accessor::Fast;
2              
3             $MooX::Emulate::Class::Accessor::Fast::VERSION = '0.04';
4              
5             =head1 NAME
6              
7             MooX::Emulate::Class::Accessor::Fast - Emulate Class::Accessor::Fast behavior using Moo attributes.
8              
9             =head1 SYNOPSYS
10              
11             package MyClass;
12             use Moo;
13            
14             with 'MooX::Emulate::Class::Accessor::Fast';
15            
16             # Fields with readers and writers:
17             __PACKAGE__->mk_accessors(qw/field1 field2/);
18            
19             # Fields with readers only:
20             __PACKAGE__->mk_ro_accessors(qw/field3 field4/);
21            
22             # Fields with writers only:
23             __PACKAGE__->mk_wo_accessors(qw/field5 field6/);
24              
25              
26             =head1 DESCRIPTION
27              
28             This module attempts to emulate the behavior of L as
29             accurately as possible using the Moo attribute system. The public API of
30             Class::Accessor::Fast is wholly supported, but the private methods are not.
31             If you are only using the public methods (as you should) migration should be a
32             matter of switching your C line to a C line.
33              
34             This module is a straight fork-and-port of L
35             version C<0.00903> for L. All tests from the original Moose module pass or
36             were, as little as possible, modified to pass. Much of the documentation, code
37             concepts, and tests are just straight copied from the original module. The core
38             functionality, though, had to be a complete rewrite for Moo.
39              
40             While we have attempted to emulate the behavior of Class::Accessor::Fast as closely
41             as possible bugs may still be lurking in edge-cases.
42              
43             =head1 BEHAVIOR
44              
45             Simple documentation is provided here for your convenience, but for more thorough
46             documentation please see L and L.
47              
48             =cut
49              
50 10     10   384890 use Package::Stash;
  10         50722  
  10         291  
51 9     9   4022 use Class::Method::Modifiers qw( install_modifier );
  9         13838  
  9         550  
52 9     9   60 use Carp qw( croak );
  9         31  
  9         328  
53              
54 9     9   1044 use Moo::Role;
  9         34124  
  9         100  
55 9     9   3668 use strictures 2;
  9         106  
  9         390  
56              
57       20 0   sub BUILD { }
58              
59             around BUILD => sub {
60             my $orig = shift;
61             my $self = shift;
62              
63             my %args = %{ $_[0] };
64             $self->$orig(\%args);
65              
66             my @extra = grep { !exists($self->{$_}) } keys %args;
67             @{$self}{@extra} = @args{@extra};
68              
69             return $self;
70             };
71              
72             =head1 METHODS
73              
74             =head2 mk_accessors
75              
76             __PACKAGE__->mk_accessors( @field_names );
77              
78             See L.
79              
80             =cut
81              
82             sub mk_accessors {
83 11     11 1 50828 my ($class, @fields) = @_;
84              
85 11         37 foreach my $field (@fields) {
86 12         57 $class->make_accessor( $field );
87             }
88              
89 11         40 return;
90             }
91              
92             =head2 mk_ro_accessors
93              
94             __PACKAGE__->mk_ro_accessors( @field_names );
95              
96             See L.
97              
98             =cut
99              
100             sub mk_ro_accessors {
101 7     7 1 41 my ($class, @fields) = @_;
102              
103 7         16 foreach my $field (@fields) {
104 7         21 $class->make_ro_accessor( $field );
105             }
106              
107 7         20 return;
108             }
109              
110             =head2 mk_wo_accessors
111              
112             __PACKAGE__->mk_wo_accessors( @field_names );
113              
114             See L.
115              
116             =cut
117              
118             sub mk_wo_accessors {
119 7     7 1 37 my ($class, @fields) = @_;
120              
121 7         15 foreach my $field (@fields) {
122 7         21 $class->make_wo_accessor( $field );
123             }
124              
125 7         21 return;
126             }
127              
128             =head2 follow_best_practice
129              
130             __PACKAGE__->follow_best_practice();
131              
132             See L.
133              
134             =cut
135              
136             sub follow_best_practice {
137 1     1 1 941 my ($class) = @_;
138              
139 1         8 my $stash = Package::Stash->new( $class );
140              
141             $stash->add_symbol(
142             '&mutator_name_for',
143 3     3   5 sub{ 'set_' . $_[1] },
144 1         10 );
145              
146             $stash->add_symbol(
147             '&accessor_name_for',
148 3     3   8 sub{ 'get_' . $_[1] },
149 1         5 );
150              
151 1         4 return;
152             }
153              
154             =head2 mutator_name_for
155              
156             sub mutator_name_for { 'change_' . $_[1] }
157              
158             See L.
159              
160             =cut
161              
162 23     23 1 40 sub mutator_name_for { $_[1] }
163              
164             =head2 accessor_name_for
165              
166             sub accessor_name_for { 'retrieve_' . $_[1] }
167              
168             See L.
169              
170             =cut
171              
172 23     23 1 54 sub accessor_name_for { $_[1] }
173              
174             =head2 set
175              
176             $object->set( $field => $value );
177              
178             See L.
179              
180             =cut
181              
182             sub set {
183 1     1 1 5 my $self = shift;
184 1         2 my $field = shift;
185              
186 1         4 my $method = "_set_moocaf_$field";
187 1         43 return $self->$method( @_ );
188             }
189              
190             =head2 get
191              
192             my $value = $object->get( $field );
193             my @values = $object->get( $field1, $field2 );
194              
195             See L.
196              
197             =cut
198              
199             sub get {
200 2     2 1 13 my $self = shift;
201              
202 2         5 my @values;
203 2         6 foreach my $field (@_) {
204 2         8 my $method = "_get_moocaf_$field";
205 2         13 push @values, $self->$method();
206             }
207              
208 2 50       16 return $values[0] if @values==1;
209 0         0 return @values;
210             }
211              
212             sub _make_moocaf_accessor {
213 29     29   70 my ($class, $field, $type) = @_;
214              
215 9 100   9   7771 if (! do { no strict 'refs'; defined &{"${class}::has"} } ) {
  9         25  
  9         5946  
  29         62  
  29         41  
  29         145  
216 6         43 require Moo;
217 6     5   417 my $ok = eval "package $class; use Moo; 1";
  5         26  
  5         10  
  5         44  
218 6 50       31 croak "Failed to import Moo into $class" if !$ok;
219             }
220              
221 29         85 my $private_reader = "_get_moocaf_$field";
222 29         56 my $private_writer = "_set_moocaf_$field";
223              
224 29 100       197 if (!$class->can($private_reader)) {
225 26         133 $class->can('has')->(
226             $field,
227             is => 'rw',
228             reader => $private_reader,
229             writer => $private_writer,
230             );
231              
232             install_modifier(
233             $class, 'around', $private_writer,
234             sub{
235 7     7   144 my $orig = shift;
236 7         17 my $self = shift;
237 7 50       25 return $self->$orig() if !@_;
238 7 100       29 my $value = (@_>1) ? [@_] : $_[0];
239 7         40 $self->$orig( $value );
240 7         17 return $self;
241             },
242 26         53564 );
243             }
244              
245 29         7373 my $reader = $class->accessor_name_for( $field );
246 29         111 my $writer = $class->mutator_name_for( $field );
247              
248 29 100       93 $reader = undef if $type eq 'wo';
249 29 100       68 $writer = undef if $type eq 'ro';
250              
251 29         326 my $stash = Package::Stash->new( $class );
252              
253 29 100 100     183 if (($reader and $writer) and ($reader eq $writer)) {
      100        
254             $stash->add_symbol(
255             '&' . $reader,
256             sub{
257 8     8   36 my $self = shift;
258 8 100       51 return $self->$private_reader() if !@_;
259 4         94 return $self->$private_writer( @_ );
260             },
261 10 100       276 ) if !$stash->has_symbol('&' . $reader);
262             }
263             else {
264             $stash->add_symbol(
265             '&' . $reader,
266 1     1   10 sub{ shift()->$private_reader( @_ ) },
267 19 100 100     220 ) if $reader and !$stash->has_symbol('&' . $reader);
268              
269             $stash->add_symbol(
270             '&' . $writer,
271 1     1   20 sub{ shift()->$private_writer( @_ ) },
272 19 100 100     188 ) if $writer and !$stash->has_symbol('&' . $writer);
273             }
274              
275             return sub{
276 4     4   13 my $self = shift;
277 4 100 66     48 return $self->$private_reader( @_ ) unless @_ and $type ne 'wo';
278 1         26 return $self->$private_writer( @_ );
279 29         309 };
280             }
281              
282             =head2 make_accessor
283              
284             my $coderef = $class->make_accessor( $field );
285              
286             See L.
287              
288             =cut
289              
290             sub make_accessor {
291 13     13 1 83 my ($class, $field) = @_;
292 13         52 return $class->_make_moocaf_accessor( $field, 'rw' );
293             }
294              
295             =head2 make_ro_accessor
296              
297             my $coderef = $class->make_ro_accessor( $field );
298              
299             See L.
300              
301             =cut
302              
303             sub make_ro_accessor {
304 8     8 1 556 my ($class, $field) = @_;
305 8         25 return $class->_make_moocaf_accessor( $field, 'ro' );
306             }
307              
308             =head2 make_wo_accessor
309              
310             my $coderef = $class->make_wo_accessor( $field );
311              
312             See L.
313              
314             =cut
315              
316             sub make_wo_accessor {
317 8     8 1 24 my ($class, $field) = @_;
318 8         29 return $class->_make_moocaf_accessor( $field, 'wo' );
319             }
320              
321             1;
322             __END__