File Coverage

blib/lib/Kavorka.pm
Criterion Covered Total %
statement 124 124 100.0
branch 20 24 83.3
condition 9 15 60.0
subroutine 37 37 100.0
pod 0 3 0.0
total 190 203 93.6


line stmt bran cond sub pod time code
1 230     245   1650244 use 5.014;
  34         102  
  4         70437  
  4         12  
2 34     72   119 use strict;
  34         41  
  34         584  
  4         13  
  4         7  
  4         63  
3 34     34   114 use warnings;
  34         37  
  34         749  
  4         12  
  4         3  
  4         80  
4 34     34   105 no warnings 'void';
  34         43  
  34         973  
  4         10  
  4         4  
  4         101  
5              
6 34     34   131 use Carp ();
  34         64  
  34         420  
  4         10  
  4         13  
  4         53  
7 34     34   15196 use Exporter::Tiny ();
  34         75086  
  34         543  
  4         1805  
  4         8689  
  4         64  
8 34     34   13823 use PadWalker ();
  34         17243  
  34         691  
  4         1597  
  4         2031  
  4         87  
9 34     34   17585 use Parse::Keyword ();
  34         114130  
  34         844  
  4         1836  
  4         15981  
  4         105  
10 34     34   12050 use Module::Runtime ();
  34         37365  
  34         684  
  4         1904  
  4         5888  
  4         94  
11 34     34   363 use Scalar::Util ();
  34         38  
  34         558  
  4         42  
  4         4  
  4         54  
12 34     34   13981 use Sub::Util ();
  34         8102  
  34         18805  
  4         1637  
  4         966  
  4         2257  
