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