File Coverage

blib/lib/MooX/late.pm
Criterion Covered Total %
statement 92 99 92.9
branch 15 22 68.1
condition 5 19 26.3
subroutine 30 30 100.0
pod 0 2 0.0
total 142 172 82.5


line stmt bran cond sub pod time code
1 7     40   530485 use 5.008;
  7         38  
2 7     40   32 use strict;
  7         9  
  7         144  
3 7     40   27 use warnings;
  7         11  
  7         352  
4              
5             package MooX::late;
6             our $AUTHORITY = 'cpan:TOBYINK';
7             our $VERSION = '0.100';
8              
9 7     40   962 use Moo qw( );
  7         4540  
  7         169  
10 7     40   31 use Carp qw( carp croak );
  7         12  
  7         324  
11 7     40   36 use Scalar::Util qw( blessed );
  7         9  
  7         317  
12 7     40   39 use Module::Runtime qw( is_module_name );
  7         18  
  7         83  
13              
14 0         0 BEGIN {
15             package MooX::late::DefinitionContext;
16 7     40   1360 our $AUTHORITY = 'cpan:TOBYINK';
17 7         12 our $VERSION = '0.100';
18            
19 7     40   565 use Moo;
  7         9  
  7         32  
20             use overload (
21             q[""] => 'to_string',
22 0     33   0 q[bool] => sub { 1 },
23 7         56 fallback => 1,
24 7     40   9291 );
  7         4284  
25            
26 7         31 has package => (is => 'ro');
27 7         124031 has filename => (is => 'ro');
28 7         1510 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 42     75 0 82 my ($class, $level) = @_;
44 42 50       107 $level = 0 unless defined $level;
45            
46 42         259 my ($p, $f, $c) = caller($level + 1);
47 42         676 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 42     75   120 qw( isa does lazy_build );
65             }
66              
67             # SUBCLASSING
68             # Not really sure why you'd want to override
69             # this.
70             #
71             sub _definition_context_class
72             {
73 42     75   171 "MooX::late::DefinitionContext";
74             }
75              
76             sub import
77             {
78 42     75   39960 my $me = shift;
79 42         89 my $caller = caller;
80            
81 42         3233 require Sub::HandlesVia;
82 42         30642 require Sub::HandlesVia::Toolkit::Moo;
83 42         654556 'Sub::HandlesVia::Toolkit::Moo'->setup_for($caller);
84            
85 42         2344 my $install_tracked;
86             {
87 7     40   2325 no warnings;
  7         26  
  7         4742  
  42         58  
88 42 100       114 if ($Moo::MAKERS{$caller})
    50          
89             {
90 40         67 $install_tracked = \&Moo::_install_tracked;
91             }
92             elsif ($Moo::Role::INFO{$caller})
93             {
94 2         5 $install_tracked = \&Moo::Role::_install_tracked;
95             }
96             else
97             {
98 0         0 croak "MooX::late applied to a non-Moo package"
99             . "(need: use Moo or use Moo::Role)";
100             }
101             }
102            
103 42 50       350 my $orig = $caller->can('has') # lolcat
104             or croak "Could not locate 'has' function to alter";
105            
106 42         135 my @handlers = $me->_handlers;
107            
108             # SUBCLASSING
109             # MooX::late itself does not provide a
110             # `_finalize_attribute` method. Your subclass
111             # can, in which case it will be called right
112             # before setting up the attribute.
113             #
114 42         158 my $finalize = $me->can("_finalize_attribute");
115            
116             $install_tracked->(
117             $caller, has => sub
118             {
119 42     75   12013 my ($proto, %spec) = @_;
        75      
        71      
        69      
        33      
        33      
        33      
        33      
        33      
120 42         135 my $context = $me->_definition_context_class->new_from_caller(0);
121            
122 42 100       9365 for my $name (ref $proto ? @$proto : $proto)
123             {
124 44         3663 my $spec = +{ %spec }; # shallow clone
125            
126 44         85 for my $option (@handlers)
127             {
128 130 100       284 next unless exists $spec->{$option};
129 48         212 my $handler = $me->can("_handle_$option");
130            
131             # SUBCLASSING
132             # Note that handlers are called as methods, and
133             # get passed:
134             # 1. the attribute name
135             # 2. the attribute spec (hashref, modifiable)
136             # 3. a context object
137             # 4. the name of the caller class/role
138             #
139 48         121 $me->$handler($name, $spec, $context, $caller);
140             }
141            
142 43 50       87 $me->$finalize($name, $spec, $context, $caller) if $finalize;
143 43         184 $orig->($name, %$spec);
144             }
145 41         33333 return;
146             },
147 42         348 );
148            
149 42         929 $me->_install_sugar($caller, $install_tracked);
150             }
151              
152             # SUBCLASSING
153             # This can be used to install additional functions
154             # into the caller package.
155             #
156             sub _install_sugar
157             {
158 42     75   65 my $me = shift;
159 42         77 my ($caller, $installer) = @_;
160 42         102 $installer->($caller, blessed => \&Scalar::Util::blessed);
161 42         1053 $installer->($caller, confess => \&Carp::confess);
162             }
163              
164             sub _handle_isa
165             {
166 42     75   55 my $me = shift;
167 42         105 my ($name, $spec, $context, $class) = @_;
168 42 100       89 return if ref $spec->{isa};
169            
170 41         2591 require Type::Utils;
171 41         22702 $spec->{isa} = Type::Utils::dwim_type($spec->{isa}, for => $class);
172            
173 40         65288 return;
174             }
175              
176             sub _handle_does
177             {
178 1     34   2 my $me = shift;
179 1         3 my ($name, $spec, $context, $class) = @_;
180 1 50       3 return unless defined $spec->{does};
181            
182 1         5 require Types::Standard;
183 1         4 $spec->{isa} = Types::Standard::ConsumerOf()->of($spec->{does});
184            
185 1         3151 return;
186             }
187              
188             sub _handle_lazy_build
189             {
190 5     38   10 my $me = shift;
191 5         12 my ($name, $spec, $context, $class) = @_;
192 5 50       14 return unless delete $spec->{lazy_build};
193            
194 5   50     12 $spec->{is} ||= "ro";
195 5   50     27 $spec->{lazy} ||= 1;
196 5   33     24 $spec->{builder} ||= "_build_$name";
197            
198 5 50       13 if ($name =~ /^_/)
199             {
200 0   0     0 $spec->{clearer} ||= "_clear$name";
201 0   0     0 $spec->{predicate} ||= "_has$name";
202             }
203             else
204             {
205 5   33     32 $spec->{clearer} ||= "clear_$name";
206 5   33     29 $spec->{predicate} ||= "has_$name";
207             }
208            
209 5         12 return;
210             }
211              
212             1;
213              
214             __END__
215              
216             =pod
217              
218             =encoding utf8
219              
220             =for stopwords superset MooX
221              
222             =head1 NAME
223              
224             MooX::late - easily translate Moose code to Moo
225              
226             =head1 SYNOPSIS
227              
228             package Foo;
229             use Moo;
230             use MooX::late;
231             has bar => (is => "ro", isa => "Str", default => "MacLaren's Pub");
232              
233             (Examples for Moo roles in section below.)
234              
235             =head1 DESCRIPTION
236              
237             L<Moo> is a light-weight object oriented programming framework which aims
238             to be compatible with L<Moose>. It does this by detecting when Moose has
239             been loaded, and automatically "inflating" its classes and roles to full
240             Moose classes and roles. This way, Moo classes can consume Moose roles,
241             Moose classes can extend Moo classes, and so forth.
242              
243             However, the surface syntax of Moo differs somewhat from Moose. For example
244             the C<isa> option when defining attributes in Moose must be either a string
245             or a blessed L<Moose::Meta::TypeConstraint> object; but in Moo must be a
246             coderef. These differences in surface syntax make porting code from Moose to
247             Moo potentially tricky. L<MooX::late> provides some assistance by enabling a
248             slightly more Moosey surface syntax.
249              
250             MooX::late does the following:
251              
252             =over
253              
254             =item 1.
255              
256             Supports C<< isa => $stringytype >>.
257              
258             =item 2.
259              
260             Supports C<< does => $rolename >> .
261              
262             =item 3.
263              
264             Supports C<< lazy_build => 1 >>.
265              
266             =item 4.
267              
268             Exports C<blessed> and C<confess> functions to your namespace.
269              
270             =item 5.
271              
272             Handles native attribute traits.
273              
274             =back
275              
276             Five features. It is not the aim of C<MooX::late> to make every aspect of
277             Moo behave exactly identically to Moose. It's just going after the low-hanging
278             fruit. So it does five things right now, and I promise that future versions
279             will never do more than seven.
280              
281             Previous releases of MooX::late added support for C<< coerce => 1 >> and
282             C<< default => $nonref >>. These features have now been added to Moo itself,
283             so MooX::late no longer has to deal with them.
284              
285             =head2 Use in Moo::Roles
286              
287             MooX::late should work in Moo::Roles, with no particular caveats.
288              
289             package MyRole;
290             use Moo::Role;
291             use MooX::late;
292              
293             L<Package::Variant> can be used to build the Moo equivalent of
294             parameterized roles. MooX::late should work in roles built with
295             Package::Variant.
296              
297             use Package::Variant
298             importing => [ qw( Moo::Role MooX::late ) ],
299             subs => [ qw( has with ) ];
300              
301             =head2 Type constraints
302              
303             Type constraint strings are interpreted using L<Type::Parser>, using the
304             type constraints defined in L<Types::Standard>. This provides a very slight
305             superset of Moose's type constraint syntax and built-in type constraints.
306              
307             Any unrecognized string that looks like it might be a class name is
308             interpreted as a class type constraint.
309              
310             =head2 Subclassing
311              
312             MooX::late is designed to be reasonably easy to subclass. There are comments
313             in the source code explaining hooks for extensibility.
314              
315             =head1 BUGS
316              
317             Please report any bugs to
318             L<http://rt.cpan.org/Dist/Display.html?Queue=MooX-late>.
319              
320             =head1 SEE ALSO
321              
322             C<MooX::late> uses L<Types::Standard> to check type constraints.
323              
324             C<MooX::late> uses L<Sub::HandlesVia> to provide native attribute traits
325             support.
326              
327             The following modules bring additional Moose functionality to Moo,
328             beyond what MooX::late offers:
329              
330             =over
331              
332             =item *
333              
334             L<MooX::Override> - support override/super
335              
336             =item *
337              
338             L<MooX::Augment> - support augment/inner
339              
340             =back
341              
342             L<MooX> allows you to load Moo plus multiple MooX extension modules in a
343             single line.
344              
345             =head1 AUTHOR
346              
347             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
348              
349             =head1 COPYRIGHT AND LICENCE
350              
351             This software is copyright (c) 2012-2014, 2019 by Toby Inkster.
352              
353             This is free software; you can redistribute it and/or modify it under
354             the same terms as the Perl 5 programming language system itself.
355              
356             =head1 DISCLAIMER OF WARRANTIES
357              
358             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
359             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
360             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
361