Version 1.12
[gitmo/Class-MOP.git] / lib / Class / MOP / Method / Accessor.pm
1
2 package Class::MOP::Method::Accessor;
3
4 use strict;
5 use warnings;
6
7 use Carp         'confess';
8 use Scalar::Util 'blessed', 'weaken';
9
10 our $VERSION   = '1.12';
11 $VERSION = eval $VERSION;
12 our $AUTHORITY = 'cpan:STEVAN';
13
14 use base 'Class::MOP::Method::Generated';
15
16 sub new {
17     my $class   = shift;
18     my %options = @_;
19
20     (exists $options{attribute})
21         || confess "You must supply an attribute to construct with";
22
23     (exists $options{accessor_type})
24         || confess "You must supply an accessor_type to construct with";
25
26     (blessed($options{attribute}) && $options{attribute}->isa('Class::MOP::Attribute'))
27         || confess "You must supply an attribute which is a 'Class::MOP::Attribute' instance";
28
29     ($options{package_name} && $options{name})
30         || confess "You must supply the package_name and name parameters $Class::MOP::Method::UPGRADE_ERROR_TEXT";
31
32     my $self = $class->_new(\%options);
33
34     # we don't want this creating
35     # a cycle in the code, if not
36     # needed
37     weaken($self->{'attribute'});
38
39     $self->_initialize_body;
40
41     return $self;
42 }
43
44 sub _new {
45     my $class = shift;
46
47     return Class::MOP::Class->initialize($class)->new_object(@_)
48         if $class ne __PACKAGE__;
49
50     my $params = @_ == 1 ? $_[0] : {@_};
51
52     return bless {
53         # inherited from Class::MOP::Method
54         body                 => $params->{body},
55         associated_metaclass => $params->{associated_metaclass},
56         package_name         => $params->{package_name},
57         name                 => $params->{name},
58         original_method      => $params->{original_method},
59
60         # inherit from Class::MOP::Generated
61         is_inline            => $params->{is_inline} || 0,
62         definition_context   => $params->{definition_context},
63
64         # defined in this class
65         attribute            => $params->{attribute},
66         accessor_type        => $params->{accessor_type},
67     } => $class;
68 }
69
70 ## accessors
71
72 sub associated_attribute { (shift)->{'attribute'}     }
73 sub accessor_type        { (shift)->{'accessor_type'} }
74
75 ## factory
76
77 sub _initialize_body {
78     my $self = shift;
79
80     my $method_name = join "_" => (
81         '_generate',
82         $self->accessor_type,
83         'method',
84         ($self->is_inline ? 'inline' : ())
85     );
86
87     $self->{'body'} = $self->$method_name();
88 }
89
90 ## generators
91
92 sub _generate_accessor_method {
93     my $attr = (shift)->associated_attribute;
94     return sub {
95         $attr->set_value($_[0], $_[1]) if scalar(@_) == 2;
96         $attr->get_value($_[0]);
97     };
98 }
99
100 sub _generate_reader_method {
101     my $attr = (shift)->associated_attribute;
102     return sub {
103         confess "Cannot assign a value to a read-only accessor" if @_ > 1;
104         $attr->get_value($_[0]);
105     };
106 }
107
108
109 sub _generate_writer_method {
110     my $attr = (shift)->associated_attribute;
111     return sub {
112         $attr->set_value($_[0], $_[1]);
113     };
114 }
115
116 sub _generate_predicate_method {
117     my $attr = (shift)->associated_attribute;
118     return sub {
119         $attr->has_value($_[0])
120     };
121 }
122
123 sub _generate_clearer_method {
124     my $attr = (shift)->associated_attribute;
125     return sub {
126         $attr->clear_value($_[0])
127     };
128 }
129
130 ## Inline methods
131
132 sub _generate_accessor_method_inline {
133     my $self = shift;
134     my $attr = $self->associated_attribute;
135
136     my ( $code, $e ) = $self->_eval_closure(
137         {},
138         'sub {'
139             . $attr->inline_set( '$_[0]', '$_[1]' )
140             . ' if scalar(@_) == 2; '
141             . $attr->inline_get('$_[0]') . '}'
142     );
143     confess "Could not generate inline accessor because : $e" if $e;
144
145     return $code;
146 }
147
148 sub _generate_reader_method_inline {
149     my $self = shift;
150     my $attr = $self->associated_attribute;
151
152     my ( $code, $e ) = $self->_eval_closure(
153         {},
154         'sub {'
155             . 'confess "Cannot assign a value to a read-only accessor" if @_ > 1;'
156             . $attr->inline_get('$_[0]') . '}'
157     );
158     confess "Could not generate inline reader because : $e" if $e;
159
160     return $code;
161 }
162
163 sub _generate_writer_method_inline {
164     my $self = shift;
165     my $attr = $self->associated_attribute;
166
167     my ( $code, $e ) = $self->_eval_closure(
168         {},
169         'sub {' . $attr->inline_set( '$_[0]', '$_[1]' ) . '}'
170     );
171     confess "Could not generate inline writer because : $e" if $e;
172
173     return $code;
174 }
175
176 sub _generate_predicate_method_inline {
177     my $self = shift;
178     my $attr = $self->associated_attribute;
179
180     my ( $code, $e ) = $self->_eval_closure(
181         {},
182         'sub {' . $attr->inline_has('$_[0]') . '}'
183     );
184     confess "Could not generate inline predicate because : $e" if $e;
185
186     return $code;
187 }
188
189 sub _generate_clearer_method_inline {
190     my $self = shift;
191     my $attr = $self->associated_attribute;
192
193     my ( $code, $e ) = $self->_eval_closure(
194         {},
195         'sub {' . $attr->inline_clear('$_[0]') . '}'
196     );
197     confess "Could not generate inline clearer because : $e" if $e;
198
199     return $code;
200 }
201
202 1;
203
204 __END__
205
206 =pod
207
208 =head1 NAME
209
210 Class::MOP::Method::Accessor - Method Meta Object for accessors
211
212 =head1 SYNOPSIS
213
214     use Class::MOP::Method::Accessor;
215
216     my $reader = Class::MOP::Method::Accessor->new(
217         attribute     => $attribute,
218         is_inline     => 1,
219         accessor_type => 'reader',
220     );
221
222     $reader->body->execute($instance); # call the reader method
223
224 =head1 DESCRIPTION
225
226 This is a subclass of C<Class::MOP::Method> which is used by
227 C<Class::MOP::Attribute> to generate accessor code. It handles
228 generation of readers, writers, predicates and clearers. For each type
229 of method, it can either create a subroutine reference, or actually
230 inline code by generating a string and C<eval>'ing it.
231
232 =head1 METHODS
233
234 =over 4
235
236 =item B<< Class::MOP::Method::Accessor->new(%options) >>
237
238 This returns a new C<Class::MOP::Method::Accessor> based on the
239 C<%options> provided.
240
241 =over 4
242
243 =item * attribute
244
245 This is the C<Class::MOP::Attribute> for which accessors are being
246 generated. This option is required.
247
248 =item * accessor_type
249
250 This is a string which should be one of "reader", "writer",
251 "accessor", "predicate", or "clearer". This is the type of method
252 being generated. This option is required.
253
254 =item * is_inline
255
256 This indicates whether or not the accessor should be inlined. This
257 defaults to false.
258
259 =item * name
260
261 The method name (without a package name). This is required.
262
263 =item * package_name
264
265 The package name for the method. This is required.
266
267 =back
268
269 =item B<< $metamethod->accessor_type >>
270
271 Returns the accessor type which was passed to C<new>.
272
273 =item B<< $metamethod->is_inline >>
274
275 Returns a boolean indicating whether or not the accessor is inlined.
276
277 =item B<< $metamethod->associated_attribute >>
278
279 This returns the L<Class::MOP::Attribute> object which was passed to
280 C<new>.
281
282 =item B<< $metamethod->body >>
283
284 The method itself is I<generated> when the accessor object is
285 constructed.
286
287 =back
288
289 =head1 AUTHORS
290
291 Stevan Little E<lt>stevan@iinteractive.comE<gt>
292
293 =head1 COPYRIGHT AND LICENSE
294
295 Copyright 2006-2010 by Infinity Interactive, Inc.
296
297 L<http://www.iinteractive.com>
298
299 This library is free software; you can redistribute it and/or modify
300 it under the same terms as Perl itself.
301
302 =cut
303