remove trailing whitespace
[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
74397c13 9our $VERSION = '0.75_01';
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
99 my $code = $self->_compile_code(
100 code => $source,
101 environment => {
102 '$meta' => \$self,
103 '$attrs' => \$attrs,
104 '@type_constraints' => \@type_constraints,
105 '@type_constraint_bodies' => \@type_constraint_bodies,
106 },
107 ) or $self->throw_error("Could not eval the constructor :\n\n$source\n\nbecause :\n\n$@", error => $@, data => $source );
d03bd989 108
e606ae5f 109 $self->{'body'} = $code;
110}
111
b905f0db 112sub _generate_params {
113 my ( $self, $var, $class_var ) = @_;
114 "my $var = " . $self->_generate_BUILDARGS( $class_var, '@_' ) . ";\n";
115}
116
117sub _generate_instance {
118 my ( $self, $var, $class_var ) = @_;
119 "my $var = "
0772362a 120 . $self->_meta_instance->inline_create_instance($class_var) . ";\n";
b905f0db 121}
122
123sub _generate_slot_initializers {
124 my ($self) = @_;
125 return (join ";\n" => map {
126 $self->_generate_slot_initializer($_)
0772362a 127 } 0 .. (@{$self->_attributes} - 1)) . ";\n";
b905f0db 128}
129
e606ae5f 130sub _generate_BUILDARGS {
131 my ( $self, $class, $args ) = @_;
132
133 my $buildargs = $self->associated_metaclass->find_method_by_name("BUILDARGS");
134
135 if ( $args eq '@_' and ( !$buildargs or $buildargs->body == \&Moose::Object::BUILDARGS ) ) {
136 return join("\n",
137 'do {',
138 $self->_inline_throw_error('"Single parameters to new() must be a HASH ref"', 'data => $_[0]'),
a62dcd43 139 ' if scalar @_ == 1 && !( defined $_[0] && ref $_[0] eq q{HASH} );',
e606ae5f 140 '(scalar @_ == 1) ? {%{$_[0]}} : {@_};',
141 '}',
142 );
143 } else {
144 return $class . "->BUILDARGS($args)";
145 }
5cf3dbcf 146}
147
148sub _generate_BUILDALL {
149 my $self = shift;
150 my @BUILD_calls;
1f779926 151 foreach my $method (reverse $self->associated_metaclass->find_all_methods_by_name('BUILD')) {
e606ae5f 152 push @BUILD_calls => '$instance->' . $method->{class} . '::BUILD($params)';
5cf3dbcf 153 }
7a5b07b3 154 return join ";\n" => @BUILD_calls;
5cf3dbcf 155}
156
1b55c340 157sub _generate_triggers {
158 my $self = shift;
159 my @trigger_calls;
0772362a 160 foreach my $i ( 0 .. $#{ $self->_attributes } ) {
161 my $attr = $self->_attributes->[$i];
708b4070 162
163 next unless $attr->can('has_trigger') && $attr->has_trigger;
164
165 my $init_arg = $attr->init_arg;
166
167 next unless defined $init_arg;
168
169 push @trigger_calls => '(exists $params->{\''
170 . $init_arg
171 . '\'}) && do {'
172 . "\n "
173 . '$attrs->['
174 . $i
175 . ']->trigger->('
176 . '$instance, '
0772362a 177 . $self->_meta_instance->inline_get_slot_value(
708b4070 178 '$instance',
179 $attr->name,
180 )
181 . ', '
c2685d20 182 . ');' . "\n}";
1b55c340 183 }
708b4070 184
185 return join ";\n" => @trigger_calls;
1b55c340 186}
187
5cf3dbcf 188sub _generate_slot_initializer {
189 my $self = shift;
190 my $index = shift;
7a5b07b3 191
0772362a 192 my $attr = $self->_attributes->[$index];
7a5b07b3 193
5cf3dbcf 194 my @source = ('## ' . $attr->name);
d66bea3c 195
196 my $is_moose = $attr->isa('Moose::Meta::Attribute'); # XXX FIXME
7a5b07b3 197
84981146 198 if ($is_moose && defined($attr->init_arg) && $attr->is_required && !$attr->has_default && !$attr->has_builder) {
e606ae5f 199 push @source => ('(exists $params->{\'' . $attr->init_arg . '\'}) ' .
3e504337 200 '|| ' . $self->_inline_throw_error('"Attribute (' . $attr->name . ') is required"') .';');
5cf3dbcf 201 }
7a5b07b3 202
ca168e89 203 if (($attr->has_default || $attr->has_builder) && !($is_moose && $attr->is_lazy)) {
7a5b07b3 204
84981146 205 if ( defined( my $init_arg = $attr->init_arg ) ) {
e606ae5f 206 push @source => 'if (exists $params->{\'' . $init_arg . '\'}) {';
207 push @source => ('my $val = $params->{\'' . $init_arg . '\'};');
208 push @source => $self->_generate_type_constraint_and_coercion($attr, $index)
209 if $is_moose;
210 push @source => $self->_generate_slot_assignment($attr, '$val', $index);
84981146 211 push @source => "} else {";
212 }
ca168e89 213 my $default;
97e11ef5 214 if ( $attr->has_default ) {
ca168e89 215 $default = $self->_generate_default_value($attr, $index);
d03bd989 216 }
97e11ef5 217 else {
ca168e89 218 my $builder = $attr->builder;
219 $default = '$instance->' . $builder;
220 }
d03bd989 221
3db3ea82 222 push @source => '{'; # wrap this to avoid my $val overwrite warnings
5cf3dbcf 223 push @source => ('my $val = ' . $default . ';');
e606ae5f 224 push @source => $self->_generate_type_constraint_and_coercion($attr, $index)
d03bd989 225 if $is_moose;
51c107ef 226 push @source => $self->_generate_slot_assignment($attr, '$val', $index);
d03bd989 227 push @source => '}'; # close - wrap this to avoid my $val overrite warnings
7a5b07b3 228
84981146 229 push @source => "}" if defined $attr->init_arg;
7a5b07b3 230 }
84981146 231 elsif ( defined( my $init_arg = $attr->init_arg ) ) {
e606ae5f 232 push @source => '(exists $params->{\'' . $init_arg . '\'}) && do {';
8ecb1fa0 233
e606ae5f 234 push @source => ('my $val = $params->{\'' . $init_arg . '\'};');
d66bea3c 235 if ($is_moose && $attr->has_type_constraint) {
7a5b07b3 236 if ($attr->should_coerce && $attr->type_constraint->has_coercion) {
688fcdda 237 push @source => $self->_generate_type_coercion(
d03bd989 238 $attr,
239 '$type_constraints[' . $index . ']',
240 '$val',
688fcdda 241 '$val'
242 );
8ecb1fa0 243 }
688fcdda 244 push @source => $self->_generate_type_constraint_check(
d03bd989 245 $attr,
246 '$type_constraint_bodies[' . $index . ']',
247 '$type_constraints[' . $index . ']',
688fcdda 248 '$val'
249 );
8ecb1fa0 250 }
9df136d0 251 push @source => $self->_generate_slot_assignment($attr, '$val', $index);
7a5b07b3 252
253 push @source => "}";
5cf3dbcf 254 }
7a5b07b3 255
5cf3dbcf 256 return join "\n" => @source;
257}
258
259sub _generate_slot_assignment {
9df136d0 260 my ($self, $attr, $value, $index) = @_;
261
262 my $source;
d03bd989 263
9df136d0 264 if ($attr->has_initializer) {
265 $source = (
266 '$attrs->[' . $index . ']->set_initial_value($instance, ' . $value . ');'
d03bd989 267 );
9df136d0 268 }
269 else {
270 $source = (
0772362a 271 $self->_meta_instance->inline_set_slot_value(
9df136d0 272 '$instance',
eae37c67 273 $attr->name,
9df136d0 274 $value
275 ) . ';'
d03bd989 276 );
9df136d0 277 }
d03bd989 278
279 my $is_moose = $attr->isa('Moose::Meta::Attribute'); # XXX FIXME
7a5b07b3 280
d66bea3c 281 if ($is_moose && $attr->is_weak_ref) {
5cf3dbcf 282 $source .= (
283 "\n" .
0772362a 284 $self->_meta_instance->inline_weaken_slot_value(
7a5b07b3 285 '$instance',
eae37c67 286 $attr->name
7a5b07b3 287 ) .
5cf3dbcf 288 ' if ref ' . $value . ';'
7a5b07b3 289 );
290 }
291
5cf3dbcf 292 return $source;
293}
294
e606ae5f 295sub _generate_type_constraint_and_coercion {
296 my ($self, $attr, $index) = @_;
d03bd989 297
e606ae5f 298 return unless $attr->has_type_constraint;
d03bd989 299
e606ae5f 300 my @source;
301 if ($attr->should_coerce && $attr->type_constraint->has_coercion) {
302 push @source => $self->_generate_type_coercion(
303 $attr,
304 '$type_constraints[' . $index . ']',
305 '$val',
306 '$val'
307 );
308 }
309 push @source => $self->_generate_type_constraint_check(
310 $attr,
311 ('$type_constraint_bodies[' . $index . ']'),
d03bd989 312 ('$type_constraints[' . $index . ']'),
e606ae5f 313 '$val'
314 );
315 return @source;
316}
317
5cf3dbcf 318sub _generate_type_coercion {
319 my ($self, $attr, $type_constraint_name, $value_name, $return_value_name) = @_;
320 return ($return_value_name . ' = ' . $type_constraint_name . '->coerce(' . $value_name . ');');
321}
322
323sub _generate_type_constraint_check {
688fcdda 324 my ($self, $attr, $type_constraint_cv, $type_constraint_obj, $value_name) = @_;
5cf3dbcf 325 return (
3e504337 326 $self->_inline_throw_error('"Attribute (' # FIXME add 'dad'
d03bd989 327 . $attr->name
328 . ') does not pass the type constraint because: " . '
3e504337 329 . $type_constraint_obj . '->get_message(' . $value_name . ')')
330 . "\n\t unless " . $type_constraint_cv . '->(' . $value_name . ');'
7a5b07b3 331 );
5cf3dbcf 332}
333
334sub _generate_default_value {
335 my ($self, $attr, $index) = @_;
336 # NOTE:
337 # default values can either be CODE refs
7a5b07b3 338 # in which case we need to call them. Or
5cf3dbcf 339 # they can be scalars (strings/numbers)
340 # in which case we can just deal with them
341 # in the code we eval.
342 if ($attr->is_default_a_coderef) {
343 return '$attrs->[' . $index . ']->default($instance)';
344 }
345 else {
4aa3d405 346 return q{"} . quotemeta( $attr->default ) . q{"};
7a5b07b3 347 }
5cf3dbcf 348}
349
3501;
351
5cf3dbcf 352__END__
353
354=pod
355
7a5b07b3 356=head1 NAME
5cf3dbcf 357
358Moose::Meta::Method::Constructor - Method Meta Object for constructors
359
5cf3dbcf 360=head1 DESCRIPTION
361
cefc9e36 362This class is a subclass of L<Class::MOP::Class::Constructor> that
363provides additional Moose-specific functionality
364
365To understand this class, you should read the the
366L<Class::MOP::Class::Constructor> documentation as well.
d44714be 367
bc89e9b5 368=head1 INHERITANCE
369
370C<Moose::Meta::Method::Constructor> is a subclass of
371L<Moose::Meta::Method> I<and> L<Class::MOP::Method::Constructor>.
372
5cf3dbcf 373=head1 METHODS
374
375=over 4
376
cefc9e36 377=item B<< $metamethod->can_be_inlined >>
a1257460 378
cefc9e36 379This returns true if the method can inlined.
5cf3dbcf 380
cefc9e36 381First, it looks at all of the parents of the associated class. If any
382of them have an inlined constructor, then the constructor can be
383inlined.
5cf3dbcf 384
cefc9e36 385If none of them have been inlined, it checks to make sure that the
386pre-inlining constructor for the class matches the constructor from
387the expected class.
5cf3dbcf 388
cefc9e36 389By default, it expects this constructor come from L<Moose::Object>,
390but subclasses can change this expectation.
5cf3dbcf 391
cefc9e36 392If the constructor cannot be inlined it warns that this is the case.
5cf3dbcf 393
394=back
395
396=head1 AUTHORS
397
398Stevan Little E<lt>stevan@iinteractive.comE<gt>
399
400=head1 COPYRIGHT AND LICENSE
401
2840a3b2 402Copyright 2006-2009 by Infinity Interactive, Inc.
5cf3dbcf 403
404L<http://www.iinteractive.com>
405
406This library is free software; you can redistribute it and/or modify
7a5b07b3 407it under the same terms as Perl itself.
5cf3dbcf 408
409=cut
410