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