File Coverage

blib/lib/MooX/late.pm
Criterion Covered Total %
statement 118 129 91.4
branch 21 32 65.6
condition 5 19 26.3
subroutine 30 30 100.0
pod 0 3 0.0
total 174 213 81.6


line stmt bran cond sub pod time code
1 8     41   639501 use 5.008;
  8         45  
2 8     41   39 use strict;
  8         12  
  8         170  
3 8     41   48 use warnings;
  8         27  
  8         447  
4              
5             package MooX::late;
6             our $AUTHORITY = 'cpan:TOBYINK';
7             our $VERSION = '0.016';
8              
9 8     41   980 use Moo qw( );
  8         4415  
  8         175  
10 8     41   45 use Carp qw( carp croak );
  8         19  
  8         383  
11 8     41   42 use Scalar::Util qw( blessed );
  8         14  
  8         365  
12 8     41   45 use Module::Runtime qw( is_module_name );
  8         15  
  8         49  
13              
14 0         0 BEGIN {
15             package MooX::late::DefinitionContext;
16 8     41   1532 our $AUTHORITY = 'cpan:TOBYINK';
17 8         17 our $VERSION = '0.016';
18            
19 8     41   623 use Moo;
  8         46  
  8         51  
20             use overload (
21             q[""] => 'to_string',
22 0     33   0 q[bool] => sub { 1 },
23 8         85 fallback => 1,
24 8     41   10678 );
  8         5444  
25            
26 8         35 has package => (is => 'ro');
27 8         148713 has filename => (is => 'ro');
28 8         1883 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 86 my ($class, $level) = @_;
44 44 50       111 $level = 0 unless defined $level;
45            
46 44         254 my ($p, $f, $c) = caller($level + 1);
47 44         683 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   130 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   175 "MooX::late::DefinitionContext";
74             }
75              
76             sub import
77             {
78 44     77   41166 my $me = shift;
79 44         106 my $caller = caller;
80            
81 44         62 my $install_tracked;
82             {
83 8     41   2495 no warnings;
  8         15  
  8         8269  
  44         62  
84 44 100       133 if ($Moo::MAKERS{$caller})
    50          
85             {
86 42         86 $install_tracked = \&Moo::_install_tracked;
87             }
88             elsif ($Moo::Role::INFO{$caller})
89             {
90 2         5 $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       403 my $orig = $caller->can('has') # lolcat
100             or croak "Could not locate 'has' function to alter";
101            
102 44         110 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         157 my $finalize = $me->can("_finalize_attribute");
111            
112             $install_tracked->(
113             $caller, has => sub
114             {
115 44     77   20698 my ($proto, %spec) = @_;
        77      
        73      
        69      
        34      
116 44         154 my $context = $me->_definition_context_class->new_from_caller(0);
117            
118 44 100       10483 for my $name (ref $proto ? @$proto : $proto)
119             {
120 46         3644 my $spec = +{ %spec }; # shallow clone
121            
122 46         87 for my $option (@handlers)
123             {
124 181 100       392 next unless exists $spec->{$option};
125 52         242 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         128 $me->$handler($name, $spec, $context, $caller);
136             }
137            
138 45 50       121 $me->$finalize($name, $spec, $context, $caller) if $finalize;
139 45         182 $orig->($name, %$spec);
140             }
141 43         41788 return;
142             },
143 44         359 );
144            
145 44         1151 $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   69 my $me = shift;
155 44         74 my ($caller, $installer) = @_;
156 44         114 $installer->($caller, blessed => \&Scalar::Util::blessed);
157 44         940 $installer->($caller, confess => \&Carp::confess);
158             }
159              
160             sub _handle_isa
161             {
162 44     77   69 my $me = shift;
163 44         92 my ($name, $spec, $context, $class) = @_;
164 44 100       105 return if ref $spec->{isa};
165            
166 43         3049 require Type::Utils;
167 43         143287 $spec->{isa} = Type::Utils::dwim_type($spec->{isa}, for => $class);
168            
169 42         310740 return;
170             }
171              
172             sub _handle_does
173             {
174 1     34   2 my $me = shift;
175 1         4 my ($name, $spec, $context, $class) = @_;
176 1 50       4 return unless defined $spec->{does};
177            
178 1         567 require Types::Standard;
179 1         83758 $spec->{isa} = Types::Standard::ConsumerOf()->of($spec->{does});
180            
181 1         2449 return;
182             }
183              
184             sub _handle_lazy_build
185             {
186 5     38   8 my $me = shift;
187 5         12 my ($name, $spec, $context, $class) = @_;
188 5 50       15 return unless delete $spec->{lazy_build};
189            
190 5   50     12 $spec->{is} ||= "ro";
191 5   50     26 $spec->{lazy} ||= 1;
192 5   33     26 $spec->{builder} ||= "_build_$name";
193            
194 5 50       13 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     22 $spec->{clearer} ||= "clear_$name";
202 5   33     14 $spec->{predicate} ||= "has_$name";
203             }
204            
205 5         11 return;
206             }
207              
208             sub _handle_traits
209             {
210 2     35   3 my $me = shift;
211 2         5 my ($name, $spec, $context, $class) = @_;
212            
213 2         3 my @new;
214 2 50       3 foreach my $trait (@{ $spec->{traits} || [] })
  2         7  
215             {
216 2         9 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         6 push @new, $me->$handler(@_);
228             }
229            
230 2         5 $spec->{traits} = \@new;
231            
232 2 50       8 if ($spec->{handles_via})
233             {
234 2 50       89 eval "require MooX::HandlesVia"
235             or croak("Requires MooX::HandlesVia for attribute trait defined at $context");
236            
237 2         15 my ($name, %spec) = MooX::HandlesVia::process_has($name, %$spec);
238 2         17809 %$spec = %spec;
239             }
240            
241 2         6 return;
242             }
243              
244             sub _handletrait_Array
245             {
246 1     34   2 my $me = shift;
247 1         3 my ($name, $spec, $context, $class) = @_;
248            
249 1         11 $spec->{handles_via} = "Data::Perl::Collection::Array::MooseLike";
250            
251 1         4 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   3 my $me = shift;
267 1         3 my ($name, $spec, $context, $class) = @_;
268            
269 1         3 $spec->{handles_via} = "Data::Perl::Code";
270            
271             # Special handling for execute_method!
272 1         2 while (my ($k, $v) = each %{ $spec->{handles} })
  3         14  
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 96 eval qq{
  1         5200  
  1         6  
281             package ${class};
282             sub ${k} {
283             my \$self = shift;
284             return \$self->${name}->(\$self, \@_);
285             }
286             };
287             }
288            
289 1         3 return;
290             }
291              
292             1;
293              
294             __END__