added Mouse::Meta::TypeConstraint and use it. Mouse::Meta::Attribute->type_constraint...
[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;
9     my $buildall = $class->_generate_BUILDALL($meta);
10     my $buildargs = $class->_generate_BUILDARGS($meta);
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     local $@;
25     my $res = eval $code;
26     die $@ if $@;
27     $res;
28 }
29
30 sub _generate_processattrs {
31     my ($class, $meta, $attrs) = @_;
32     my @res;
33
34     for my $index (0 .. @$attrs - 1) {
35         my $attr = $attrs->[$index];
36         my $key  = $attr->name;
37         my $code = '';
38
39         if (defined $attr->init_arg) {
40             my $from = $attr->init_arg;
41
42             $code .= "if (exists \$args->{'$from'}) {\n";
43
44             if ($attr->should_coerce && $attr->type_constraint) {
45                 $code .= "my \$value = Mouse::Util::TypeConstraints->typecast_constraints('".$attr->associated_class->name."', \$attrs[$index]->{type_constraint}, \$attrs[$index]->{type_constraint}, \$args->{'$from'});\n";
46             }
47             else {
48                 $code .= "my \$value = \$args->{'$from'};\n";
49             }
50
51             if ($attr->has_type_constraint) {
52                 $code .= "{
53                     unless (\$attrs[$index]->{type_constraint}->check(\$value)) {
54                         \$attrs[$index]->verify_type_constraint_error('$key', \$_, \$attrs[$index]->type_constraint)
55                     }
56                 }";
57             }
58
59             $code .= "\$instance->{'$key'} = \$value;\n";
60
61             if ($attr->is_weak_ref) {
62                 $code .= "Scalar::Util::weaken( \$instance->{'$key'} ) if ref( \$value );\n";
63             }
64
65             if ($attr->has_trigger) {
66                 $code .= "\$attrs[$index]->{trigger}->( \$instance, \$value );\n";
67             }
68
69             $code .= "\n} else {\n";
70         }
71
72         if ($attr->has_default || $attr->has_builder) {
73             unless ($attr->is_lazy) {
74                 my $default = $attr->default;
75                 my $builder = $attr->builder;
76
77                 $code .= "my \$value = ";
78
79                 if ($attr->should_coerce && $attr->type_constraint) {
80                     $code .= "Mouse::Util::TypeConstraints->typecast_constraints('".$attr->associated_class->name."', \$attrs[$index]->{type_constraint}, \$attrs[$index]->{type_constraint}, ";
81                 }
82
83                     if ($attr->has_builder) {
84                         $code .= "\$instance->$builder";
85                     }
86                     elsif (ref($default) eq 'CODE') {
87                         $code .= "\$attrs[$index]->{default}->(\$instance)";
88                     }
89                     elsif (!defined($default)) {
90                         $code .= 'undef';
91                     }
92                     elsif ($default =~ /^\-?[0-9]+(?:\.[0-9]+)$/) {
93                         $code .= $default;
94                     }
95                     else {
96                         $code .= "'$default'";
97                     }
98
99                 if ($attr->should_coerce) {
100                     $code .= ");\n";
101                 }
102                 else {
103                     $code .= ";\n";
104                 }
105
106                 if ($attr->has_type_constraint) {
107                     $code .= "{
108                         unless (\$attrs[$index]->{type_constraint}->check(\$value)) {
109                             \$attrs[$index]->verify_type_constraint_error('$key', \$_, \$attrs[$index]->type_constraint)
110                         }
111                     }";
112                 }
113
114                 $code .= "\$instance->{'$key'} = \$value;\n";
115
116                 if ($attr->is_weak_ref) {
117                     $code .= "Scalar::Util::weaken( \$instance->{'$key'} ) if ref( \$value );\n";
118                 }
119             }
120         }
121         elsif ($attr->is_required) {
122             $code .= "Carp::confess('Attribute ($key) is required');";
123         }
124
125         $code .= "}\n" if defined $attr->init_arg;
126
127         push @res, $code;
128     }
129
130     return join "\n", @res;
131 }
132
133 sub _generate_BUILDARGS {
134     my $self = shift;
135     my $meta = shift;
136
137     if ($meta->name->can('BUILDARGS') && $meta->name->can('BUILDARGS') != Mouse::Object->can('BUILDARGS')) {
138         return '$class->BUILDARGS(@_)';
139     }
140
141     return <<'...';
142     do {
143         if ( scalar @_ == 1 ) {
144             ( ref( $_[0] ) eq 'HASH' )
145                 || Carp::confess "Single parameters to new() must be a HASH ref";
146             +{ %{ $_[0] } };
147         }
148         else {
149             +{@_};
150         }
151     };
152 ...
153 }
154
155 sub _generate_BUILDALL {
156     my ($class, $meta) = @_;
157     return '' unless $meta->name->can('BUILD');
158
159     my @code = ();
160     push @code, q{no strict 'refs';};
161     push @code, q{no warnings 'once';};
162     no strict 'refs';
163     no warnings 'once';
164     for my $klass ($meta->linearized_isa) {
165         if (*{ $klass . '::BUILD' }{CODE}) {
166             push  @code, qq{${klass}::BUILD(\$instance, \$args);};
167         }
168     }
169     return join "\n", @code;
170 }
171
172 1;