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