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