File Coverage

blib/lib/Mouse/Meta/Attribute.pm
Criterion Covered Total %
statement 130 138 94.2
branch 53 64 82.8
condition 15 21 71.4
subroutine 22 25 88.0
pod 6 17 35.2
total 226 265 85.2


line stmt bran cond sub pod time code
1             package Mouse::Meta::Attribute;
2 282     282   1937 use Mouse::Util qw(:meta); # enables strict and warnings
  282         735  
  282         1770  
3              
4 282     282   1883 use Carp ();
  282         645  
  282         5111  
5              
6 282     282   109832 use Mouse::Meta::TypeConstraint;
  282         921  
  282         482500  
7              
8             my %valid_options = map { $_ => undef } (
9             'accessor',
10             'auto_deref',
11             'builder',
12             'clearer',
13             'coerce',
14             'default',
15             'documentation',
16             'does',
17             'handles',
18             'init_arg',
19             'insertion_order',
20             'is',
21             'isa',
22             'lazy',
23             'lazy_build',
24             'name',
25             'predicate',
26             'reader',
27             'required',
28             'traits',
29             'trigger',
30             'type_constraint',
31             'weak_ref',
32             'writer',
33              
34             # internally used
35             'associated_class',
36             'associated_methods',
37             '__METACLASS__',
38              
39             # Moose defines, but Mouse doesn't
40             #'definition_context',
41             #'initializer',
42              
43             # special case for AttributeHelpers
44             'provides',
45             'curries',
46             );
47              
48             our @CARP_NOT = qw(Mouse::Meta::Class);
49              
50             sub new {
51 604     604 1 11592 my $class = shift;
52 604         1972 my $name = shift;
53              
54 604         4400 my $args = $class->Mouse::Object::BUILDARGS(@_);
55              
56 604         7034 $class->_process_options($name, $args);
57              
58 592         2340 $args->{name} = $name;
59              
60             # check options
61             # (1) known by core
62 592         1790 my @bad = grep{ !exists $valid_options{$_} } keys %{$args};
  3567         11817  
  592         3389  
63              
64             # (2) known by subclasses
65 592 100 100     3133 if(@bad && $class ne __PACKAGE__){
66             my %valid_attrs = (
67 12         44 map { $_ => undef }
68 12         33 grep { defined }
69 8         46 map { $_->init_arg() }
  12         53  
70             $class->meta->get_all_attributes()
71             );
72 8         24 @bad = grep{ !exists $valid_attrs{$_} } @bad;
  9         32  
73             }
74              
75             # (3) bad options found
76 592 100       2411 if(@bad){
77 1         17 Carp::carp(
78             "Found unknown argument(s) passed to '$name' attribute constructor in '$class': "
79             . Mouse::Util::english_list(@bad));
80             }
81              
82 592         2601 my $self = bless $args, $class;
83 592 100       4319 if($class ne __PACKAGE__){
84 25         109 $class->meta->_initialize_object($self, $args);
85             }
86 592         3545 return $self;
87             }
88              
89 0 0   0 0 0 sub has_read_method { $_[0]->has_reader || $_[0]->has_accessor }
90 0 0   0 0 0 sub has_write_method { $_[0]->has_writer || $_[0]->has_accessor }
91              
92 34 100   34 0 316 sub get_read_method { $_[0]->reader || $_[0]->accessor }
93 4 50   4 0 40 sub get_write_method { $_[0]->writer || $_[0]->accessor }
94              
95             sub get_read_method_ref{
96 12     12 1 100 my($self) = @_;
97             return $self->{_mouse_cache_read_method_ref}
98 12   66     101 ||= $self->_get_accessor_method_ref('get_read_method', '_generate_reader');
99             }
100              
101             sub get_write_method_ref{
102 6     6 1 23 my($self) = @_;
103             return $self->{_mouse_cache_write_method_ref}
104 6   66     45 ||= $self->_get_accessor_method_ref('get_write_method', '_generate_writer');
105             }
106              
107             sub interpolate_class{
108 595     595 0 1576 my($class, $args) = @_;
109              
110 595 100       2033 if(my $metaclass = delete $args->{metaclass}){
111 12         48 $class = Mouse::Util::resolve_metaclass_alias( Attribute => $metaclass );
112             }
113              
114 595         1167 my @traits;
115 595 100       1805 if(my $traits_ref = delete $args->{traits}){
116              
117 11         31 for (my $i = 0; $i < @{$traits_ref}; $i++) {
  22         75  
118 11         60 my $trait = Mouse::Util::resolve_metaclass_alias(Attribute => $traits_ref->[$i], trait => 1);
119              
120 11 100       95 next if $class->does($trait);
121              
122 10         32 push @traits, $trait;
123              
124             # are there options?
125 10 100       71 push @traits, $traits_ref->[++$i]
126             if ref($traits_ref->[$i+1]);
127             }
128              
129 11 100       51 if (@traits) {
130 10         182 $class = Mouse::Meta::Class->create_anon_class(
131             superclasses => [ $class ],
132             roles => \@traits,
133             cache => 1,
134             )->name;
135             }
136             }
137              
138 595         2378 return( $class, @traits );
139             }
140              
141             sub verify_against_type_constraint {
142 1     1 1 4 my ($self, $value) = @_;
143              
144 1         3 my $type_constraint = $self->{type_constraint};
145 1 50       6 return 1 if !$type_constraint;
146 0 0       0 return 1 if $type_constraint->check($value);
147              
148 0         0 $self->_throw_type_constraint_error($value, $type_constraint);
149             }
150              
151             sub _throw_type_constraint_error {
152 0     0   0 my($self, $value, $type) = @_;
153              
154 0         0 $self->throw_error(
155             sprintf q{Attribute (%s) does not pass the type constraint because: %s},
156             $self->name,
157             $type->get_message($value),
158             );
159             }
160              
161             sub illegal_options_for_inheritance {
162 34     34 0 125 return qw(reader writer accessor clearer predicate);
163             }
164              
165             sub clone_and_inherit_options{
166 33     33 1 68 my $self = shift;
167 33         168 my $args = $self->Mouse::Object::BUILDARGS(@_);
168              
169 33         109 foreach my $illegal($self->illegal_options_for_inheritance) {
170 163 100 100     433 if(exists $args->{$illegal} and exists $self->{$illegal}) {
171 4         28 $self->throw_error("Illegal inherited option: $illegal");
172             }
173             }
174              
175 29         69 foreach my $name(keys %{$self}){
  29         113  
176 243 100       557 if(!exists $args->{$name}){
177 226         511 $args->{$name} = $self->{$name}; # inherit from self
178             }
179             }
180              
181 29         138 my($attribute_class, @traits) = ref($self)->interpolate_class($args);
182 29 100       97 $args->{traits} = \@traits if @traits;
183              
184             # remove temporary caches
185 29         60 foreach my $attr(keys %{$args}){
  29         117  
186 254 100       620 if($attr =~ /\A _mouse_cache_/xms){
187 2         8 delete $args->{$attr};
188             }
189             }
190              
191             # remove default if lazy_build => 1
192 29 100       114 if($args->{lazy_build}) {
193 1         2 delete $args->{default};
194             }
195              
196 29         139 return $attribute_class->new($self->name, $args);
197             }
198              
199              
200             sub _get_accessor_method_ref {
201 17     17   54 my($self, $type, $generator) = @_;
202              
203 17   33     81 my $metaclass = $self->associated_class
204             || $self->throw_error('No asocciated class for ' . $self->name);
205              
206 17         92 my $accessor = $self->$type();
207 17 100       65 if($accessor){
208 10         62 return $metaclass->get_method_body($accessor);
209             }
210             else{
211 7         117 return $self->accessor_metaclass->$generator($self, $metaclass);
212             }
213             }
214              
215             sub set_value {
216 4     4 0 2042 my($self, $object, $value) = @_;
217 4         18 return $self->get_write_method_ref()->($object, $value);
218             }
219              
220             sub get_value {
221 3     3 0 9 my($self, $object) = @_;
222 3         9 return $self->get_read_method_ref()->($object);
223             }
224              
225             sub has_value {
226 6     6 0 12 my($self, $object) = @_;
227             my $accessor_ref = $self->{_mouse_cache_predicate_ref}
228 6   66     20 ||= $self->_get_accessor_method_ref('predicate', '_generate_predicate');
229              
230 6         33 return $accessor_ref->($object);
231             }
232              
233             sub clear_value {
234 2     2 0 4 my($self, $object) = @_;
235             my $accessor_ref = $self->{_mouse_cache_crealer_ref}
236 2   66     10 ||= $self->_get_accessor_method_ref('clearer', '_generate_clearer');
237              
238 2         7 return $accessor_ref->($object);
239             }
240              
241             sub associate_method{
242             #my($attribute, $method_name) = @_;
243 632     632 1 1397 my($attribute) = @_;
244 632         1743 $attribute->{associated_methods}++;
245 632         1599 return;
246             }
247              
248             sub install_accessors{
249 589     589 0 1979 my($attribute) = @_;
250              
251 589         2427 my $metaclass = $attribute->associated_class;
252 589         2455 my $accessor_class = $attribute->accessor_metaclass;
253              
254 589         2104 foreach my $type(qw(accessor reader writer predicate clearer)){
255 2945 100       8680 if(exists $attribute->{$type}){
256 611         1546 my $generator = '_generate_' . $type;
257 611         11496 my $code = $accessor_class->$generator($attribute, $metaclass);
258 611         1685 my $name = $attribute->{$type};
259             # TODO: do something for compatibility
260             # if( $metaclass->name->can($name) ) {
261             # my $t = $metaclass->has_method($name) ? 'method' : 'function';
262             # Carp::cluck("You are overwriting a locally defined $t"
263             # . " ($name) with an accessor");
264             # }
265 611         6045 $metaclass->add_method($name => $code);
266 611         1952 $attribute->associate_method($name);
267             }
268             }
269              
270             # install delegation
271 589 100       2394 if(exists $attribute->{handles}){
272 19         61 my %handles = $attribute->_canonicalize_handles();
273 17         199 while(my($handle, $method_to_call) = each %handles){
274 39 100       298 next if Mouse::Object->can($handle);
275              
276 23 100       99 if($metaclass->has_method($handle)) {
277 2         26 $attribute->throw_error("You cannot overwrite a locally defined method ($handle) with a delegation");
278             }
279              
280 21         74 $metaclass->add_method($handle =>
281             $attribute->_make_delegation_method(
282             $handle, $method_to_call));
283              
284 21         70 $attribute->associate_method($handle);
285             }
286             }
287              
288 585         2965 return;
289             }
290              
291             sub delegation_metaclass() { ## no critic
292             'Mouse::Meta::Method::Delegation'
293             }
294              
295             sub _canonicalize_handles {
296 19     19   37 my($self) = @_;
297 19         46 my $handles = $self->{handles};
298              
299 19         41 my $handle_type = ref $handles;
300 19 100       91 if ($handle_type eq 'HASH') {
    100          
    100          
    50          
301 2         11 return %$handles;
302             }
303             elsif ($handle_type eq 'ARRAY') {
304 8         23 return map { $_ => $_ } @$handles;
  10         49  
305             }
306             elsif ($handle_type eq 'Regexp') {
307 4         14 my $meta = $self->_find_delegate_metaclass();
308 13         45 return map { $_ => $_ }
309 3 50       37 grep { /$handles/ }
  22         104  
310             Mouse::Util::is_a_metarole($meta)
311             ? $meta->get_method_list
312             : $meta->get_all_method_names;
313             }
314             elsif ($handle_type eq 'CODE') {
315 5         14 return $handles->( $self, $self->_find_delegate_metaclass() );
316             }
317             else {
318 0         0 $self->throw_error("Unable to canonicalize the 'handles' option with $handles");
319             }
320             }
321              
322             sub _find_delegate_metaclass {
323 9     9   15 my($self) = @_;
324 9         17 my $meta;
325 9 100       29 if($self->{isa}) {
    50          
326 7         41 $meta = Mouse::Meta::Class->initialize("$self->{isa}");
327             }
328             elsif($self->{does}) {
329 0         0 $meta = Mouse::Util::get_metaclass_by_name("$self->{does}");
330             }
331 9 100       42 defined($meta) or $self->throw_error(
332             "Cannot find delegate metaclass for attribute " . $self->name);
333 7         22 return $meta;
334             }
335              
336              
337             sub _make_delegation_method {
338 21     21   58 my($self, $handle, $method_to_call) = @_;
339 21         100 return Mouse::Util::load_class($self->delegation_metaclass)
340             ->_generate_delegation($self, $handle, $method_to_call);
341             }
342              
343             1;
344             __END__