docs for MM::Method::Constructor.
[gitmo/Moose.git] / lib / Moose / Meta / Method / Constructor.pm
CommitLineData
5cf3dbcf 1
2package Moose::Meta::Method::Constructor;
3
4use strict;
5use warnings;
6
5cf3dbcf 7use Scalar::Util 'blessed', 'weaken', 'looks_like_number';
8
4b2189ce 9our $VERSION = '0.72';
5cf3dbcf 10our $AUTHORITY = 'cpan:STEVAN';
11
badb7e89 12use base 'Moose::Meta::Method',
13 'Class::MOP::Method::Generated';
5cf3dbcf 14
15sub new {
16 my $class = shift;
17 my %options = @_;
7a5b07b3 18
3e504337 19 my $meta = $options{metaclass};
20
21 (ref $options{options} eq 'HASH')
a9538ac9 22 || $class->throw_error("You must pass a hash of options", data => $options{options});
7a5b07b3 23
1b2aea39 24 ($options{package_name} && $options{name})
a9538ac9 25 || $class->throw_error("You must supply the package_name and name parameters $Class::MOP::Method::UPGRADE_ERROR_TEXT");
1b2aea39 26
5cf3dbcf 27 my $self = bless {
28 # from our superclass
e606ae5f 29 'body' => undef,
30 'package_name' => $options{package_name},
31 'name' => $options{name},
5cf3dbcf 32 # specific to this subclass
e606ae5f 33 'options' => $options{options},
34 'meta_instance' => $meta->get_meta_instance,
35 'attributes' => [ $meta->compute_all_applicable_attributes ],
5cf3dbcf 36 # ...
e606ae5f 37 'associated_metaclass' => $meta,
5cf3dbcf 38 } => $class;
39
7a5b07b3 40 # we don't want this creating
41 # a cycle in the code, if not
5cf3dbcf 42 # needed
e606ae5f 43 weaken($self->{'associated_metaclass'});
5cf3dbcf 44
415e6f85 45 $self->initialize_body;
5cf3dbcf 46
7a5b07b3 47 return $self;
5cf3dbcf 48}
49
308e04fa 50sub can_be_inlined {
51 my $self = shift;
52 my $metaclass = $self->associated_metaclass;
53
5d826cfa 54 my $expected_class = $self->_expected_constructor_class;
55
12875d6e 56 # If any of our parents have been made immutable, we are okay to
e4c7477b 57 # inline our own new method. The assumption is that an inlined new
58 # method provided by a parent does not actually get used by
59 # children anyway.
60 for my $meta (
61 grep { $_->is_immutable }
62 map { ( ref $metaclass )->initialize($_) }
5d826cfa 63 grep { $_ ne $expected_class }
e4c7477b 64 $metaclass->linearized_isa
65 ) {
12875d6e 66 my $transformer = $meta->get_immutable_transformer;
67
5d826cfa 68 # This is actually a false positive if we're in a subclass of
69 # this class, _and_ the expected class is not overridden (but
70 # should be), and the real expected class is actually
71 # immutable itself (see Fey::Object::Table for an example of
72 # how this can happen). I'm not sure how to actually handle
73 # that case, since it's effectively a bug in the subclass (for
74 # not overriding _expected_constructor_class).
e4c7477b 75 return 1 if $transformer->inlined_constructor;
12875d6e 76 }
77
308e04fa 78 if ( my $constructor = $metaclass->find_method_by_name( $self->name ) ) {
e4c7477b 79 my $class = $self->associated_metaclass->name;
308e04fa 80
81 if ( $constructor->body != $expected_class->can('new') ) {
81766020 82 my $warning
83 = "Not inlining a constructor for $class since it is not"
37236439 84 . " inheriting the default $expected_class constructor\n"
85 . "If you are certain you don't need to inline your"
86 . " constructor, specify inline_constructor => 0 in your"
87 . " call to $class->meta->make_immutable\n";
308e04fa 88
81766020 89 $warning .= " (constructor has method modifiers which would be lost if it were inlined)\n"
90 if $constructor->isa('Class::MOP::Method::Wrapped');
91
92 warn $warning;
93
308e04fa 94 return 0;
95 }
96 else {
97 return 1;
98 }
99 }
100
101 # This would be a rather weird case where we have no constructor
102 # in the inheritance chain.
103 return 1;
104}
105
106# This is here so can_be_inlined can be inherited by MooseX modules.
107sub _expected_constructor_class {
108 return 'Moose::Object';
109}
110
7a5b07b3 111## accessors
5cf3dbcf 112
e606ae5f 113sub meta_instance { (shift)->{'meta_instance'} }
114sub attributes { (shift)->{'attributes'} }
5cf3dbcf 115
5cf3dbcf 116## method
117
384c126d 118# this was changed in 0.41, but broke MooseX::Singleton, so try to catch
119# any other code using the original broken spelling
70ea9161 120sub intialize_body { $_[0]->throw_error("Please correct the spelling of 'intialize_body' to 'initialize_body'") }
384c126d 121
ac070e13 122sub _generate_params {
123 my ($self, $var, $class_var) = @_;
124 "my $var = " . $self->_generate_BUILDARGS($class_var, '@_') . ";\n";
125}
126
127sub _generate_instance {
128 my ($self, $var, $class_var) = @_;
129 "my $var = " . $self->meta_instance->inline_create_instance($class_var)
130 . ";\n";
131}
132
133sub _generate_slot_initializers {
134 my ($self) = @_;
135 return (join ";\n" => map {
136 $self->_generate_slot_initializer($_)
137 } 0 .. (@{$self->attributes} - 1)) . ";\n";
138}
139
415e6f85 140sub initialize_body {
5cf3dbcf 141 my $self = shift;
142 # TODO:
7a5b07b3 143 # the %options should also include a both
144 # a call 'initializer' and call 'SUPER::'
145 # options, which should cover approx 90%
146 # of the possible use cases (even if it
147 # requires some adaption on the part of
5cf3dbcf 148 # the author, after all, nothing is free)
149 my $source = 'sub {';
1f779926 150 $source .= "\n" . 'my $class = shift;';
7a5b07b3 151
587ae0d2 152 $source .= "\n" . 'return $class->Moose::Object::new(@_)';
ac070e13 153 $source .= "\n if \$class ne '" . $self->associated_metaclass->name
154 . "';\n";
93e98578 155
ac070e13 156 $source .= $self->_generate_params('$params', '$class');
157 $source .= $self->_generate_instance('$instance', '$class');
158 $source .= $self->_generate_slot_initializers;
7a5b07b3 159
ac070e13 160 $source .= $self->_generate_triggers();
5cf3dbcf 161 $source .= ";\n" . $self->_generate_BUILDALL();
7a5b07b3 162
ac070e13 163 $source .= ";\nreturn \$instance";
7a5b07b3 164 $source .= ";\n" . '}';
165 warn $source if $self->options->{debug};
166
5442a061 167 # We need to check if the attribute ->can('type_constraint')
168 # since we may be trying to immutabilize a Moose meta class,
169 # which in turn has attributes which are Class::MOP::Attribute
170 # objects, rather than Moose::Meta::Attribute. And
171 # Class::MOP::Attribute attributes have no type constraints.
172 # However we need to make sure we leave an undef value there
173 # because the inlined code is using the index of the attributes
174 # to determine where to find the type constraint
175
176 my $attrs = $self->attributes;
177
178 my @type_constraints = map {
179 $_->can('type_constraint') ? $_->type_constraint : undef
180 } @$attrs;
181
182 my @type_constraint_bodies = map {
183 defined $_ ? $_->_compiled_type_constraint : undef;
184 } @type_constraints;
185
186 my $code = $self->_compile_code(
187 code => $source,
188 environment => {
189 '$meta' => \$self,
190 '$attrs' => \$attrs,
191 '@type_constraints' => \@type_constraints,
192 '@type_constraint_bodies' => \@type_constraint_bodies,
193 },
194 ) or $self->throw_error("Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@", error => $@, data => $source );
a62dcd43 195
e606ae5f 196 $self->{'body'} = $code;
197}
198
199sub _generate_BUILDARGS {
200 my ( $self, $class, $args ) = @_;
201
202 my $buildargs = $self->associated_metaclass->find_method_by_name("BUILDARGS");
203
204 if ( $args eq '@_' and ( !$buildargs or $buildargs->body == \&Moose::Object::BUILDARGS ) ) {
205 return join("\n",
206 'do {',
207 $self->_inline_throw_error('"Single parameters to new() must be a HASH ref"', 'data => $_[0]'),
a62dcd43 208 ' if scalar @_ == 1 && !( defined $_[0] && ref $_[0] eq q{HASH} );',
e606ae5f 209 '(scalar @_ == 1) ? {%{$_[0]}} : {@_};',
210 '}',
211 );
212 } else {
213 return $class . "->BUILDARGS($args)";
214 }
5cf3dbcf 215}
216
217sub _generate_BUILDALL {
218 my $self = shift;
219 my @BUILD_calls;
1f779926 220 foreach my $method (reverse $self->associated_metaclass->find_all_methods_by_name('BUILD')) {
e606ae5f 221 push @BUILD_calls => '$instance->' . $method->{class} . '::BUILD($params)';
5cf3dbcf 222 }
7a5b07b3 223 return join ";\n" => @BUILD_calls;
5cf3dbcf 224}
225
1b55c340 226sub _generate_triggers {
227 my $self = shift;
228 my @trigger_calls;
708b4070 229 foreach my $i ( 0 .. $#{ $self->attributes } ) {
1b55c340 230 my $attr = $self->attributes->[$i];
708b4070 231
232 next unless $attr->can('has_trigger') && $attr->has_trigger;
233
234 my $init_arg = $attr->init_arg;
235
236 next unless defined $init_arg;
237
238 push @trigger_calls => '(exists $params->{\''
239 . $init_arg
240 . '\'}) && do {'
241 . "\n "
242 . '$attrs->['
243 . $i
244 . ']->trigger->('
245 . '$instance, '
246 . $self->meta_instance->inline_get_slot_value(
247 '$instance',
248 $attr->name,
249 )
250 . ', '
251 . '$attrs->['
252 . $i . ']' . ');' . "\n}";
1b55c340 253 }
708b4070 254
255 return join ";\n" => @trigger_calls;
1b55c340 256}
257
5cf3dbcf 258sub _generate_slot_initializer {
259 my $self = shift;
260 my $index = shift;
7a5b07b3 261
5cf3dbcf 262 my $attr = $self->attributes->[$index];
7a5b07b3 263
5cf3dbcf 264 my @source = ('## ' . $attr->name);
d66bea3c 265
266 my $is_moose = $attr->isa('Moose::Meta::Attribute'); # XXX FIXME
7a5b07b3 267
84981146 268 if ($is_moose && defined($attr->init_arg) && $attr->is_required && !$attr->has_default && !$attr->has_builder) {
e606ae5f 269 push @source => ('(exists $params->{\'' . $attr->init_arg . '\'}) ' .
3e504337 270 '|| ' . $self->_inline_throw_error('"Attribute (' . $attr->name . ') is required"') .';');
5cf3dbcf 271 }
7a5b07b3 272
ca168e89 273 if (($attr->has_default || $attr->has_builder) && !($is_moose && $attr->is_lazy)) {
7a5b07b3 274
84981146 275 if ( defined( my $init_arg = $attr->init_arg ) ) {
e606ae5f 276 push @source => 'if (exists $params->{\'' . $init_arg . '\'}) {';
277 push @source => ('my $val = $params->{\'' . $init_arg . '\'};');
278 push @source => $self->_generate_type_constraint_and_coercion($attr, $index)
279 if $is_moose;
280 push @source => $self->_generate_slot_assignment($attr, '$val', $index);
84981146 281 push @source => "} else {";
282 }
ca168e89 283 my $default;
97e11ef5 284 if ( $attr->has_default ) {
ca168e89 285 $default = $self->_generate_default_value($attr, $index);
97e11ef5 286 }
287 else {
ca168e89 288 my $builder = $attr->builder;
289 $default = '$instance->' . $builder;
290 }
688fcdda 291
3db3ea82 292 push @source => '{'; # wrap this to avoid my $val overwrite warnings
5cf3dbcf 293 push @source => ('my $val = ' . $default . ';');
e606ae5f 294 push @source => $self->_generate_type_constraint_and_coercion($attr, $index)
295 if $is_moose;
51c107ef 296 push @source => $self->_generate_slot_assignment($attr, '$val', $index);
bad76b8e 297 push @source => '}'; # close - wrap this to avoid my $val overrite warnings
7a5b07b3 298
84981146 299 push @source => "}" if defined $attr->init_arg;
7a5b07b3 300 }
84981146 301 elsif ( defined( my $init_arg = $attr->init_arg ) ) {
e606ae5f 302 push @source => '(exists $params->{\'' . $init_arg . '\'}) && do {';
8ecb1fa0 303
e606ae5f 304 push @source => ('my $val = $params->{\'' . $init_arg . '\'};');
d66bea3c 305 if ($is_moose && $attr->has_type_constraint) {
7a5b07b3 306 if ($attr->should_coerce && $attr->type_constraint->has_coercion) {
688fcdda 307 push @source => $self->_generate_type_coercion(
308 $attr,
309 '$type_constraints[' . $index . ']',
310 '$val',
311 '$val'
312 );
8ecb1fa0 313 }
688fcdda 314 push @source => $self->_generate_type_constraint_check(
315 $attr,
316 '$type_constraint_bodies[' . $index . ']',
317 '$type_constraints[' . $index . ']',
318 '$val'
319 );
8ecb1fa0 320 }
9df136d0 321 push @source => $self->_generate_slot_assignment($attr, '$val', $index);
7a5b07b3 322
323 push @source => "}";
5cf3dbcf 324 }
7a5b07b3 325
5cf3dbcf 326 return join "\n" => @source;
327}
328
329sub _generate_slot_assignment {
9df136d0 330 my ($self, $attr, $value, $index) = @_;
331
332 my $source;
333
334 if ($attr->has_initializer) {
335 $source = (
336 '$attrs->[' . $index . ']->set_initial_value($instance, ' . $value . ');'
337 );
338 }
339 else {
340 $source = (
341 $self->meta_instance->inline_set_slot_value(
342 '$instance',
eae37c67 343 $attr->name,
9df136d0 344 $value
345 ) . ';'
346 );
347 }
348
349 my $is_moose = $attr->isa('Moose::Meta::Attribute'); # XXX FIXME
7a5b07b3 350
d66bea3c 351 if ($is_moose && $attr->is_weak_ref) {
5cf3dbcf 352 $source .= (
353 "\n" .
354 $self->meta_instance->inline_weaken_slot_value(
7a5b07b3 355 '$instance',
eae37c67 356 $attr->name
7a5b07b3 357 ) .
5cf3dbcf 358 ' if ref ' . $value . ';'
7a5b07b3 359 );
360 }
361
5cf3dbcf 362 return $source;
363}
364
e606ae5f 365sub _generate_type_constraint_and_coercion {
366 my ($self, $attr, $index) = @_;
367
368 return unless $attr->has_type_constraint;
369
370 my @source;
371 if ($attr->should_coerce && $attr->type_constraint->has_coercion) {
372 push @source => $self->_generate_type_coercion(
373 $attr,
374 '$type_constraints[' . $index . ']',
375 '$val',
376 '$val'
377 );
378 }
379 push @source => $self->_generate_type_constraint_check(
380 $attr,
381 ('$type_constraint_bodies[' . $index . ']'),
382 ('$type_constraints[' . $index . ']'),
383 '$val'
384 );
385 return @source;
386}
387
5cf3dbcf 388sub _generate_type_coercion {
389 my ($self, $attr, $type_constraint_name, $value_name, $return_value_name) = @_;
390 return ($return_value_name . ' = ' . $type_constraint_name . '->coerce(' . $value_name . ');');
391}
392
393sub _generate_type_constraint_check {
688fcdda 394 my ($self, $attr, $type_constraint_cv, $type_constraint_obj, $value_name) = @_;
5cf3dbcf 395 return (
3e504337 396 $self->_inline_throw_error('"Attribute (' # FIXME add 'dad'
688fcdda 397 . $attr->name
398 . ') does not pass the type constraint because: " . '
3e504337 399 . $type_constraint_obj . '->get_message(' . $value_name . ')')
400 . "\n\t unless " . $type_constraint_cv . '->(' . $value_name . ');'
7a5b07b3 401 );
5cf3dbcf 402}
403
404sub _generate_default_value {
405 my ($self, $attr, $index) = @_;
406 # NOTE:
407 # default values can either be CODE refs
7a5b07b3 408 # in which case we need to call them. Or
5cf3dbcf 409 # they can be scalars (strings/numbers)
410 # in which case we can just deal with them
411 # in the code we eval.
412 if ($attr->is_default_a_coderef) {
413 return '$attrs->[' . $index . ']->default($instance)';
414 }
415 else {
4aa3d405 416 return q{"} . quotemeta( $attr->default ) . q{"};
7a5b07b3 417 }
5cf3dbcf 418}
419
4201;
421
5cf3dbcf 422__END__
423
424=pod
425
7a5b07b3 426=head1 NAME
5cf3dbcf 427
428Moose::Meta::Method::Constructor - Method Meta Object for constructors
429
5cf3dbcf 430=head1 DESCRIPTION
431
cefc9e36 432This class is a subclass of L<Class::MOP::Class::Constructor> that
433provides additional Moose-specific functionality
434
435To understand this class, you should read the the
436L<Class::MOP::Class::Constructor> documentation as well.
d44714be 437
5cf3dbcf 438=head1 METHODS
439
440=over 4
441
cefc9e36 442=item B<< $metamethod->can_be_inlined >>
a1257460 443
cefc9e36 444This returns true if the method can inlined.
5cf3dbcf 445
cefc9e36 446First, it looks at all of the parents of the associated class. If any
447of them have an inlined constructor, then the constructor can be
448inlined.
5cf3dbcf 449
cefc9e36 450If none of them have been inlined, it checks to make sure that the
451pre-inlining constructor for the class matches the constructor from
452the expected class.
5cf3dbcf 453
cefc9e36 454By default, it expects this constructor come from L<Moose::Object>,
455but subclasses can change this expectation.
5cf3dbcf 456
cefc9e36 457If the constructor cannot be inlined it warns that this is the case.
5cf3dbcf 458
459=back
460
461=head1 AUTHORS
462
463Stevan Little E<lt>stevan@iinteractive.comE<gt>
464
465=head1 COPYRIGHT AND LICENSE
466
2840a3b2 467Copyright 2006-2009 by Infinity Interactive, Inc.
5cf3dbcf 468
469L<http://www.iinteractive.com>
470
471This library is free software; you can redistribute it and/or modify
7a5b07b3 472it under the same terms as Perl itself.
5cf3dbcf 473
474=cut
475