2 package Moose::Meta::Method::Constructor;
8 use Scalar::Util 'blessed', 'weaken', 'looks_like_number', 'refaddr';
11 our $VERSION = '1.19';
12 our $AUTHORITY = 'cpan:STEVAN';
14 use base 'Moose::Meta::Method',
15 'Class::MOP::Method::Constructor';
21 my $meta = $options{metaclass};
23 (ref $options{options} eq 'HASH')
24 || $class->throw_error("You must pass a hash of options", data => $options{options});
26 ($options{package_name} && $options{name})
27 || $class->throw_error("You must supply the package_name and name parameters $Class::MOP::Method::UPGRADE_ERROR_TEXT");
31 'package_name' => $options{package_name},
32 'name' => $options{name},
33 'options' => $options{options},
34 'associated_metaclass' => $meta,
35 '_expected_method_class' => $options{_expected_method_class} || 'Moose::Object',
38 # we don't want this creating
39 # a cycle in the code, if not
41 weaken($self->{'associated_metaclass'});
43 $self->_initialize_body;
50 sub _initialize_body {
53 # the %options should also include a both
54 # a call 'initializer' and call 'SUPER::'
55 # options, which should cover approx 90%
56 # of the possible use cases (even if it
57 # requires some adaption on the part of
58 # the author, after all, nothing is free)
62 'my $_instance = shift;',
63 'my $class = Scalar::Util::blessed($_instance) || $_instance;',
64 'if ($class ne \'' . $self->associated_metaclass->name . '\') {',
65 'return ' . $self->_generate_fallback_constructor('$class') . ';',
67 $self->_generate_params('$params', '$class'),
68 $self->_generate_instance('$instance', '$class'),
69 $self->_generate_slot_initializers,
70 $self->_generate_triggers,
71 $self->_generate_BUILDALL,
75 warn join("\n", @source) if $self->options->{debug};
77 # We need to check if the attribute ->can('type_constraint')
78 # since we may be trying to immutabilize a Moose meta class,
79 # which in turn has attributes which are Class::MOP::Attribute
80 # objects, rather than Moose::Meta::Attribute. And
81 # Class::MOP::Attribute attributes have no type constraints.
82 # However we need to make sure we leave an undef value there
83 # because the inlined code is using the index of the attributes
84 # to determine where to find the type constraint
86 my $attrs = $self->_attributes;
88 my @type_constraints = map {
89 $_->can('type_constraint') ? $_->type_constraint : undef
92 my @type_constraint_bodies = map {
93 defined $_ ? $_->_compiled_type_constraint : undef;
96 my $defaults = [map { $_->default } @$attrs];
104 '$defaults' => \$defaults,
105 '@type_constraints' => \@type_constraints,
106 '@type_constraint_bodies' => \@type_constraint_bodies,
111 my $source = join("\n", @source);
113 "Could not eval the constructor :\n\n$source\n\nbecause :\n\n$_",
119 $self->{'body'} = $code;
122 sub _generate_fallback_constructor {
124 my ($class_var) = @_;
125 return $class_var . '->Moose::Object::new(@_)'
128 sub _generate_params {
130 my ($var, $class_var) = @_;
132 'my ' . $var . ' = ',
133 $self->_generate_BUILDARGS($class_var, '@_'),
138 sub _generate_instance {
140 my ($var, $class_var) = @_;
141 my $meta = $self->associated_metaclass;
144 'my ' . $var . ' = ',
145 $meta->inline_create_instance($class_var) . ';',
149 sub _generate_slot_initializers {
151 return map { $self->_generate_slot_initializer($_) }
152 0 .. (@{$self->_attributes} - 1);
155 sub _generate_BUILDARGS {
157 my ($class, $args) = @_;
159 my $buildargs = $self->associated_metaclass->find_method_by_name("BUILDARGS");
162 && (!$buildargs or $buildargs->body == \&Moose::Object::BUILDARGS)) {
167 'if (scalar @_ == 1) {',
168 'if (!defined($_[0]) || ref($_[0]) ne \'HASH\') {',
169 $self->_inline_throw_error(
170 '"Single parameters to new() must be a HASH ref"',
174 '$params = { %{ $_[0] } };',
178 '"The new() method for ' . $class . ' expects a '
179 . 'hash reference or a key/value list. You passed an '
180 . 'odd number of arguments"',
182 '$params = {@_, undef};',
192 return $class . '->BUILDARGS(' . $args . ')';
196 sub _generate_BUILDALL {
199 my @methods = reverse $self->associated_metaclass->find_all_methods_by_name('BUILD');
202 foreach my $method (@methods) {
204 '$instance->' . $method->{class} . '::BUILD($params);';
210 sub _generate_triggers {
214 for my $i (0 .. $#{ $self->_attributes }) {
215 my $attr = $self->_attributes->[$i];
217 next unless $attr->can('has_trigger') && $attr->has_trigger;
219 my $init_arg = $attr->init_arg;
220 next unless defined $init_arg;
223 'if (exists $params->{\'' . $init_arg . '\'}) {',
224 '$attrs->[' . $i . ']->trigger->(',
226 $attr->_inline_instance_get('$instance') . ',',
231 return @trigger_calls;
234 sub _generate_slot_initializer {
238 my $attr = $self->_attributes->[$index];
240 my @source = ('## ' . $attr->name);
242 push @source, $self->_check_required_attr($attr);
244 if (defined $attr->init_arg) {
246 'if (exists $params->{\'' . $attr->init_arg . '\'}) {',
247 $self->_init_attr_from_constructor($attr, $index),
249 if (my @default = $self->_init_attr_from_default($attr, $index)) {
257 if (my @default = $self->_init_attr_from_default($attr, $index)) {
259 '{', # _init_attr_from_default creates variables
268 sub _check_required_attr {
272 return unless defined $attr->init_arg;
273 return unless $attr->can('is_required') && $attr->is_required;
274 return if $attr->has_default || $attr->has_builder;
277 'if (!exists $params->{\'' . $attr->init_arg . '\'}) {',
278 $self->_inline_throw_error(
279 '"Attribute (' . quotemeta($attr->name) . ') is required"'
285 sub _init_attr_from_constructor {
287 my ($attr, $index) = @_;
290 'my $val = $params->{\'' . $attr->init_arg . '\'};',
291 $self->_generate_slot_assignment($attr, $index, '$val'),
295 sub _init_attr_from_default {
297 my ($attr, $index) = @_;
299 my $default = $self->_generate_default_value($attr, $index);
300 return unless $default;
303 'my $val = ' . $default . ';',
304 $self->_generate_slot_assignment($attr, $index, '$val'),
308 sub _generate_slot_assignment {
310 my ($attr, $index, $value) = @_;
314 if ($self->can('_generate_type_constraint_and_coercion')) {
315 push @source, $self->_generate_type_constraint_and_coercion(
316 $attr, $index, $value,
320 if ($attr->has_initializer) {
322 '$attrs->[' . $index . ']->set_initial_value(',
330 $attr->_inline_instance_set('$instance', $value) . ';',
337 sub _generate_type_constraint_and_coercion {
339 my ($attr, $index, $value) = @_;
341 return unless $attr->can('has_type_constraint')
342 && $attr->has_type_constraint;
346 if ($attr->should_coerce && $attr->type_constraint->has_coercion) {
347 push @source => $self->_generate_type_coercion(
348 '$type_constraints[' . $index . ']',
354 push @source => $self->_generate_type_constraint_check(
356 '$type_constraint_bodies[' . $index . ']',
357 '$type_constraints[' . $index . ']',
364 sub _generate_type_coercion {
366 my ($tc_obj, $value, $return_value) = @_;
367 return $return_value . ' = ' . $tc_obj . '->coerce(' . $value . ');';
370 sub _generate_type_constraint_check {
372 my ($attr, $tc_body, $tc_obj, $value) = @_;
374 $self->_inline_throw_error(
375 '"Attribute (' . quotemeta($attr->name) . ') '
376 . 'does not pass the type constraint because: " . '
377 . $tc_obj . '->get_message(' . $value . ')'
379 'unless ' . $tc_body . '->(' . $value . ');'
391 Moose::Meta::Method::Constructor - Method Meta Object for constructors
395 This class is a subclass of L<Class::MOP::Method::Constructor> that
396 provides additional Moose-specific functionality
398 To understand this class, you should read the the
399 L<Class::MOP::Method::Constructor> documentation as well.
403 C<Moose::Meta::Method::Constructor> is a subclass of
404 L<Moose::Meta::Method> I<and> L<Class::MOP::Method::Constructor>.
408 See L<Moose/BUGS> for details on reporting bugs.
412 Stevan Little E<lt>stevan@iinteractive.comE<gt>
414 =head1 COPYRIGHT AND LICENSE
416 Copyright 2006-2010 by Infinity Interactive, Inc.
418 L<http://www.iinteractive.com>
420 This library is free software; you can redistribute it and/or modify
421 it under the same terms as Perl itself.