File Coverage

blib/lib/MooX/Emulate/Class/Accessor/Fast.pm
Criterion Covered Total %
statement 91 92 98.9
branch 25 28 89.2
condition 14 15 93.3
subroutine 26 26 100.0
pod 11 12 91.6
total 167 173 96.5


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