Add Meta/Method/Accessor.pm
[gitmo/Mouse.git] / lib / Mouse / Meta / Method / Accessor.pm
1 package Mouse::Meta::Method::Accessor;
2 use strict;
3 use warnings;
4
5 sub _install_accessor{
6     my (undef, $attribute, $method_name, $class, $type) = @_;
7
8     my $name          = $attribute->name;
9     my $default       = $attribute->default;
10     my $constraint    = $attribute->type_constraint;
11     my $builder       = $attribute->builder;
12     my $trigger       = $attribute->trigger;
13     my $is_weak       = $attribute->is_weak_ref;
14     my $should_deref  = $attribute->should_auto_deref;
15     my $should_coerce = $attribute->should_coerce;
16
17     my $compiled_type_constraint    = $constraint ? $constraint->{_compiled_type_constraint} : undef;
18
19     my $self  = '$_[0]';
20     my $key   = $attribute->_inlined_name;
21
22     $type ||= 'accessor';
23
24     my $accessor = 
25         '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
26         "sub {\n";
27     if ($type eq 'accessor' || $type eq 'writer') {
28         if($type eq 'accessor'){
29             $accessor .= 
30                 '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
31                 'if (scalar(@_) >= 2) {' . "\n";
32         }
33         else{ # writer
34             $accessor .= 
35                 '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
36                 'if(@_ < 2){ Carp::confess("Not enough arguments for writer '.$method_name.'") }'.
37                 '{' . "\n";
38         }
39                 
40         my $value = '$_[1]';
41
42         if ($constraint) {
43             if ($should_coerce) {
44                 $accessor .=
45                     "\n".
46                     '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
47                     'my $val = Mouse::Util::TypeConstraints->typecast_constraints("'.$attribute->associated_class->name.'", $attribute->{type_constraint}, '.$value.');';
48                 $value = '$val';
49             }
50             if ($compiled_type_constraint) {
51                 $accessor .= 
52                     "\n".
53                     '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
54                     'unless ($compiled_type_constraint->('.$value.')) {
55                         $attribute->verify_type_constraint_error($name, '.$value.', $attribute->{type_constraint});
56                     }' . "\n";
57             } else {
58                 $accessor .= 
59                     "\n".
60                     '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
61                     'unless ($constraint->check('.$value.')) {
62                         $attribute->verify_type_constraint_error($name, '.$value.', $attribute->{type_constraint});
63                     }' . "\n";
64             }
65         }
66
67         # if there's nothing left to do for the attribute we can return during
68         # this setter
69         $accessor .= 'return ' if !$is_weak && !$trigger && !$should_deref;
70
71         $accessor .= $self.'->{'.$key.'} = '.$value.';' . "\n";
72
73         if ($is_weak) {
74             $accessor .= 'Scalar::Util::weaken('.$self.'->{'.$key.'}) if ref('.$self.'->{'.$key.'});' . "\n";
75         }
76
77         if ($trigger) {
78             $accessor .= '$trigger->('.$self.', '.$value.');' . "\n";
79         }
80
81         $accessor .= "}\n";
82     }
83     elsif($type eq 'reader') {
84         $accessor .= 'Carp::confess("Cannot assign a value to a read-only accessor") if scalar(@_) >= 2;' . "\n";
85     }
86     else{
87         $class->throw_error("Unknown accessor type '$type'");
88     }
89
90     if ($attribute->is_lazy) {
91         $accessor .= $self.'->{'.$key.'} = ';
92
93         $accessor .= $attribute->has_builder
94                 ? $self.'->$builder'
95                     : ref($default) eq 'CODE'
96                     ? '$default->('.$self.')'
97                     : '$default';
98         $accessor .= ' if !exists '.$self.'->{'.$key.'};' . "\n";
99     }
100
101     if ($should_deref) {
102         if ($constraint->is_a_type_of('ArrayRef')) {
103             $accessor .= 'if (wantarray) {
104                 return @{ '.$self.'->{'.$key.'} || [] };
105             }';
106         }
107         elsif($constraint->is_a_type_of('HashRef')){
108             $accessor .= 'if (wantarray) {
109                 return %{ '.$self.'->{'.$key.'} || {} };
110             }';
111         }
112         else{
113             $class->throw_error("Can not auto de-reference the type constraint " . $constraint->name);
114         }
115     }
116
117     $accessor .= 'return '.$self.'->{'.$key."};\n}";
118
119     #print $accessor, "\n";
120     my $code = eval $accessor;
121     $attribute->throw_error($@) if $@;
122
123     $class->add_method($method_name => $code);
124     return;
125 }
126
127 sub _install_reader{
128     my $class = shift;
129     $class->_install_accessor(@_, 'reader');
130     return;
131 }
132
133 sub _install_writer{
134     my $class = shift;
135     $class->_install_accessor(@_, 'writer');
136     return;
137 }
138
139
140 sub _install_predicate {
141     my (undef, $attribute, $method_name, $class) = @_;
142
143     my $key = $attribute->_inlined_name;
144
145     my $predicate = 'sub { exists($_[0]->{'.$key.'}) }';
146
147     my $code = eval $predicate;
148     $attribute->throw_error($@) if $@;
149     $class->add_method($method_name => $code);
150     return;
151 }
152
153 sub _install_clearer {
154     my (undef, $attribute, $method_name, $class) = @_;
155
156     my $key = $attribute->_inlined_name;
157
158     my $clearer = 'sub { delete($_[0]->{'.$key.'}) }';
159
160     my $code = eval $clearer;
161     $attribute->throw_error($@) if $@;
162     $class->add_method($method_name => $code);
163     return;
164 }
165
166 sub _install_handles {
167     my (undef, $attribute, $handles, $class) = @_;
168
169     my $reader  = $attribute->name;
170     my %handles = $attribute->_canonicalize_handles($handles);
171
172     my @methods;
173
174     foreach my $local_method (keys %handles) {
175         my $remote_method = $handles{$local_method};
176
177         my $method = 'sub {
178             my $self = shift;
179             $self->'.$reader.'->'.$remote_method.'(@_)
180         }';
181
182         my $code = eval $method;
183         $attribute->throw_error($@) if $@;
184
185         push @methods, ($local_method => $code);
186     }
187
188     # install after all the method compiled successfully
189     while(my($name, $code) = splice @methods, 0, 2){
190         $class->add_method($name, $code);
191     }
192     return;
193 }
194
195
196 1;