4312f5efbfe88e525aee527c29bbc20f1509c353
[gitmo/Mouse.git] / lib / Mouse / Meta / Method / Constructor.pm
1 package Mouse::Meta::Method::Constructor;
2 use Mouse::Util qw(get_code_ref); # enables strict and warnings
3
4 sub _inline_slot{
5     my(undef, $self_var, $attr_name) = @_;
6     return sprintf '%s->{q{%s}}', $self_var, $attr_name;
7 }
8
9 sub _generate_constructor {
10     my ($class, $metaclass, $args) = @_;
11
12     my $associated_metaclass_name = $metaclass->name;
13
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
20     my @checks = map { $_ && $_->_compiled_type_constraint }
21                  map { $_->type_constraint } @attrs;
22
23     my $source = sprintf("#line %d %s\n", __LINE__, __FILE__).<<"...";
24         sub \{
25             my \$class = shift;
26             return \$class->Mouse::Object::new(\@_)
27                 if \$class ne q{$associated_metaclass_name};
28             # BUILDARGS
29             $buildargs;
30             my \$instance = bless {}, \$class;
31             # process attributes
32             $processattrs;
33             # BUILDALL
34             $buildall;
35             return \$instance;
36         }
37 ...
38     #warn $source;
39     my $code;
40     my $e = do{
41         local $@;
42         $code = eval $source;
43         $@;
44     };
45     die $e if $e;
46     return $code;
47 }
48
49 sub _generate_processattrs {
50     my ($method_class, $metaclass, $attrs) = @_;
51     my @res;
52
53     my $has_triggers;
54
55     for my $index (0 .. @$attrs - 1) {
56         my $code = '';
57
58         my $attr = $attrs->[$index];
59         my $key  = $attr->name;
60
61         my $init_arg        = $attr->init_arg;
62         my $type_constraint = $attr->type_constraint;
63         my $need_coercion;
64
65         my $instance_slot  = $method_class->_inline_slot('$instance', $key);
66         my $attr_var       = "\$attrs[$index]";
67         my $constraint_var;
68
69         if(defined $type_constraint){
70              $constraint_var = "$attr_var\->{type_constraint}";
71              $need_coercion  = ($attr->should_coerce && $type_constraint->has_coercion);
72         }
73
74         $code .= "# initialize $key\n";
75
76         my $post_process = '';
77         if(defined $type_constraint){
78             $post_process .= "\$checks[$index]->($instance_slot)";
79             $post_process .= "  or $attr_var->verify_type_constraint_error(q{$key}, $instance_slot, $constraint_var);\n";
80         }
81         if($attr->is_weak_ref){
82             $post_process .= "Scalar::Util::weaken($instance_slot) if ref $instance_slot;\n";
83         }
84
85         if (defined $init_arg) {
86             my $value = "\$args->{q{$init_arg}}";
87
88             $code .= "if (exists $value) {\n";
89
90             if($need_coercion){
91                 $value = "$constraint_var->coerce($value)";
92             }
93
94             $code .= "$instance_slot = $value;\n";
95             $code .= $post_process;
96
97             if ($attr->has_trigger) {
98                 $has_triggers++;
99                 $code .= "push \@triggers, [$attr_var\->{trigger}, $instance_slot];\n";
100             }
101
102             $code .= "\n} else {\n";
103         }
104
105         if ($attr->has_default || $attr->has_builder) {
106             unless ($attr->is_lazy) {
107                 my $default = $attr->default;
108                 my $builder = $attr->builder;
109
110                 my $value;
111                 if (defined($builder)) {
112                     $value = "\$instance->$builder()";
113                 }
114                 elsif (ref($default) eq 'CODE') {
115                     $value = "$attr_var\->{default}->(\$instance)";
116                 }
117                 elsif (defined($default)) {
118                     $value = "$attr_var\->{default}";
119                 }
120                 else {
121                     $value = 'undef';
122                 }
123
124                 if($need_coercion){
125                     $value = "$constraint_var->coerce($value)";
126                 }
127
128                 $code .= "$instance_slot = $value;\n";
129             }
130         }
131         elsif ($attr->is_required) {
132             $code .= "Carp::confess('Attribute ($key) is required');";
133         }
134
135         $code .= "}\n" if defined $init_arg;
136
137         push @res, $code;
138     }
139
140     if($metaclass->is_anon_class){
141         push @res, q{$instance->{__METACLASS__} = $metaclass;};
142     }
143
144     if($has_triggers){
145         unshift @res, q{my @triggers;};
146         push    @res,  q{$_->[0]->($instance, $_->[1]) for @triggers;};
147     }
148
149     return join "\n", @res;
150 }
151
152 sub _generate_BUILDARGS {
153     my(undef, $metaclass) = @_;
154
155     my $class = $metaclass->name;
156     if ( $class->can('BUILDARGS') && $class->can('BUILDARGS') != \&Mouse::Object::BUILDARGS ) {
157         return 'my $args = $class->BUILDARGS(@_)';
158     }
159
160     return <<'...';
161         my $args;
162         if ( scalar @_ == 1 ) {
163             ( ref( $_[0] ) eq 'HASH' )
164                 || Carp::confess "Single parameters to new() must be a HASH ref";
165             $args = +{ %{ $_[0] } };
166         }
167         else {
168             $args = +{@_};
169         }
170 ...
171 }
172
173 sub _generate_BUILDALL {
174     my (undef, $metaclass) = @_;
175
176     return '' unless $metaclass->name->can('BUILD');
177
178     my @code;
179     for my $class ($metaclass->linearized_isa) {
180         if (get_code_ref($class, 'BUILD')) {
181             unshift  @code, qq{${class}::BUILD(\$instance, \$args);};
182         }
183     }
184     return join "\n", @code;
185 }
186
187 1;
188 __END__
189
190 =head1 NAME
191
192 Mouse::Meta::Method::Constructor - A Mouse method generator for constructors
193
194 =head1 VERSION
195
196 This document describes Mouse version 0.40_01
197
198 =head1 SEE ALSO
199
200 L<Moose::Meta::Method::Constructor>
201
202 =cut