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