Fix docs. The phrases "Fewer than 1%" and "over 96%" are very confusing, so I removed...
[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
f8cbb121 4sub _inline_slot{
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
2efc0af1 12 my $associated_metaclass_name = $metaclass->name;
0bfc7290 13
7ca5c5fb 14 my @attrs = $metaclass->get_all_attributes;
15
16 my $buildall = $class->_generate_BUILDALL($metaclass);
17 my $buildargs = $class->_generate_BUILDARGS($metaclass);
18 my $processattrs = $class->_generate_processattrs($metaclass, \@attrs);
19
0bfc7290 20 my @checks = map { $_ && $_->_compiled_type_constraint }
21 map { $_->type_constraint } @attrs;
ad087d11 22
23 my $source = sprintf("#line %d %s\n", __LINE__, __FILE__).<<"...";
24 sub \{
2a464664 25 my \$class = shift;
26 return \$class->Mouse::Object::new(\@_)
27 if \$class ne q{$associated_metaclass_name};
0bfc7290 28 # BUILDARGS
2a464664 29 $buildargs;
30 my \$instance = bless {}, \$class;
0bfc7290 31 # process attributes
2a464664 32 $processattrs;
0bfc7290 33 # BUILDALL
2a464664 34 $buildall;
35 return \$instance;
36 }
fc1d8369 37...
0bfc7290 38 #warn $source;
ad087d11 39 my $code;
40 my $e = do{
41 local $@;
42 $code = eval $source;
43 $@;
44 };
45 die $e if $e;
380e1cd7 46 return $code;
fc1d8369 47}
48
49sub _generate_processattrs {
f8cbb121 50 my ($method_class, $metaclass, $attrs) = @_;
fc1d8369 51 my @res;
9df6f0cd 52
2efc0af1 53 my $has_triggers;
e128626c 54 my $strict_constructor = $metaclass->__strict_constructor;
55
56
57 if($strict_constructor){
58 push @res, 'my $used = 0;';
59 }
2efc0af1 60
9df6f0cd 61 for my $index (0 .. @$attrs - 1) {
0bfc7290 62 my $code = '';
63
c12edd9a 64 my $attr = $attrs->[$index];
fc1d8369 65 my $key = $attr->name;
9df6f0cd 66
0bfc7290 67 my $init_arg = $attr->init_arg;
68 my $type_constraint = $attr->type_constraint;
8aba926d 69 my $is_weak_ref = $attr->is_weak_ref;
0bfc7290 70 my $need_coercion;
c91862e8 71
f8cbb121 72 my $instance_slot = $method_class->_inline_slot('$instance', $key);
0bfc7290 73 my $attr_var = "\$attrs[$index]";
74 my $constraint_var;
c91862e8 75
0bfc7290 76 if(defined $type_constraint){
77 $constraint_var = "$attr_var\->{type_constraint}";
78 $need_coercion = ($attr->should_coerce && $type_constraint->has_coercion);
79 }
c91862e8 80
0bfc7290 81 $code .= "# initialize $key\n";
82
83 my $post_process = '';
84 if(defined $type_constraint){
85 $post_process .= "\$checks[$index]->($instance_slot)";
da23cd4a 86 $post_process .= " or $attr_var->_throw_type_constraint_error($instance_slot, $constraint_var);\n";
0bfc7290 87 }
8aba926d 88 if($is_weak_ref){
0bfc7290 89 $post_process .= "Scalar::Util::weaken($instance_slot) if ref $instance_slot;\n";
90 }
c91862e8 91
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
0bfc7290 101 $code .= "$instance_slot = $value;\n";
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
e128626c 109 if ($strict_constructor){
110 $code .= '$used++;' . "\n";
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
0bfc7290 139 $code .= "$instance_slot = $value;\n";
8aba926d 140 if($is_weak_ref){
141 $code .= "Scalar::Util::weaken($instance_slot);\n";
142 }
fc1d8369 143 }
713a2a05 144 }
9df6f0cd 145 elsif ($attr->is_required) {
7756897f 146 $code .= "Carp::confess('Attribute ($key) is required');";
9df6f0cd 147 }
148
0bfc7290 149 $code .= "}\n" if defined $init_arg;
9df6f0cd 150
fc1d8369 151 push @res, $code;
152 }
9df6f0cd 153
e128626c 154 if($strict_constructor){
155 push @res, q{if($used < keys %{$args})}
156 . q{{ Mouse::Meta::Method::Constructor::_report_unknown_args($metaclass, \@attrs, $instance, $args) }};
157 }
158
2efc0af1 159 if($metaclass->is_anon_class){
a8391b11 160 push @res, q{$instance->{__METACLASS__} = $metaclass;};
2efc0af1 161 }
162
163 if($has_triggers){
164 unshift @res, q{my @triggers;};
e128626c 165 push @res, q{$_->[0]->($instance, $_->[1]) for @triggers;};
2efc0af1 166 }
167
168 return join "\n", @res;
fc1d8369 169}
170
171sub _generate_BUILDARGS {
0bfc7290 172 my(undef, $metaclass) = @_;
9dcd7d23 173
0bfc7290 174 my $class = $metaclass->name;
175 if ( $class->can('BUILDARGS') && $class->can('BUILDARGS') != \&Mouse::Object::BUILDARGS ) {
53d4053e 176 return 'my $args = $class->BUILDARGS(@_)';
9dcd7d23 177 }
178
179 return <<'...';
53d4053e 180 my $args;
fc1d8369 181 if ( scalar @_ == 1 ) {
c9aefe26 182 ( ref( $_[0] ) eq 'HASH' )
fc1d8369 183 || Carp::confess "Single parameters to new() must be a HASH ref";
53d4053e 184 $args = +{ %{ $_[0] } };
fc1d8369 185 }
186 else {
53d4053e 187 $args = +{@_};
fc1d8369 188 }
fc1d8369 189...
190}
191
192sub _generate_BUILDALL {
0bfc7290 193 my (undef, $metaclass) = @_;
7ca5c5fb 194
2efc0af1 195 return '' unless $metaclass->name->can('BUILD');
fc1d8369 196
7ca5c5fb 197 my @code;
198 for my $class ($metaclass->linearized_isa) {
a5c683f6 199 if (Mouse::Util::get_code_ref($class, 'BUILD')) {
7ca5c5fb 200 unshift @code, qq{${class}::BUILD(\$instance, \$args);};
fc1d8369 201 }
202 }
203 return join "\n", @code;
204}
205
e128626c 206sub _report_unknown_args {
207 my($metaclass, $attrs, $instance, $args) = @_;
208
209 my @unknowns;
210 my %init_args;
211 foreach my $attr(@{$attrs}){
212 my $init_arg = $attr->init_arg;
213 if(defined $init_arg){
214 $init_args{$init_arg}++;
215 }
216 }
217
218 while(my $key = each %{$args}){
219 if(!exists $init_args{$key}){
220 push @unknowns, $key;
221 }
222 }
223
224 $metaclass->throw_error( sprintf
225 "Unknown attribute passed to the constructor of %s: %s",
226 ref($instance), join ', ', @unknowns
227 );
228}
229
fc1d8369 2301;
0126c27c 231__END__
a25ca8d6 232
233=head1 NAME
234
235Mouse::Meta::Method::Constructor - A Mouse method generator for constructors
236
237=head1 VERSION
238
d990f791 239This document describes Mouse version 0.50_02
a25ca8d6 240
241=head1 SEE ALSO
242
243L<Moose::Meta::Method::Constructor>
244
245=cut