Assign the value of the parameter to a lexical so we don't have to continually look...
[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 $part1 = do {
41             my @code;
42
43             push @code, "my \$value = \$args->{'$from'};";
44
45             if ($attr->should_coerce) {
46                 push @code, "\$value = \$attr->coerce_constraint( \$value );";
47             }
48
49             if ($attr->has_type_constraint) {
50                 push @code, "\$attr->verify_type_constraint( \$value );";
51             }
52
53             push @code, "\$instance->{'$key'} = \$value;";
54
55             if ($attr->is_weak_ref) {
56                 push @code, "weaken( \$instance->{'$key'} ) if ref( \$value );";
57             }
58
59             if ( $attr->has_trigger ) {
60                 push @code, "\$attr->trigger->( \$instance, \$value, \$attr );";
61             }
62
63             join "\n", @code;
64         };
65
66         my $part2 = do {
67             my @code;
68             if ( $attr->has_default || $attr->has_builder ) {
69                 unless ( $attr->is_lazy ) {
70                     my $default = $attr->default;
71                     my $builder = $attr->builder;
72                     if ($attr->has_builder) {
73                         push @code, "my \$value = \$instance->$builder;";
74                     } elsif (ref($default) eq 'CODE') {
75                         push @code, "my \$value = \$attr->default()->();";
76                     } else {
77                         push @code, "my \$value = \$attr->default();";
78                     }
79                     if ($attr->should_coerce) {
80                         push @code, "\$value = \$attr->coerce_constraint(\$value);";
81                     }
82                     if ($attr->has_type_constraint) {
83                         push @code, "\$attr->verify_type_constraint(\$value);";
84                     }
85                     push @code, "\$instance->{'$key'} = \$value;";
86                     if ($attr->is_weak_ref) {
87                         push @code, "weaken( \$instance->{'$key'} ) if ref( \$instance->{'$key'} );";
88                     }
89                 }
90                 join "\n", @code;
91             }
92             else {
93                 if ( $attr->is_required ) {
94                     qq{Carp::confess("Attribute ($key) is required");};
95                 } else {
96                     ""
97                 }
98             }
99         };
100         my $code = <<"...";
101             {
102                 my \$attr = \$attrs[$index];
103                 if (exists(\$args->{'$from'})) {
104                     $part1;
105                 } else {
106                     $part2;
107                 }
108             }
109 ...
110         push @res, $code;
111     }
112     return join "\n", @res;
113 }
114
115 sub _generate_BUILDARGS {
116     <<'...';
117     do {
118         if ( scalar @_ == 1 ) {
119             if ( defined $_[0] ) {
120                 ( ref( $_[0] ) eq 'HASH' )
121                 || Carp::confess "Single parameters to new() must be a HASH ref";
122                 +{ %{ $_[0] } };
123             }
124             else {
125                 +{};
126             }
127         }
128         else {
129             +{@_};
130         }
131     };
132 ...
133 }
134
135 sub _generate_BUILDALL {
136     my ($class, $meta) = @_;
137     return '' unless $meta->name->can('BUILD');
138
139     my @code = ();
140     push @code, q{no strict 'refs';};
141     push @code, q{no warnings 'once';};
142     no strict 'refs';
143     for my $class ($meta->linearized_isa) {
144         if (*{ $class . '::BUILD' }{CODE}) {
145             push  @code, qq{${class}::BUILD->(\$instance, \$args);};
146         }
147     }
148     return join "\n", @code;
149 }
150
151 1;