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