Skip threading tests on Test::More 2.00_01
[gitmo/Mouse.git] / lib / Mouse / Meta / Method / Constructor.pm
CommitLineData
fc1d8369 1package Mouse::Meta::Method::Constructor;
3821b191 2use Mouse::Util qw(:meta); # enables strict and warnings
fc1d8369 3
f031f4d5 4sub _inline_slot{
f8cbb121 5 my(undef, $self_var, $attr_name) = @_;
6 return sprintf '%s->{q{%s}}', $self_var, $attr_name;
7}
8
380e1cd7 9sub _generate_constructor {
2a464664 10 my ($class, $metaclass, $args) = @_;
24ad3f66 11
f031f4d5 12 my $associated_metaclass_name = $metaclass->name;
13
3db78d2a 14 my $buildall = $class->_generate_BUILDALL($metaclass);
f031f4d5 15 my $buildargs = $class->_generate_BUILDARGS($metaclass);
51b0597d 16 my $initializer = $metaclass->{_initialize_object} ||= do {
17 $class->_generate_initialize_object($metaclass);
18 };
a3a09e38 19 my $source = sprintf(<<'EOT', __LINE__, __FILE__, $metaclass->name, $buildargs, $buildall);
20#line %d %s
21 package %s;
22 sub {
23 my $class = shift;
24 return $class->Mouse::Object::new(@_)
25 if $class ne __PACKAGE__;
0bfc7290 26 # BUILDARGS
a3a09e38 27 %s;
28 my $instance = bless {}, $class;
ab688693 29 $metaclass->$initializer($instance, $args, 0);
0bfc7290 30 # BUILDALL
a3a09e38 31 %s;
32 return $instance;
2a464664 33 }
a3a09e38 34EOT
0bfc7290 35 #warn $source;
a3a09e38 36 my $body;
ad087d11 37 my $e = do{
38 local $@;
a3a09e38 39 $body = eval $source;
ad087d11 40 $@;
41 };
42 die $e if $e;
a3a09e38 43 return $body;
fc1d8369 44}
45
a3a09e38 46sub _generate_initialize_object {
47 my ($method_class, $metaclass) = @_;
48 my @attrs = $metaclass->get_all_attributes;
49
50 my @checks = map { $_ && $_->_compiled_type_constraint }
51 map { $_->type_constraint } @attrs;
52
fc1d8369 53 my @res;
9df6f0cd 54
2efc0af1 55 my $has_triggers;
fb4ddd88 56 my $strict = $metaclass->strict_constructor;
e128626c 57
8801a6e6 58 if($strict){
e128626c 59 push @res, 'my $used = 0;';
60 }
2efc0af1 61
a3a09e38 62 for my $index (0 .. @attrs - 1) {
0bfc7290 63 my $code = '';
64
a3a09e38 65 my $attr = $attrs[$index];
fc1d8369 66 my $key = $attr->name;
9df6f0cd 67
0bfc7290 68 my $init_arg = $attr->init_arg;
69 my $type_constraint = $attr->type_constraint;
8aba926d 70 my $is_weak_ref = $attr->is_weak_ref;
0bfc7290 71 my $need_coercion;
c91862e8 72
f031f4d5 73 my $instance_slot = $method_class->_inline_slot('$instance', $key);
0bfc7290 74 my $attr_var = "\$attrs[$index]";
75 my $constraint_var;
c91862e8 76
0bfc7290 77 if(defined $type_constraint){
78 $constraint_var = "$attr_var\->{type_constraint}";
79 $need_coercion = ($attr->should_coerce && $type_constraint->has_coercion);
80 }
c91862e8 81
0bfc7290 82 $code .= "# initialize $key\n";
83
84 my $post_process = '';
85 if(defined $type_constraint){
a3a09e38 86 $post_process .= "\$checks[$index]->($instance_slot)\n";
da23cd4a 87 $post_process .= " or $attr_var->_throw_type_constraint_error($instance_slot, $constraint_var);\n";
0bfc7290 88 }
8aba926d 89 if($is_weak_ref){
a3a09e38 90 $post_process = "Scalar::Util::weaken($instance_slot) "
91 . "if ref $instance_slot;\n";
0bfc7290 92 }
c91862e8 93
a3a09e38 94 # build cde for an attribute
0bfc7290 95 if (defined $init_arg) {
96 my $value = "\$args->{q{$init_arg}}";
c91862e8 97
0bfc7290 98 $code .= "if (exists $value) {\n";
99
100 if($need_coercion){
620c3203 101 $value = "$constraint_var->coerce($value)";
41cdacce 102 }
c91862e8 103
f031f4d5 104 $code .= "$instance_slot = $value;\n";
0bfc7290 105 $code .= $post_process;
106
9df6f0cd 107 if ($attr->has_trigger) {
2efc0af1 108 $has_triggers++;
620c3203 109 $code .= "push \@triggers, [$attr_var\->{trigger}, $instance_slot];\n";
fc1d8369 110 }
c91862e8 111
8801a6e6 112 if ($strict){
113 $code .= '++$used;' . "\n";
e128626c 114 }
115
116 $code .= "\n} else {\n"; # $value exists
9df6f0cd 117 }
c91862e8 118
9df6f0cd 119 if ($attr->has_default || $attr->has_builder) {
120 unless ($attr->is_lazy) {
121 my $default = $attr->default;
122 my $builder = $attr->builder;
e8ba7b26 123
0bfc7290 124 my $value;
125 if (defined($builder)) {
126 $value = "\$instance->$builder()";
ffbbf459 127 }
128 elsif (ref($default) eq 'CODE') {
0bfc7290 129 $value = "$attr_var\->{default}->(\$instance)";
ffbbf459 130 }
0bfc7290 131 elsif (defined($default)) {
132 $value = "$attr_var\->{default}";
ffbbf459 133 }
134 else {
0bfc7290 135 $value = 'undef';
ffbbf459 136 }
e8ba7b26 137
0bfc7290 138 if($need_coercion){
139 $value = "$constraint_var->coerce($value)";
9df6f0cd 140 }
9df6f0cd 141
f031f4d5 142 $code .= "$instance_slot = $value;\n";
a3a09e38 143 $code .= $post_process;
fc1d8369 144 }
713a2a05 145 }
9df6f0cd 146 elsif ($attr->is_required) {
a3a09e38 147 $code .= "\$meta->throw_error('Attribute ($key) is required')";
148 $code .= " unless \$is_cloning;\n";
9df6f0cd 149 }
150
0bfc7290 151 $code .= "}\n" if defined $init_arg;
9df6f0cd 152
fc1d8369 153 push @res, $code;
154 }
9df6f0cd 155
8801a6e6 156 if($strict){
e128626c 157 push @res, q{if($used < keys %{$args})}
a3a09e38 158 . q{{ $meta->_report_unknown_args(\@attrs, $args) }};
e128626c 159 }
160
2efc0af1 161 if($metaclass->is_anon_class){
a3a09e38 162 push @res, q{$instance->{__METACLASS__} = $meta;};
2efc0af1 163 }
164
165 if($has_triggers){
166 unshift @res, q{my @triggers;};
e128626c 167 push @res, q{$_->[0]->($instance, $_->[1]) for @triggers;};
2efc0af1 168 }
169
a3a09e38 170 my $source = sprintf <<'EOT', __LINE__, __FILE__, $metaclass->name, join "\n", @res;
171#line %d %s
172 package %s;
173 sub {
174 my($meta, $instance, $args, $is_cloning) = @_;
175 %s;
176 return $instance;
177 }
178EOT
179 warn $source if $ENV{MOUSE_DEBUG};
180 my $body;
181 my $e = do {
182 local $@;
183 $body = eval $source;
184 $@;
185 };
186 die $e if $e;
187 return $body;
fc1d8369 188}
189
190sub _generate_BUILDARGS {
0bfc7290 191 my(undef, $metaclass) = @_;
9dcd7d23 192
0bfc7290 193 my $class = $metaclass->name;
194 if ( $class->can('BUILDARGS') && $class->can('BUILDARGS') != \&Mouse::Object::BUILDARGS ) {
53d4053e 195 return 'my $args = $class->BUILDARGS(@_)';
9dcd7d23 196 }
197
198 return <<'...';
53d4053e 199 my $args;
fc1d8369 200 if ( scalar @_ == 1 ) {
c9aefe26 201 ( ref( $_[0] ) eq 'HASH' )
fc1d8369 202 || Carp::confess "Single parameters to new() must be a HASH ref";
53d4053e 203 $args = +{ %{ $_[0] } };
fc1d8369 204 }
205 else {
53d4053e 206 $args = +{@_};
fc1d8369 207 }
fc1d8369 208...
209}
210
211sub _generate_BUILDALL {
0bfc7290 212 my (undef, $metaclass) = @_;
7ca5c5fb 213
2efc0af1 214 return '' unless $metaclass->name->can('BUILD');
fc1d8369 215
7ca5c5fb 216 my @code;
217 for my $class ($metaclass->linearized_isa) {
a5c683f6 218 if (Mouse::Util::get_code_ref($class, 'BUILD')) {
7ca5c5fb 219 unshift @code, qq{${class}::BUILD(\$instance, \$args);};
fc1d8369 220 }
221 }
222 return join "\n", @code;
223}
224
2251;
0126c27c 226__END__
a25ca8d6 227
228=head1 NAME
229
230Mouse::Meta::Method::Constructor - A Mouse method generator for constructors
231
232=head1 VERSION
233
b0d52f03 234This document describes Mouse version 0.73
a25ca8d6 235
236=head1 SEE ALSO
237
238L<Moose::Meta::Method::Constructor>
239
240=cut