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