make inlining a bit more easily extensible
[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 $self = shift;
95     my $attr = $self->associated_attribute;
96
97     return sub {
98         if (@_ >= 2) {
99             $attr->set_value($_[0], $_[1]);
100         }
101         $attr->get_value($_[0]);
102     };
103 }
104
105 sub _generate_accessor_method_inline {
106     my $self = shift;
107     my $attr = $self->associated_attribute;
108
109     return try {
110         $self->_compile_code([
111             'sub {',
112                 'if (@_ >= 2) {',
113                     $attr->_inline_set_value('$_[0]', '$_[1]'),
114                 '}',
115                 $attr->_inline_get_value('$_[0]'),
116             '}',
117         ]);
118     }
119     catch {
120         confess "Could not generate inline accessor because : $_";
121     };
122 }
123
124 sub _generate_reader_method {
125     my $self = shift;
126     my $attr = $self->associated_attribute;
127
128     return sub {
129         confess "Cannot assign a value to a read-only accessor"
130             if @_ > 1;
131         $attr->get_value($_[0]);
132     };
133 }
134
135 sub _generate_reader_method_inline {
136     my $self = shift;
137     my $attr = $self->associated_attribute;
138
139     return try {
140         $self->_compile_code([
141             'sub {',
142                 'confess "Cannot assign a value to a read-only accessor"',
143                     'if @_ > 1;',
144                 $attr->_inline_get_value('$_[0]'),
145             '}',
146         ]);
147     }
148     catch {
149         confess "Could not generate inline reader because : $_";
150     };
151 }
152
153 sub _generate_writer_method {
154     my $self = shift;
155     my $attr = $self->associated_attribute;
156
157     return sub {
158         $attr->set_value($_[0], $_[1]);
159     };
160 }
161
162 sub _generate_writer_method_inline {
163     my $self = shift;
164     my $attr = $self->associated_attribute;
165
166     return try {
167         $self->_compile_code([
168             'sub {',
169                 $attr->_inline_set_value('$_[0]', '$_[1]'),
170             '}',
171         ]);
172     }
173     catch {
174         confess "Could not generate inline writer because : $_";
175     };
176 }
177
178 sub _generate_predicate_method {
179     my $self = shift;
180     my $attr = $self->associated_attribute;
181
182     return sub {
183         $attr->has_value($_[0])
184     };
185 }
186
187 sub _generate_predicate_method_inline {
188     my $self = shift;
189     my $attr = $self->associated_attribute;
190
191     return try {
192         $self->_compile_code([
193             'sub {',
194                 $attr->_inline_has_value('$_[0]'),
195             '}',
196         ]);
197     }
198     catch {
199         confess "Could not generate inline predicate because : $_";
200     };
201 }
202
203 sub _generate_clearer_method {
204     my $self = shift;
205     my $attr = $self->associated_attribute;
206
207     return sub {
208         $attr->clear_value($_[0])
209     };
210 }
211
212 sub _generate_clearer_method_inline {
213     my $self = shift;
214     my $attr = $self->associated_attribute;
215
216     return try {
217         $self->_compile_code([
218             'sub {',
219                 $attr->_inline_clear_value('$_[0]'),
220             '}',
221         ]);
222     }
223     catch {
224         confess "Could not generate inline clearer because : $_";
225     };
226 }
227
228 1;
229
230 __END__
231
232 =pod
233
234 =head1 NAME
235
236 Class::MOP::Method::Accessor - Method Meta Object for accessors
237
238 =head1 SYNOPSIS
239
240     use Class::MOP::Method::Accessor;
241
242     my $reader = Class::MOP::Method::Accessor->new(
243         attribute     => $attribute,
244         is_inline     => 1,
245         accessor_type => 'reader',
246     );
247
248     $reader->body->execute($instance); # call the reader method
249
250 =head1 DESCRIPTION
251
252 This is a subclass of C<Class::MOP::Method> which is used by
253 C<Class::MOP::Attribute> to generate accessor code. It handles
254 generation of readers, writers, predicates and clearers. For each type
255 of method, it can either create a subroutine reference, or actually
256 inline code by generating a string and C<eval>'ing it.
257
258 =head1 METHODS
259
260 =over 4
261
262 =item B<< Class::MOP::Method::Accessor->new(%options) >>
263
264 This returns a new C<Class::MOP::Method::Accessor> based on the
265 C<%options> provided.
266
267 =over 4
268
269 =item * attribute
270
271 This is the C<Class::MOP::Attribute> for which accessors are being
272 generated. This option is required.
273
274 =item * accessor_type
275
276 This is a string which should be one of "reader", "writer",
277 "accessor", "predicate", or "clearer". This is the type of method
278 being generated. This option is required.
279
280 =item * is_inline
281
282 This indicates whether or not the accessor should be inlined. This
283 defaults to false.
284
285 =item * name
286
287 The method name (without a package name). This is required.
288
289 =item * package_name
290
291 The package name for the method. This is required.
292
293 =back
294
295 =item B<< $metamethod->accessor_type >>
296
297 Returns the accessor type which was passed to C<new>.
298
299 =item B<< $metamethod->is_inline >>
300
301 Returns a boolean indicating whether or not the accessor is inlined.
302
303 =item B<< $metamethod->associated_attribute >>
304
305 This returns the L<Class::MOP::Attribute> object which was passed to
306 C<new>.
307
308 =item B<< $metamethod->body >>
309
310 The method itself is I<generated> when the accessor object is
311 constructed.
312
313 =back
314
315 =head1 AUTHORS
316
317 Stevan Little E<lt>stevan@iinteractive.comE<gt>
318
319 =head1 COPYRIGHT AND LICENSE
320
321 Copyright 2006-2010 by Infinity Interactive, Inc.
322
323 L<http://www.iinteractive.com>
324
325 This library is free software; you can redistribute it and/or modify
326 it under the same terms as Perl itself.
327
328 =cut
329