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