Refactor pp method generators
[gitmo/Mouse.git] / lib / Mouse / Meta / Method / Accessor.pm
1 package Mouse::Meta::Method::Accessor;
2 use Mouse::Util qw(:meta); # enables strict and warnings
3 use warnings FATAL => 'recursion';
4
5 use Mouse::Meta::Method::Constructor; # for slot access
6
7 sub _generate_accessor_any{
8     my($method_class, $type, $attribute, $class) = @_;
9
10     my $c             = 'Mouse::Meta::Method::Constructor';
11
12     my $key           = $attribute->name;
13     my $default       = $attribute->default;
14     my $constraint    = $attribute->type_constraint;
15     my $builder       = $attribute->builder;
16     my $trigger       = $attribute->trigger;
17     my $is_weak       = $attribute->is_weak_ref;
18     my $should_deref  = $attribute->should_auto_deref;
19     my $should_coerce = (defined($constraint) && $constraint->has_coercion && $attribute->should_coerce);
20
21     my $compiled_type_constraint = defined($constraint) ? $constraint->_compiled_type_constraint : undef;
22
23     my $instance  = '$_[0]';
24     my $slot      = $c->_inline_get_slot($instance, $key);;
25
26     my $accessor = sprintf(<<'END_SUB_START', $class->name, __LINE__, $type, $key, __FILE__);
27 package %s;
28 #line %d "%s-accessor for %s (%s)
29 sub {
30 END_SUB_START
31
32     if ($type eq 'rw' || $type eq 'wo') {
33         if($type eq 'rw'){
34             $accessor .= 
35                 'if (scalar(@_) >= 2) {' . "\n";
36         }
37         else{ # writer
38             $accessor .= 
39                 'if(@_ < 2){ Carp::confess("Not enough arguments for the writer of '.$key.'") }'.
40                 '{' . "\n";
41         }
42                 
43         my $value = '$_[1]';
44
45         if (defined $constraint) {
46             if ($should_coerce) {
47                 $accessor .=
48                     "\n".
49                     'my $val = $constraint->coerce('.$value.');';
50                 $value = '$val';
51             }
52             $accessor .= 
53                 "\n".
54                 '$compiled_type_constraint->('.$value.') or
55                     $attribute->_throw_type_constraint_error('.$value.', $constraint);' . "\n";
56         }
57
58         # if there's nothing left to do for the attribute we can return during
59         # this setter
60         $accessor .= 'return ' if !$is_weak && !$trigger && !$should_deref;
61
62         $accessor .= $c->_inline_set_slot($instance, $key, $value) . ";\n";
63
64         if ($is_weak) {
65             $accessor .= $c->_inline_weaken_slot($instance, $key) ." if ref $slot;\n";
66         }
67
68         if ($trigger) {
69             $accessor .= '$trigger->('.$instance.', '.$value.');' . "\n";
70         }
71
72         $accessor .= "}\n";
73     }
74     elsif($type eq 'ro') {
75         $accessor .= 'Carp::confess("Cannot assign a value to a read-only accessor") if scalar(@_) >= 2;' . "\n";
76     }
77     else{
78         $class->throw_error("Unknown accessor type '$type'");
79     }
80
81     if ($attribute->is_lazy) {
82         my $value;
83
84         if (defined $builder){
85             $value = "$instance->\$builder()";
86         }
87         elsif (ref($default) eq 'CODE'){
88             $value = "$instance->\$default()";
89         }
90         else{
91             $value = '$default';
92         }
93
94         $accessor .= sprintf "if(!%s){\n", $c->_inline_has_slot($instance, $key);
95         if($should_coerce){
96             $value = "\$constraint->coerce($value)";
97         }
98         elsif(defined $constraint){
99             $accessor .= "my \$tmp = $value;\n";
100
101             $accessor .= "\$compiled_type_constraint->(\$tmp)";
102             $accessor .= " || \$attribute->_throw_type_constraint_error(\$tmp, \$constraint);\n";
103             $value = '$tmp';
104         }
105
106         $accessor .= $c->_inline_set_slot($instance, $key, $value) .";\n";
107
108         if ($is_weak) {
109             $accessor .= $c->_inline_weaken_slot($instance, $key) . " if ref $slot;\n";
110         }
111         $accessor .= "}\n";
112     }
113
114     if ($should_deref) {
115         if ($constraint->is_a_type_of('ArrayRef')) {
116             $accessor .= "return \@{ $slot || [] } if wantarray;\n";
117         }
118         elsif($constraint->is_a_type_of('HashRef')){
119             $accessor .= "return \%{ $slot || {} } if wantarray;\n";
120         }
121         else{
122             $class->throw_error("Can not auto de-reference the type constraint " . $constraint->name);
123         }
124     }
125
126     $accessor .= "return $slot;\n}\n";
127
128     #print $accessor, "\n";
129     my $code;
130     my $e = do{
131         local $@;
132         $code = eval $accessor;
133         $@;
134     };
135     die $e if $e;
136
137     return $code;
138 }
139
140 sub _generate_accessor{
141     my $class = shift;
142     return $class->_generate_accessor_any(rw => @_);
143 }
144
145 sub _generate_reader {
146     my $class = shift;
147     return $class->_generate_accessor_any(ro => @_);
148 }
149
150 sub _generate_writer {
151     my $class = shift;
152     return $class->_generate_accessor_any(wo => @_);
153 }
154
155 sub _generate_predicate {
156     my (undef, $attribute, $class) = @_;
157
158     my $slot = $attribute->name;
159     return sub{
160         return exists $_[0]->{$slot};
161     };
162 }
163
164 sub _generate_clearer {
165     my (undef, $attribute, $class) = @_;
166
167     my $slot = $attribute->name;
168     return sub{
169         delete $_[0]->{$slot};
170     };
171 }
172
173 1;
174 __END__
175
176 =head1 NAME
177
178 Mouse::Meta::Method::Accessor - A Mouse method generator for accessors
179
180 =head1 VERSION
181
182 This document describes Mouse version 0.50_04
183
184 =head1 SEE ALSO
185
186 L<Moose::Meta::Method::Accessor>
187
188 =cut