set instance to attributes default code
[gitmo/Mouse.git] / lib / Mouse / Meta / Method / Constructor.pm
1 package Mouse::Meta::Method::Constructor;
2 use strict;
3 use warnings;
4
5 sub generate_constructor_method_inline {
6     my ($class, $meta) = @_;
7
8     my @attrs = $meta->compute_all_applicable_attributes; # this one is using by evaled code
9     my $buildall = $class->_generate_BUILDALL($meta);
10     my $buildargs = $class->_generate_BUILDARGS();
11     my $processattrs = $class->_generate_processattrs($meta, \@attrs);
12
13     my $code = <<"...";
14     sub {
15         my \$class = shift;
16         my \$args = $buildargs;
17         my \$instance = bless {}, \$class;
18         $processattrs;
19         $buildall;
20         return \$instance;
21     }
22 ...
23
24     warn $code if $ENV{DEBUG};
25
26     local $@;
27     my $res = eval $code;
28     die $@ if $@;
29     $res;
30 }
31
32 sub _generate_processattrs {
33     my ($class, $meta, $attrs) = @_;
34     my @res;
35     for my $index (0..scalar(@$attrs)-1) {
36         my $attr = $attrs->[$index];
37         my $from = $attr->init_arg;
38         my $key  = $attr->name;
39
40         my $set_value = do {
41             my @code;
42
43             if ($attr->should_coerce) {
44                 push @code, "my \$value = \$attrs[$index]->coerce_constraint( \$args->{'$from'});";
45             }
46             else {
47                 push @code, "my \$value = \$args->{'$from'};";
48             }
49
50             if ($attr->has_type_constraint) {
51                 push @code, "\$attrs[$index]->verify_type_constraint( \$value );";
52             }
53
54             push @code, "\$instance->{'$key'} = \$value;";
55
56             if ($attr->is_weak_ref) {
57                 push @code, "weaken( \$instance->{'$key'} ) if ref( \$value );";
58             }
59
60             if ( $attr->has_trigger ) {
61                 push @code, "\$attrs[$index]->trigger->( \$instance, \$value, \$attrs[$index] );";
62             }
63
64             join "\n", @code;
65         };
66
67         my $make_default_value = do {
68             my @code;
69
70             if ( $attr->has_default || $attr->has_builder ) {
71                 unless ( $attr->is_lazy ) {
72                     my $default = $attr->default;
73                     my $builder = $attr->builder;
74
75                     push @code, "my \$value = ";
76
77                     if ($attr->should_coerce) {
78                         push @code, "\$attrs[$index]->coerce_constraint(";
79                     }
80                         if ($attr->has_builder) {
81                             push @code, "\$instance->$builder";
82                         }
83                         elsif (ref($default) eq 'CODE') {
84                             push @code, "\$attrs[$index]->default()->(\$instance)";
85                         }
86                         elsif (!defined($default)) {
87                             push @code, 'undef';
88                         }
89                         elsif ($default =~ /^\-?[0-9]+(?:\.[0-9]+)$/) {
90                             push @code, $default;
91                         }
92                         else {
93                             push @code, "'$default'";
94                         }
95
96                     if ($attr->should_coerce) {
97                         push @code, ");";
98                     }
99                     else {
100                         push @code, ";";
101                     }
102
103                     if ($attr->has_type_constraint) {
104                         push @code, "\$attrs[$index]->verify_type_constraint(\$value);";
105                     }
106
107                     push @code, "\$instance->{'$key'} = \$value;";
108
109                     if ($attr->is_weak_ref) {
110                         push @code, "weaken( \$instance->{'$key'} ) if ref( \$value );";
111                     }
112                 }
113                 join "\n", @code;
114             }
115             else {
116                 if ( $attr->is_required ) {
117                     qq{Carp::confess("Attribute ($key) is required");};
118                 } else {
119                     ""
120                 }
121             }
122         };
123         my $code = <<"...";
124             {
125                 if (exists(\$args->{'$from'})) {
126                     $set_value;
127 ...
128         if ($make_default_value) {
129             $code .= <<"...";
130                 } else {
131                     $make_default_value;
132 ...
133         }
134         $code .= <<"...";
135                 }
136             }
137 ...
138         push @res, $code;
139     }
140     return join "\n", @res;
141 }
142
143 sub _generate_BUILDARGS {
144     <<'...';
145     do {
146         if ( scalar @_ == 1 ) {
147             if ( defined $_[0] ) {
148                 ( ref( $_[0] ) eq 'HASH' )
149                 || Carp::confess "Single parameters to new() must be a HASH ref";
150                 +{ %{ $_[0] } };
151             }
152             else {
153                 +{};
154             }
155         }
156         else {
157             +{@_};
158         }
159     };
160 ...
161 }
162
163 sub _generate_BUILDALL {
164     my ($class, $meta) = @_;
165     return '' unless $meta->name->can('BUILD');
166
167     my @code = ();
168     push @code, q{no strict 'refs';};
169     push @code, q{no warnings 'once';};
170     no strict 'refs';
171     for my $klass ($meta->linearized_isa) {
172         if (*{ $klass . '::BUILD' }{CODE}) {
173             push  @code, qq{${klass}::BUILD(\$instance, \$args);};
174         }
175     }
176     return join "\n", @code;
177 }
178
179 1;