13              
14             package Kavorka;
15              
16             our $AUTHORITY = 'cpan:TOBYINK';
17             our $VERSION = '0.037';
18              
19             our @ISA = qw( Exporter::Tiny );
20             our @EXPORT = qw( fun method );
21             our @EXPORT_OK = qw( fun method after around before override augment classmethod objectmethod multi );
22             our %EXPORT_TAGS = (
23             modifiers => [qw( after around before )],
24             allmodifiers => [qw( after around before override augment )],
25             );
26              
27             our %IMPLEMENTATION = (
28             after => 'Kavorka::Sub::After',
29             around => 'Kavorka::Sub::Around',
30             augment => 'Kavorka::Sub::Augment',
31             before => 'Kavorka::Sub::Before',
32             classmethod => 'Kavorka::Sub::ClassMethod',
33             f => 'Kavorka::Sub::Fun',
34             fun => 'Kavorka::Sub::Fun',
35             func => 'Kavorka::Sub::Fun',
36             function => 'Kavorka::Sub::Fun',
37             method => 'Kavorka::Sub::Method',
38             multi => 'Kavorka::Multi',
39             objectmethod => 'Kavorka::Sub::ObjectMethod',
40             override => 'Kavorka::Sub::Override',
41             );
42              
43             our %INFO;
44              
45             sub info
46             {
47 16     16 0 3771 my $me = shift;
48 16         19 my $code = $_[0];
49 16         70 $INFO{$code};
50             }
51              
52             sub guess_implementation
53             {
54 297     297 0 312 my $me = shift;
55 297         1247 $IMPLEMENTATION{$_[0]};
56             }
57              
58             sub compose_implementation
59             {
60 1     1 0 2 shift;
61 1         4 require Moo::Role;
62 1         3 Moo::Role->create_class_with_roles(@_);
63             }
64              
65             sub _exporter_validate_opts
66             {
67 81     81   74342 my $class = shift;
68 81         325 $^H{'Kavorka/package'} = $_[0]{into};
69 81 50       356 $_[0]{replace} = 1 unless exists $_[0]{replace};
70             }
71              
72             sub _fqname ($;$)
73             {
74 164     164   202 my $name = shift;
75 164         164 my ($package, $subname);
76            
77 164         261 $name =~ s{'}{::}g;
78            
79 164 100       301 if ($name =~ /::/)
80             {
81 5         23 ($package, $subname) = $name =~ m{^(.+)::(\w+)$};
82             }
83             else
84             {
85 159 50       366 my $caller = @_ ? shift : $^H{'Kavorka/package'};
86 159         255 ($package, $subname) = ($caller, $name);
87             }
88            
89 164 50       535 return wantarray ? ($package, $subname) : "$package\::$subname";
90             }
91              
92              
93             sub _exporter_fail
94             {
95 298     298   14828 my $me = shift;
96 298         371 my ($name, $args, $globals) = @_;
97            
98             my $implementation =
99 298   66     1747 $args->{'implementation'}
      33        
100             // $me->guess_implementation($name)
101             // $me;
102            
103 298         377 my $into = $globals->{into};
104            
105 298         729 Module::Runtime::use_package_optimistically($implementation);
106            
107             {
108 298   33     6376 my $traits = $globals->{traits} // $args->{traits};
  298         1013  
109 298 100       633 $implementation = $me->compose_implementation($implementation, @$traits)
110             if $traits;
111             }
112            
113 298 50       1883 $implementation->can('parse')
114             or Carp::croak("No suitable implementation for keyword '$name'");
115            
116             # Workaround for RT#95786 which might be caused by a bug in the Perl
117             # interpreter.
118             # Also RT#98666 is why we can't just call undefer_all.
119 298         1216 require Sub::Defer;
120 298         3069 for (keys %Sub::Defer::DEFERRED) {
121 38     38   185 no warnings;
  38         34  
  38         18348  
122             Sub::Defer::undefer_sub($_)
123 19443 100 100     527979 if $Sub::Defer::DEFERRED{$_} && $Sub::Defer::DEFERRED{$_}[0] =~ /\AKavorkaX?\b/;
124             }
125            
126             # Kavorka::Multi (for example) needs to know what Kavorka keywords are
127             # currently in scope.
128 298         22306 $^H{'Kavorka'} .= "$name=$implementation ";
129            
130             # This is the code that gets called at run-time.
131             #
132             my $code = Sub::Util::set_subname(
133             "$me\::$name",
134             sub {
135 199 100 66 199   3387 unless (Scalar::Util::blessed($_[0]) and $_[0]->DOES('Kavorka::Sub'))
        194      
        196      
        105      
        77      
        45      
        30      
        43      
        43      
        39      
        30      
        43      
        37      
        33      
        26      
        6      
        6      
136             {
137 3         24 return $implementation->bypass_custom_parsing($name, $into, \@_);
138             }
139              
140 196         3925 my $subroutine = shift;
141            
142             # Post-parse clean-up
143 196         647 $subroutine->_post_parse();
144            
145             # Store $subroutine for introspection
146 196         558 $INFO{ $subroutine->body } = $subroutine;
147            
148             # Install sub
149 196 100       762 my @r = wantarray
150             ? $subroutine->install_sub
151             : scalar($subroutine->install_sub);
152            
153             # Workarounds for closure issues in Parse::Keyword
154 192 100       3612 if ($subroutine->is_anonymous)
155             {
156 29         33 my $orig = $r[0];
157 29         127 my $caller_vars = PadWalker::peek_my(1);
158             @r = Sub::Util::set_subname($subroutine->package."::__ANON__", sub {
159 50     172   15907 $subroutine->_poke_pads($caller_vars);
160 50         78 goto $orig;
161 29         273 });
162 29         86 &Scalar::Util::set_prototype($r[0], $_) for grep defined, prototype($orig);
163 29         59 $INFO{ $r[0] } = $subroutine;
164 29         70 Scalar::Util::weaken($INFO{ $r[0] });
165             }
166             else
167             {
168 163         860 $subroutine->_poke_pads( PadWalker::peek_my(1) );
169             }
170            
171             # Prevents a cycle between %INFO and $subroutine.
172             Scalar::Util::weaken($subroutine->{body})
173 192 100       853 unless Scalar::Util::isweak($subroutine->{body});
174            
175 192 100       495 wantarray ? @r : $r[0];
176             },
177 298         2701 );
178            
179             # This joins up the code above with our custom parsing via
180             # Parse::Keyword
181             #
182             Parse::Keyword::install_keyword_handler(
183             $code => Sub::Util::set_subname(
184             "$me\::parse_$name",
185             sub {
186 188     264   245804 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
187 188         1224 my $subroutine = $implementation->parse(keyword => $name);
188             return (
189 296         1271 sub { ($subroutine, $args) },
190 188         17277 !! $subroutine->declared_name,
191             );
192             },
193 298         2062 ),
194             );
195            
196             # Symbol for Exporter::Tiny to export
197 2         9 return ($name => $code);
198             }
199              
200             1;
201              
202             __END__
203              
204             =pod
205              
206             =encoding utf-8
207              
208             =for stopwords invocant invocants lexicals unintuitive yada globals
209              
210             =head1 NAME
211              
212             Kavorka - function signatures with the lure of the animal
213              
214             =head1 SYNOPSIS
215              
216             use Kavorka;
217            
218             fun maxnum (Num @numbers) {
219             my $max = shift @numbers;
220             for (@numbers) {
221             $max = $_ if $max < $_;
222             }
223             return $max;
224             }
225            
226             my $biggest = maxnum(42, 3.14159, 666);
227              
228             =head1 STATUS
229              
230             Kavorka is still at a very early stage of development; there are likely
231             to be many bugs that still need to be shaken out. Certain syntax
232             features are a little odd and may need to be changed in incompatible
233             ways.
234              
235             =head1 DESCRIPTION
236              
237             Kavorka provides C<fun> and C<method> keywords for declaring functions
238             and methods. It uses Perl 5.14's keyword API, so should work more
239             reliably than source filters or L<Devel::Declare>-based modules.
240              
241             The syntax provided by Kavorka is largely inspired by Perl 6, though
242             it has also been greatly influenced by L<Method::Signatures> and
243             L<Function::Parameters>.
244              
245             For information using the keywords exported by Kavorka:
246              
247             =over
248              
249             =item *
250              
251             L<Kavorka::Manual::Functions>
252              
253             =item *
254              
255             L<Kavorka::Manual::Methods>
256              
257             =item *
258              
259             L<Kavorka::Manual::MethodModifiers>
260              
261             =item *
262              
263             L<Kavorka::Manual::MultiSubs>
264              
265             =back
266              
267             =head2 Exports
268              
269             =over
270              
271             =item C<< -default >>
272              
273             Exports C<fun> and C<method>.
274              
275             =item C<< -modifiers >>
276              
277             Exports C<before>, C<after>, and C<around>.
278              
279             =item C<< -allmodifiers >>
280              
281             Exports C<before>, C<after>, C<around>, C<augment>, and C<override>.
282              
283             =item C<< -all >>
284              
285             Exports C<fun>, C<method>, C<before>, C<after>, C<around>,
286             C<augment>, C<override>, C<classmethod>, C<objectmethod>,
287             and C<multi>.
288              
289             =back
290              
291             For example:
292              
293             # Everything except objectmethod and multi...
294             use Kavorka qw( -default -allmodifiers classmethod );
295              
296             You can rename imported functions:
297              
298             use Kavorka method => { -as => 'meth' };
299              
300             You can provide alternative implementations:
301              
302             # use My::Sub::Method instead of Kavorka::Sub::Method
303             use Kavorka method => { implementation => 'My::Sub::Method' };
304              
305             Or add traits to the default implementation:
306              
307             use Kavorka method => { traits => ['My::Sub::Role::Foo'] };
308              
309             See L<Exporter::Tiny> for more tips.
310              
311             =head2 Function Introspection API
312              
313             The coderef for any sub created by Kavorka can be passed to the
314             C<< Kavorka->info >> method. This returns a blessed object that
315             does the L<Kavorka::Sub> role.
316              
317             fun foo (:$x, :$y) { }
318            
319             my $info = Kavorka->info(\&foo);
320            
321             my $function_name = $info->qualified_name;
322             my @named_params = $info->signature->named_params;
323            
324             say $named_params[0]->named_names->[0]; # says 'x'
325              
326             See L<Kavorka::Sub>, L<Kavorka::Signature> and
327             L<Kavorka::Parameter> for further details.
328              
329             If you're using Moose, consider using L<MooseX::KavorkaInfo> to expose
330             Kavorka method signatures via the meta object protocol.
331              
332             L<Kavorka::Manual::API> provides more details and examples using the
333             introspection API.
334              
335             =head1 CAVEATS
336              
337             =over
338              
339             =item *
340              
341             As noted in L<Kavorka::Manual::PrototypeAndAttributes>, subroutine
342             attributes don't work properly for anonymous functions.
343              
344             =item *
345              
346             This module is based on L<Parse::Keyword>, which has a chronically
347             broken implementation of closures. Kavorka uses L<PadWalker> to attempt
348             to work around the problem. This mostly seems to work, but you may
349             experience some problems in edge cases, especially for anonymous
350             functions and methods.
351              
352             =item *
353              
354             If importing Kavorka's method modifiers into Moo/Mouse/Moose classes,
355             pay attention to load order:
356              
357             use Moose;
358             use Kavorka -all; # ok
359              
360             If you do it this way, Moose's C<before>, C<after>, and C<around>
361             keywords will stomp on top of Kavorka's...
362              
363             use Kavorka -all;
364             use Moose; # STOMP, STOMP, STOMP! :-(
365              
366             This can lead to delightfully hard to debug errors.
367              
368             =back
369              
370             =head1 BUGS
371              
372             Please report any bugs to
373             L<http://rt.cpan.org/Dist/Display.html?Queue=Kavorka>.
374              
375             =head1 SUPPORT
376              
377             B<< IRC: >> support is available through in the I<< #moops >> channel
378             on L<irc.perl.org|http://www.irc.perl.org/channels.html>.
379              
380             =head1 SEE ALSO
381              
382             L<Kavorka::Manual>.
383              
384             B<< Inspirations: >>
385             L<http://perlcabal.org/syn/S06.html>,
386             L<Function::Parameters>,
387             L<Method::Signatures>.
388              
389             L<http://en.wikipedia.org/wiki/The_Conversion_(Seinfeld)>.
390              
391             =head1 AUTHOR
392              
393             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
394              
395             =head1 COPYRIGHT AND LICENCE
396              
397             This software is copyright (c) 2013-2014, 2017 by Toby Inkster.
398              
399             This is free software; you can redistribute it and/or modify it under
400             the same terms as the Perl 5 programming language system itself.
401              
402             =head1 DISCLAIMER OF WARRANTIES
403              
404             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
405             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
406             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
407