0.002 added the constructor functionality and removed auto_install
[gitmo/MooseX-Emulate-Class-Accessor-Fast.git] / lib / MooseX / Emulate / Class / Accessor / Fast.pm
1 package MooseX::Emulate::Class::Accessor::Fast;
2
3 use Moose::Role;
4
5 our $VERSION = '0.00200';
6
7 =head1 NAME
8
9 MooseX::Emulate::Class::Accessor::Fast -
10   Emulate Class::Accessor::Fast behavior using Moose attributes
11
12 =head1 SYNOPSYS
13
14     package MyClass;
15     Use Moose;
16
17     with 'MooseX::Emulate::Class::Accessor::Fast';
18
19
20     #fields with readers and writers
21     __PACKAGE__->mk_accessors(qw/field1 field2/);
22     #fields with readers only
23     __PACKAGE__->mk_accessors(qw/field3 field4/);
24     #fields with writers only
25     __PACKAGE__->mk_accessors(qw/field5 field6/);
26
27
28 =head1 DESCRIPTION
29
30 This module attempts to emulate the behavior of L<Class::Accessor::Fast> as
31 accurately as possible using the Moose attribute system. The public API of
32 C<Class::Accessor::Fast> is wholly supported, but the private methods are not.
33 If you are only using the public methods (as you should) migration should be a
34 matter of switching your C<use base> line to a C<with> line.
35
36 While I have attempted to emulate the behavior of Class::Accessor::Fast as closely
37 as possible bugs may still be lurking in edge-cases.
38
39 =head1 BEHAVIOR
40
41 Simple documentation is provided here for your convenience, but for more thorough
42 documentation please see L<Class::Accessor::Fast> and L<Class::Accessor>.
43
44 =head2 A note about introspection
45
46 Please note that, at this time, the C<is> flag attribute is not being set. To
47 determine the C<reader> and C<writer> methods using introspection in later versions
48 of L<Class::MOP> ( > 0.38) please use the C<get_read_method> and C<get_write_method>
49 methods in L<Class::MOP::Attribute>. Example
50
51     # with Class::MOP <= 0.38
52     my $attr = $self->meta->find_attribute_by_name($field_name);
53     my $reader_method = $attr->reader || $attr->accessor;
54     my $writer_method = $attr->writer || $attr->accessor;
55
56     # with Class::MOP > 0.38
57     my $attr = $self->meta->find_attribute_by_name($field_name);
58     my $reader_method = $attr->get_read_method;
59     my $writer_method = $attr->get_write_method;
60
61 =head1 METHODS
62
63 =head2 new %args
64
65 Extend the default Moose constructor to emulate the behavior of C::A::F and
66 store arguments in the instance hashref.
67
68 =cut
69
70 around new => sub{
71   my $orig = shift;
72   my $class = shift;
73   my %args;
74   if (scalar @_ == 1 && defined $_[0] && ref($_[0]) eq 'HASH') {
75     %args = %{$_[0]};
76   } else {
77     %args = @_;
78   }
79   my $self = $class->$orig(@_);
80   my @extra = grep { !exists($self->{$_}) } keys %args;
81   @{$self}{@extra} = @args{@extra};
82   return $self;
83 };
84
85 =head2 mk_accessors @field_names
86
87 Create read-write accessors. An attribute named C<$field_name> will be created.
88 The name of the c<reader> and C<writer> methods will be determined by the return
89 value of C<accessor_name_for> and C<mutator_name_for>, which by default return the
90 name passed unchanged. If the accessor and mutator names are equal the C<accessor>
91 attribute will be passes to Moose, otherwise the C<reader> and C<writer> attributes
92 will be passed. Please see L<Class::MOP::Attribute> for more information.
93
94 =cut
95
96 sub mk_accessors{
97   my $self = shift;
98   my $meta = $self->meta;
99   for my $attr_name (@_){
100     my $reader = $self->accessor_name_for($attr_name);
101     my $writer = $self->mutator_name_for( $attr_name);
102     #dont overwrite existing methods
103     my @opts = $reader eq $writer ?
104       ( $self->can($reader) ? () : (accessor => $reader) ) :
105         (
106          ( $self->can($reader) ? () : (reader => $reader) ),
107          ( $self->can($writer) ? () : (writer => $writer) ),
108         );
109     $meta->add_attribute($attr_name, @opts);
110
111     $meta->add_method("_${attr_name}_accessor", $self->can($reader) )
112       if($reader eq $attr_name && !$self->can("_${attr_name}_accessor") );
113   }
114 }
115
116 =head2 mk_ro_accessors @field_names
117
118 Create read-only accessors.
119
120 =cut
121
122 sub mk_ro_accessors{
123   my $self = shift;
124   my $meta = $self->meta;
125   for my $attr_name (@_){
126     my $reader = $self->accessor_name_for($attr_name);
127     $meta->add_attribute($attr_name,
128                          $self->can($reader) ? () : (reader => $reader) );
129     $meta->add_method("_${attr_name}_accessor", $meta->find_method_by_name($reader))
130       if($reader eq $attr_name && !$self->can("_${attr_name}_accessor") );
131   }
132 }
133
134 =head2 mk_ro_accessors @field_names
135
136 Create write-only accessors.
137
138 =cut
139
140 #this is retarded.. but we need it for compatibility or whatever.
141 sub mk_wo_accessors{
142   my $self = shift;
143   my $meta = $self->meta;
144   for my $attr_name (@_){
145     my $writer = $self->mutator_name_for($attr_name);
146     $meta->add_attribute($attr_name, $self->can($writer) ? () : (writer => $writer) );
147     $meta->add_method("_${attr_name}_accessor", $meta->find_method_by_name($writer))
148       if($writer eq $attr_name && !$self->can("_${attr_name}_accessor") );
149   }
150 }
151
152 =head2 follow_best_practices
153
154 Preface readers with 'get_' and writers with 'set_'.
155 See original L<Class::Accessor> documentation for more information.
156
157 =cut
158
159 sub follow_best_practice{
160   my $self = shift;
161   my $meta = $self->meta;
162
163   $meta->remove_method('mutator_name_for');
164   $meta->remove_method('accessor_name_for');
165   $meta->add_method('mutator_name_for',  sub{ return "set_".$_[1] });
166   $meta->add_method('accessor_name_for', sub{ return "get_".$_[1] });
167 }
168
169 =head2 mutator_name_for
170
171 =head2 accessor_name_for
172
173 See original L<Class::Accessor> documentation for more information.
174
175 =cut
176
177 sub mutator_name_for{  return $_[1] }
178 sub accessor_name_for{ return $_[1] }
179
180 =head2 set
181
182 See original L<Class::Accessor> documentation for more information.
183
184 =cut
185
186 sub set{
187   my $self = shift;
188   my $k = shift;
189   confess "Wrong number of arguments received" unless scalar @_;
190
191   #my $writer = $self->mutator_name_for( $k );
192   confess "No such attribute  '$k'"
193     unless ( my $attr = $self->meta->find_attribute_by_name($k) );
194   my $writer = $attr->writer || $attr->accessor;
195   $self->$writer(@_ > 1 ? [@_] : @_);
196 }
197
198 =head2 get
199
200 See original L<Class::Accessor> documentation for more information.
201
202 =cut
203
204 sub get{
205   my $self = shift;
206   confess "Wrong number of arguments received" unless scalar @_;
207
208   my @values;
209   #while( my $attr = $self->meta->find_attribute_by_name( shift(@_) ){
210   for( @_ ){
211     confess "No such attribute  '$_'"
212       unless ( my $attr = $self->meta->find_attribute_by_name($_) );
213     my $reader = $attr->reader || $attr->accessor;
214     @_ > 1 ? push(@values, $self->$reader) : return $self->$reader;
215   }
216
217   return @values;
218 }
219
220 1;
221
222 =head2 meta
223
224 See L<Moose::Meta::Class>.
225
226 =cut
227
228 =head1 SEE ALSO
229
230 L<Moose>, L<Moose::Meta::Attribute>, L<Class::Accessor>, L<Class::Accessor::Fast>,
231 L<Class::MOP::Attribute>, L<MooseX::Adopt::Class::Accessor::Fast>
232
233 =head1 AUTHOR
234
235 Guillermo Roditi (groditi) <groditi@cpan.org>
236
237 =head1 LICENSE
238
239 You may distribute this code under the same terms as Perl itself.
240
241 =cut