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