File Coverage

blib/lib/MooX/Lsub.pm
Criterion Covered Total %
statement 78 78 100.0
branch 1 2 50.0
condition n/a
subroutine 16 16 100.0
pod n/a
total 95 96 98.9


line stmt bran cond sub pod time code
1 8     8   23687 use 5.008; # utf8
  8         20  
2 8     8   37 use strict;
  8         13  
  8         179  
3 8     8   39 use warnings;
  8         7  
  8         218  
4 8     8   4901 use utf8;
  8         69  
  8         47  
5              
6             package MooX::Lsub;
7              
8             our $VERSION = '0.002001';
9              
10             # ABSTRACT: Very shorthand syntax for bulk lazy builders
11              
12             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
13              
14 8     8   2980 use Eval::Closure qw(eval_closure);
  8         6843  
  8         428  
15 8     8   42 use Carp qw(croak);
  8         9  
  8         430  
16             ## no critic (Capitalization,ProhibitConstantPragma,RequireCheckingReturnValueOfEval);
17 8     8   29 use constant can_haz_subname => eval { require Sub::Util };
  8         10  
  8         11  
  8         1475  
18              
19             ## no critic (TestingAndDebugging::ProhibitNoStrict)
20             sub _get_sub {
21 9     9   14 my ( undef, $target, $subname ) = @_;
22 8     8   740 no strict 'refs';
  8         9  
  8         576  
23 9         11 return \&{ $target . q[::] . $subname };
  9         33  
24             }
25              
26             sub _set_sub {
27 9     9   15 my ( undef, $target, $subname, $code ) = @_;
28 8     8   32 no strict 'refs';
  8         11  
  8         477  
29 9         10 *{ $target . q[::] . $subname } = $code;
  9         50  
30 9         14 return;
31             }
32              
33             sub _set_sub_named {
34 11     11   47143 my ( undef, $target, $subname, $code ) = @_;
35 8     8   55 no strict 'refs';
  8         12  
  8         2929  
36 11         69 *{ $target . q[::] . $subname } = can_haz_subname ? Sub::Util::set_subname( $target . q[::] . $subname, $code ) : $code;
  11         42  
37 11         148 return;
38             }
39             ## use critic
40             #
41             sub import {
42 9     9   3196 my ( $class, @args ) = @_;
43 9         17 my $target = caller;
44 9         21 my $has = $class->_get_sub( $target, 'has' );
45              
46 9 50       31 croak "No 'has' method in $target. Did you forget to import Moo(se)?" if not $has;
47              
48 9         39 my $lsub_code = $class->_make_lsub(
49             {
50             target => $target,
51             has => $has,
52             options => \@args,
53             },
54             );
55              
56 9         35 $class->_set_sub( $target, 'lsub', $lsub_code );
57              
58 9         461 return;
59             }
60              
61             sub _make_lsub_code {
62 11     11   23 my ( $class, $options ) = @_;
63             ## no critic (ValuesAndExpressions::RequireInterpolationOfMetachars)
64 11         16 my $nl = qq[\n];
65 11         16 my $code = 'sub($$) {' . $nl;
66 11         21 $code .= q[ package ] . $class . q[; ] . $nl;
67 11         19 $code .= q[ my ( $subname, $sub , @extras ) = @_; ] . $nl;
68 11         16 $code .= q[ if ( @extras ) { ] . $nl;
69 11         23 $code .= q[ croak "Too many arguments to 'lsub'. Did you misplace a ';'?"; ] . $nl;
70 11         16 $code .= q[ } ] . $nl;
71 11         20 $code .= q[ if ( not defined $subname or not length $subname or ref $subname ) { ] . $nl;
72 11         18 $code .= q[ croak "Subname must be defined + length + not a ref"; ] . $nl;
73 11         14 $code .= q[ } ] . $nl;
74 11         17 $code .= q[ if ( not 'CODE' eq ref $sub ) { ] . $nl;
75 11         17 $code .= q[ croak "Sub must be a CODE ref"; ] . $nl;
76 11         16 $code .= q[ } ] . $nl;
77 11         93 $code .= q[ $class->_set_sub_named($target, "_build_" . $subname , $sub ); ] . $nl;
78 11         24 $code .= q[ package ] . $options->{'target'} . q[; ] . $nl;
79 11         15 $code .= q[ return $has->( ] . $nl;
80 11         21 $code .= q[ $subname, ] . $nl;
81 11         14 $code .= q[ ( ] . $nl;
82 11         13 $code .= q[ is => 'ro', ] . $nl;
83 11         13 $code .= q[ lazy => 1, ] . $nl;
84 11         14 $code .= q[ builder => '_build_' . $subname, ] . $nl;
85 11         14 $code .= q[ ) ] . $nl;
86 11         13 $code .= q[ ); ] . $nl;
87 11         28 $code .= q[}] . $nl;
88             ## use critic
89 11         23 return $code;
90             }
91              
92             sub _make_lsub {
93 10     10   2165 my ( $class, $options ) = @_;
94              
95 10         21 my $code = $class->_make_lsub_code($options);
96              
97             my $sub = eval_closure(
98             source => $code,
99             environment => {
100             ## no critic (ValuesAndExpressions::RequireInterpolationOfMetachars)
101             '$class' => \$class,
102             '$has' => \$options->{'has'},
103 10         63 '$target' => \$options->{'target'},
104             ## use critic
105             },
106             );
107 10         3483 return $sub;
108             }
109              
110             1;
111              
112             __END__
113              
114             =pod
115              
116             =encoding UTF-8
117              
118             =head1 NAME
119              
120             MooX::Lsub - Very shorthand syntax for bulk lazy builders
121              
122             =head1 VERSION
123              
124             version 0.002001
125              
126             =head1 SYNOPSIS
127              
128             use MooX::Lsub;
129              
130             # Shorthand for
131             # has foo => ( is => ro =>, lazy => 1, builder => '_build_foo' );
132             # sub _build_foo { "Hello" }
133              
134             lsub foo => sub { "Hello" };
135              
136             =head1 DESCRIPTION
137              
138             I often want to use a lot of lazy build subs to implement some plumbing, with scope to allow
139             it to be overridden by people who know what they're doing with an injection library like Bread::Board.
140              
141             Usually, the syntax of C<Class::Tiny> is what I use for such things.
142              
143             use Class::Tiny {
144             'a' => sub { },
145             'b' => sub { },
146             };
147              
148             Etc.
149              
150             But switching things to Moo means I usually have to get much uglier, and repeat myself a *lot*.
151              
152             So this module exists as a compromise.
153              
154             Additionally, I always forgot to declare C<use Moo 1.000008> which was the first version of C<Moo> where
155             C<< builder => sub >> worked, and I would invariably get silly test failures in smokers as a consequence.
156              
157             This module avoids such problem entirely, and is tested to work with C<Moo 0.009001>.
158              
159             =head1 AUTHOR
160              
161             Kent Fredric <kentnl@cpan.org>
162              
163             =head1 COPYRIGHT AND LICENSE
164              
165             This software is copyright (c) 2017 by Kent Fredric <kentfredric@gmail.com>.
166              
167             This is free software; you can redistribute it and/or modify it under
168             the same terms as the Perl 5 programming language system itself.
169              
170             =cut