File Coverage

blib/lib/MooX/late.pm
Criterion Covered Total %
statement 119 129 92.2
branch 21 32 65.6
condition 5 19 26.3
subroutine 30 30 100.0
pod 0 3 0.0
total 175 213 82.1


line stmt bran cond sub pod time code
1 8     41   1278979 use 5.008;
  8         32  
  8         367  
2 8     41   60 use strict;
  8         18  
  8         279  
3 8     41   39 use warnings;
  8         20  
  8         611  
4              
5             package MooX::late;
6             our $AUTHORITY = 'cpan:TOBYINK';
7             our $VERSION = '0.015';
8              
9 8     41   2198 use Moo qw( );
  8         4699  
  8         192  
10 8     41   47 use Carp qw( carp croak );
  8         12  
  8         832  
11 8     41   45 use Scalar::Util qw( blessed );
  8         13  
  8         935  
12 8     41   113 use Module::Runtime qw( is_module_name );
  8         16  
  8         68  
13              
14             BEGIN {
15             package MooX::late::DefinitionContext;
16 8     41   2304 our $AUTHORITY = 'cpan:TOBYINK';
17 8         18 our $VERSION = '0.015';
18            
19 8     41   854 use Moo;
  8         15  
  8         42  
20             use overload (
21             q[""] => 'to_string',
22 0     33   0 q[bool] => sub { 1 },
23 8         99 fallback => 1,
24 8     41   15127 );
  8         6473  
25            
26 8         44 has package => (is => 'ro');
27 8         216055 has filename => (is => 'ro');
28 8         10732 has line => (is => 'ro');
29            
30             sub to_string
31             {
32 0     33 0 0 my $self = shift;
33 0         0 sprintf(
34             '%s:%d, package %s',
35             $self->filename,
36             $self->line,
37             $self->package,
38             );
39             }
40            
41             sub new_from_caller
42             {
43 44     77 0 119 my ($class, $level) = @_;
44 44 50       157 $level = 0 unless defined $level;
45            
46 44         278 my ($p, $f, $c) = caller($level + 1);
47 44         1177 return $class->new(
48             package => $p,
49             filename => $f,
50             line => $c,
51             );
52             }
53             };
54              
55             # SUBCLASSING
56             # This is a hook for people subclassing MooX::late.
57             # It should be easy to tack on your own handlers
58             # to the end of the list. A handler is only called
59             # if exists($spec{$handler_name}) in the attribute
60             # spec.
61             #
62             sub _handlers
63             {
64 44     77   189 qw( isa does lazy_build traits );
65             }
66              
67             # SUBCLASSING
68             # Not really sure why you'd want to override
69             # this.
70             #
71             sub _definition_context_class
72             {
73 44     77   418 "MooX::late::DefinitionContext";
74             }
75              
76             sub import
77             {
78 44     77   152205 my $me = shift;
79 44         163 my $caller = caller;
80            
81 44         230 my $install_tracked;
82             {
83 8     41   3342 no warnings;
  8         16  
  8         13140  
  44         72  
84 44 100       176 if ($Moo::MAKERS{$caller})
    50          
85             {
86 42         108 $install_tracked = \&Moo::_install_tracked;
87             }
88             elsif ($Moo::Role::INFO{$caller})
89             {
90 2         6 $install_tracked = \&Moo::Role::_install_tracked;
91             }
92             else
93             {
94 0         0 croak "MooX::late applied to a non-Moo package"
95             . "(need: use Moo or use Moo::Role)";
96             }
97             }
98            
99 44 50       1186 my $orig = $caller->can('has') # lolcat
100             or croak "Could not locate 'has' function to alter";
101            
102 44         172 my @handlers = $me->_handlers;
103            
104             # SUBCLASSING
105             # MooX::late itself does not provide a
106             # `_finalize_attribute` method. Your subclass
107             # can, in which case it will be called right
108             # before setting up the attribute.
109             #
110 44         315 my $finalize = $me->can("_finalize_attribute");
111            
112             $install_tracked->(
113             $caller, has => sub
114             {
115 44     77   21980 my ($proto, %spec) = @_;
        77      
        73      
        69      
        34      
116 44         1585 my $context = $me->_definition_context_class->new_from_caller(0);
117            
118 44 100       12202 for my $name (ref $proto ? @$proto : $proto)
119             {
120 46         1939 my $spec = +{ %spec }; # shallow clone
121            
122 46         132 for my $option (@handlers)
123             {
124 181 100       613 next unless exists $spec->{$option};
125 52         347 my $handler = $me->can("_handle_$option");
126            
127             # SUBCLASSING
128             # Note that handlers are called as methods, and
129             # get passed:
130             # 1. the attribute name
131             # 2. the attribute spec (hashref, modifiable)
132             # 3. a context object
133             # 4. the name of the caller class/role
134             #
135 52         195 $me->$handler($name, $spec, $context, $caller);
136             }
137            
138 45 50       164 $me->$finalize($name, $spec, $context, $caller) if $finalize;
139 45         289 $orig->($name, %$spec);
140             }
141 43         83284 return;
142             },
143 44         476 );
144            
145 44         1384 $me->_install_sugar($caller, $install_tracked);
146             }
147              
148             # SUBCLASSING
149             # This can be used to install additional functions
150             # into the caller package.
151             #
152             sub _install_sugar
153             {
154 44     77   88 my $me = shift;
155 44         81 my ($caller, $installer) = @_;
156 44         153 $installer->($caller, blessed => \&Scalar::Util::blessed);
157 44         5937 $installer->($caller, confess => \&Carp::confess);
158             }
159              
160             sub _handle_isa
161             {
162 44     77   104 my $me = shift;
163 44         85 my ($name, $spec, $context, $class) = @_;
164 44 100       369 return if ref $spec->{isa};
165            
166 43         6792 require Type::Utils;
167 43         225943 $spec->{isa} = Type::Utils::dwim_type($spec->{isa}, for => $class);
168            
169 42         679553 return;
170             }
171              
172             sub _handle_does
173             {
174 1     34   3 my $me = shift;
175 1         2 my ($name, $spec, $context, $class) = @_;
176 1 50       5 return unless defined $spec->{does};
177            
178 1         1129 require Types::Standard;
179 1         85552 $spec->{isa} = Types::Standard::ConsumerOf()->of($spec->{does});
180            
181 1         3359 return;
182             }
183              
184             sub _handle_lazy_build
185             {
186 5     38   10 my $me = shift;
187 5         11 my ($name, $spec, $context, $class) = @_;
188 5 50       19 return unless delete $spec->{lazy_build};
189            
190 5   50     17 $spec->{is} ||= "ro";
191 5   50     32 $spec->{lazy} ||= 1;
192 5   33     27 $spec->{builder} ||= "_build_$name";
193            
194 5 50       19 if ($name =~ /^_/)
195             {
196 0   0     0 $spec->{clearer} ||= "_clear$name";
197 0   0     0 $spec->{predicate} ||= "_has$name";
198             }
199             else
200             {
201 5   33     27 $spec->{clearer} ||= "clear_$name";
202 5   33     29 $spec->{predicate} ||= "has_$name";
203             }
204            
205 5         12 return;
206             }
207              
208             sub _handle_traits
209             {
210 2     35   4 my $me = shift;
211 2         5 my ($name, $spec, $context, $class) = @_;
212            
213 2         3 my @new;
214 2 50       4 foreach my $trait (@{ $spec->{traits} || [] })
  2         11  
215             {
216 2         11 my $handler = $me->can("_handletrait_$trait");
217 2 50       6 croak "$me cannot process trait $trait" unless $handler;
218            
219             # SUBCLASSING
220             # There is a second level of handlers for traits.
221             # Just add a method called "_handletrait_Foo"
222             # and it will be called to handle the trait "Foo".
223             # These handlers should normally return the empty
224             # list, but may return a list of strings to add to
225             # a *new* traits arrayref.
226             #
227 2         8 push @new, $me->$handler(@_);
228             }
229            
230 2         7 $spec->{traits} = \@new;
231            
232 2 50       10 if ($spec->{handles_via})
233             {
234 2 50       124 eval "require MooX::HandlesVia"
235             or croak("Requires MooX::HandlesVia for attribute trait defined at $context");
236            
237 2         19 my ($name, %spec) = MooX::HandlesVia::process_has($name, %$spec);
238 2         21198 %$spec = %spec;
239             }
240            
241 2         15 return;
242             }
243              
244             sub _handletrait_Array
245             {
246 1     34   2 my $me = shift;
247 1         4 my ($name, $spec, $context, $class) = @_;
248            
249 1         3 $spec->{handles_via} = "Data::Perl::Collection::Array::MooseLike";
250            
251 1         3 return;
252             }
253              
254             sub _handletrait_Hash
255             {
256 0     33   0 my $me = shift;
257 0         0 my ($name, $spec, $context, $class) = @_;
258            
259 0         0 $spec->{handles_via} = "Data::Perl::Collection::Hash::MooseLike";
260            
261 0         0 return;
262             }
263              
264             sub _handletrait_Code
265             {
266 1     34   2 my $me = shift;
267 1         2 my ($name, $spec, $context, $class) = @_;
268            
269 1         3 $spec->{handles_via} = "Data::Perl::Code";
270            
271             # Special handling for execute_method!
272 1         1 while (my ($k, $v) = each %{ $spec->{handles} })
  3         12  
273             {
274 2 100       5 next unless $v eq q(execute_method);
275            
276             # MooX::HandlesVia can't handle this right yet.
277 1         3 delete $spec->{handles}{$k};
278            
279             # ... so we handle it ourselves.
280 1       0 88 eval qq{
  1         10437  
  1         10  
281             package ${class};
282             sub ${k} {
283             my \$self = shift;
284             return \$self->${name}->(\$self, \@_);
285             }
286             };
287             }
288            
289 1         5 return;
290             }
291              
292             1;
293              
294             __END__