Made the delegation closure have useful error trace information.
[gitmo/Moose.git] / lib / Moose / Meta / Method / Delegation.pm
CommitLineData
a05f85c1 1
2package Moose::Meta::Method::Delegation;
3
4use strict;
5use warnings;
6
7use Carp 'confess';
8use Scalar::Util 'blessed', 'weaken';
9
870d0f1a 10our $VERSION = '0.94';
a05f85c1 11$VERSION = eval $VERSION;
12our $AUTHORITY = 'cpan:STEVAN';
13
01164604 14use base 'Moose::Meta::Method',
8aeb5bfd 15 'Class::MOP::Method::Inlined';
a05f85c1 16
17
18sub new {
19 my $class = shift;
20 my %options = @_;
21
46f7e6a5 22 ( exists $options{attribute} )
a05f85c1 23 || confess "You must supply an attribute to construct with";
24
46f7e6a5 25 ( blessed( $options{attribute} )
26 && $options{attribute}->isa('Moose::Meta::Attribute') )
27 || confess
28 "You must supply an attribute which is a 'Moose::Meta::Attribute' instance";
a05f85c1 29
46f7e6a5 30 ( $options{package_name} && $options{name} )
31 || confess
32 "You must supply the package_name and name parameters $Class::MOP::Method::UPGRADE_ERROR_TEXT";
a05f85c1 33
46f7e6a5 34 ( $options{delegate_to_method} && ( !ref $options{delegate_to_method} )
35 || ( 'CODE' eq ref $options{delegate_to_method} ) )
36 || confess
37 'You must supply a delegate_to_method which is a method name or a CODE reference';
a05f85c1 38
4adb0e7b 39 exists $options{curried_arguments}
40 || ( $options{curried_arguments} = [] );
2de18801 41
4adb0e7b 42 ( $options{curried_arguments} &&
43 ( 'ARRAY' eq ref $options{curried_arguments} ) )
44 || confess 'You must supply a curried_arguments which is an ARRAY reference';
2de18801 45
46f7e6a5 46 my $self = $class->_new( \%options );
47
48 weaken( $self->{'attribute'} );
49
50 $self->_initialize_body;
a05f85c1 51
52 return $self;
53}
54
01cd78f8 55sub _new {
56 my $class = shift;
57 my $options = @_ == 1 ? $_[0] : {@_};
58
59 return bless $options, $class;
60}
61
2de18801 62sub curried_arguments { (shift)->{'curried_arguments'} }
63
01cd78f8 64sub associated_attribute { (shift)->{'attribute'} }
65
46f7e6a5 66sub delegate_to_method { (shift)->{'delegate_to_method'} }
67
8aeb5bfd 68sub is_inline {
69 (shift)->{is_inline}
70}
71
72sub definition_context {
73 exists $_[0]->{definition_context} ? $_[0]->{definition_context}
74 : ($_[0]->{definition_context} = $_[0]->_generate_definition_context);
75}
76
77sub _generate_definition_context {
78 my $self = shift;
79 my $attr = $self->associated_attribute;
80 my $ctx = $attr->definition_context;
81 return unless $ctx;
82
83 my $desc = "delegation of "
84 . $self->name
85 . ' to '
86 . $attr->name
87 . '->'
88 . $self->delegate_to_method;
89
90 return { %$ctx, description => $desc };
91}
92
46f7e6a5 93sub _initialize_body {
94 my $self = shift;
95
96 my $method_to_call = $self->delegate_to_method;
97 return $self->{body} = $method_to_call
98 if ref $method_to_call;
99
8aeb5bfd 100 return $self->{body} = $self->is_inline
101 ? $self->_generate_body_inline
102 : $self->_generate_body;
103}
104
105sub _generate_body_inline {
106 my $self = shift;
107 my $method_to_call = $self->delegate_to_method;
108 my $attr = $self->associated_attribute;
109 my $attr_name = $attr->name;
110 my $meta_instance = $attr->associated_class->instance_metaclass;
111 my $handle_name = $self->name;
112
113 my ( $code, $e ) = $self->_compile_code(
114 environment => {
115 '@curried_arguments' => $self->curried_arguments,
116 '$method' => \$self,
117 },
118 code => (
119 'sub {'."\n"
120 . 'my $instance = shift; '."\n"
121 . 'my $proxy = '
122 . $meta_instance->inline_get_slot_value('$instance',$attr_name)
123 . ';'."\n"
124 . 'my $error '."\n"
125 . ' = !defined $proxy ? q{ is not defined} '."\n"
126 . q{ : ref($proxy) && !Scalar::Util::blessed($proxy) ? qq{ is not an object (got '$proxy')} }."\n"
127 . ' : undef;'."\n"
128 . 'if ($error) {'."\n"
129 . ' $method->throw_error('."\n"
130 . ' "Cannot delegate '. $handle_name.' to '.$method_to_call
131 . ' because the value of '
132 . $attr_name
133 . '" . $error, '."\n"
134 . ' method_name => q{'.$method_to_call.'}, '."\n"
135 . ' object => $instance, '."\n"
136 . ');}'."\n"
137 . ($self->curried_arguments
138 ? 'unshift @_, @{curried_arguments};'."\n"
139 : '')
140 . '$proxy->'.$method_to_call.'(@_);'."\n"
141 . '};'
142 ),
143 );
144 confess "Could not generate inline accessor because : $e" if $e;
145
146 return $code;
147}
148
149sub _generate_body {
150 my $self = shift;
151
152 my $method_to_call = $self->delegate_to_method;
153
46f7e6a5 154 my $accessor = $self->_get_delegate_accessor;
155
156 my $handle_name = $self->name;
157
158 # NOTE: we used to do a goto here, but the goto didn't handle
159 # failure correctly (it just returned nothing), so I took that
160 # out. However, the more I thought about it, the less I liked it
16f00af3 161 # doing the goto, and I preferred the act of delegation being
46f7e6a5 162 # actually represented in the stack trace. - SL
5120f8ff 163 # not inlining this, since it won't really speed things up at
164 # all... the only thing that would end up different would be
165 # interpolating in $method_to_call, and a bunch of things in the
166 # error handling that mostly never gets called - doy
8aeb5bfd 167 return sub {
46f7e6a5 168 my $instance = shift;
169 my $proxy = $instance->$accessor();
6148c167 170
171 my $error
2cc6f982 172 = !defined $proxy ? ' is not defined'
173 : ref($proxy) && !blessed($proxy) ? qq{ is not an object (got '$proxy')}
174 : undef;
6148c167 175
176 if ($error) {
177 $self->throw_error(
178 "Cannot delegate $handle_name to $method_to_call because "
179 . "the value of "
180 . $self->associated_attribute->name
181 . $error,
182 method_name => $method_to_call,
183 object => $instance
46f7e6a5 184 );
6148c167 185 }
c279b82f 186 unshift @_, @{ $self->curried_arguments };
187 $proxy->$method_to_call(@_);
46f7e6a5 188 };
189}
190
191sub _get_delegate_accessor {
192 my $self = shift;
53b3e214 193 my $attr = $self->associated_attribute;
194
195 # NOTE:
196 # always use a named method when
197 # possible, if you use the method
198 # ref and there are modifiers on
199 # the accessors then it will not
200 # pick up the modifiers too. Only
201 # the named method will assure that
202 # we also have any modifiers run.
203 # - SL
204 my $accessor = $attr->has_read_method
205 ? $attr->get_read_method
206 : $attr->get_read_method_ref;
207
208 $accessor = $accessor->body if Scalar::Util::blessed $accessor;
46f7e6a5 209
210 return $accessor;
211}
212
a05f85c1 2131;
01cd78f8 214
215__END__
216
217=pod
218
219=head1 NAME
220
221Moose::Meta::Method::Delegation - A Moose Method metaclass for delegation methods
222
223=head1 DESCRIPTION
224
225This is a subclass of L<Moose::Meta::Method> for delegation
226methods.
227
228=head1 METHODS
229
230=over 4
231
e6fb6ad2 232=item B<< Moose::Meta::Method::Delegation->new(%options) >>
01cd78f8 233
e6fb6ad2 234This creates the delegation methods based on the provided C<%options>.
01cd78f8 235
236=over 4
237
238=item I<attribute>
239
240This must be an instance of C<Moose::Meta::Attribute> which this
e6fb6ad2 241accessor is being generated for. This options is B<required>.
01cd78f8 242
46f7e6a5 243=item I<delegate_to_method>
244
245The method in the associated attribute's value to which we
246delegate. This can be either a method name or a code reference.
247
2de18801 248=item I<curried_arguments>
249
250An array reference of arguments that will be prepended to the argument list for
251any call to the delegating method.
252
01cd78f8 253=back
254
e6fb6ad2 255=item B<< $metamethod->associated_attribute >>
01cd78f8 256
257Returns the attribute associated with this method.
258
2de18801 259=item B<< $metamethod->curried_arguments >>
260
261Return any curried arguments that will be passed to the delegated method.
262
e6fb6ad2 263=item B<< $metamethod->delegate_to_method >>
46f7e6a5 264
e6fb6ad2 265Returns the method to which this method delegates, as passed to the
266constructor.
46f7e6a5 267
01cd78f8 268=back
269
270=head1 BUGS
271
d4048ef3 272See L<Moose/BUGS> for details on reporting bugs.
01cd78f8 273
274=head1 AUTHOR
275
276Dave Rolsky E<lt>autarch@urth.orgE<gt>
277
278=head1 COPYRIGHT AND LICENSE
279
2840a3b2 280Copyright 2009 by Infinity Interactive, Inc.
01cd78f8 281
282L<http://www.iinteractive.com>
283
284This library is free software; you can redistribute it and/or modify
285it under the same terms as Perl itself.
286
287=cut