File Coverage

blib/lib/Params/Callbacks.pm
Criterion Covered Total %
statement 42 46 91.3
branch 7 12 58.3
condition 6 9 66.6
subroutine 12 12 100.0
pod 5 5 100.0
total 72 84 85.7


line stmt bran cond sub pod time code
1             =pod
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Params::Callbacks - Make your subroutines accept blocking callbacks
8              
9             =head1 VERSION
10              
11             version 2.152510
12              
13             =head1 SYNOPSIS
14              
15             use Params::Callbacks 'callbacks', 'callback'; # Or use ':all' tag
16             use Data::Dumper;
17              
18             $Data::Dumper::Indent = 0;
19             $Data::Dumper::Terse = 1;
20              
21             sub foo
22             {
23             my ( $callbacks, @params ) = &callbacks;
24             # If &callbacks makes the hairs
25             # on your neck standp, then use
26             # a cleaner alternative:
27             #
28             # - callbacks(@_), or ...
29             # - Params::Callbacks->new(@_)
30              
31             return $callbacks->transform(@params);
32             }
33              
34             # No callbacks; no change to result!
35             my @result_1 = foo( 0, 1, 2, 3 );
36             print Dumper( [@result_1] ), "\n"; # [0,1,2,3]
37              
38             # With callback, result is transformed before being returned!
39             my @result_2 = foo( 0, 1, 2, 3, callback { 0 + 2 ** $_ } );
40             print Dumper( [@result_2] ), "\n"; # [1,2,4,8]
41              
42             # With multiple callbacks, result is transformed in multiple stages
43             my @result_3 = foo( 0, 1, 2, 3, callback { 0 + 2 ** $_ }
44             callback { 0 + 10 * $_ });
45             print Dumper( [@result_3] ), "\n"; # [10,20,40,80];
46              
47             =head1 DESCRIPTION
48              
49             Use this module to enable a function or method to accept optional blocking
50             callbacks.
51              
52             Perhaps you would like to keep your implementation lightweight, while
53             providing the caller with an opportunity to modify its result before it
54             is finally returned to the calling scope.
55              
56             Callbacks are a fabulous for two reasons:
57              
58             =over
59              
60             =item * They reduce the need by the implementer to produce reams of speculative,
61             kitchen-sink code, helping to keep functions and methods lightweight and focussed
62             upon doing what is important.
63              
64             =item * They reduce the need by the caller to litter the calling scope with
65             lexical cruft that will never be used again. All of that can be localised within
66             a transformative callback, to be disposed of at the end of its scope.
67              
68             =back
69              
70             =head2 How callbacks are identified and processed
71              
72             Callbacks are passed to your function by placing them at the end of the call's
73             argument list. This module provides you with a means to identify and separate any
74             callbacks from your function's arguments. It also provides dispatchers that will
75             pass the return value into the callback chain and capture the result, ready to
76             pass it back up to the caller.
77              
78             Callbacks work simply enough. Like any function, they accept input in C<@_>
79             and their output is returned explicitly or as the result of their terminal
80             expression. When chaining together multiple callbacks, the dispatcher takes
81             the function's return value and passes it to the first callback; the output
82             from that callback is then passed to the following callback, and so on until
83             their are no more callbacks to process the value. The result of the final
84             callback is returned to the program ready to be returned to the caller.
85              
86             As a convenience, a callback also receives a copy of the input value in C<$_>.
87              
88             If an empty list is returned then the value is discarded and the callback
89             chain is terminated for that value.
90              
91             =head2 Creating and passing callbacks into a function
92              
93             ##################################
94             # We define our MyModule.pm file #
95             ##################################
96              
97             package MyModule;
98             use Exporter;
99             use Params::Callbacks 'callbacks';
100             use namespace::clean;
101             use Params::Callbacks 'callback';
102             our @EXPORT = 'callback';
103             our @EXPORT_OK = 'awesome';
104             our @ISA = 'Exporter';
105              
106             sub awesome {
107             my ( $callbacks, @names ) = &callbacks;
108             return $callbacks->transform(@names);
109             }
110              
111             1;
112              
113             #############################
114             # Meanwhile, back in main:: #
115             #############################
116              
117             # No callbacks ...
118             #
119             use MyModule 'awesome';
120             my @team = awesome('Imran', 'Merlyn', 'Iain');
121             print "$_\n" for @team;
122             #
123             # Imran
124             # Merlyn
125             # Iain
126             #
127             # (Not so awesome.)
128              
129              
130             # With a callback ...
131             #
132             use MyModule 'awesome';
133             my @team = awesome('Imran', 'Merlyn', 'Iain', callback {
134             "$_, you're awesome!"
135             });
136             print "$_\n" for @team;
137             #
138             # Imran, you're awesome!
139             # Merlyn, you're awesome!
140             # Iain, you're awesome!
141             #
142             # (This time with added awesome!)
143              
144              
145             # With two callbacks ...
146             #
147             use MyModule 'awesome';
148             my @team = awesome('Imran', 'Merlyn', 'Iain', callback {
149             "$_, you're awesome!"
150             } # Comma is optional here.
151             callback {
152             print "$_[0]\n";
153             return $_[0];
154             });
155             #
156             # Imran, you're awesome!
157             # Merlyn, you're awesome!
158             # Iain, you're awesome!
159             #
160             # (Moar awesome!)
161              
162             =cut
163              
164             package Params::Callbacks;
165 1     1   29497 BEGIN { $Params::Callbacks::VERSION = '2.152510'; }
166             # ABSTRACT: Use to enable subroutines to accept optional blocking callbacks.
167 1     1   7 use strict;
  1         1  
  1         24  
168 1     1   5 use warnings;
  1         1  
  1         28  
169 1     1   9 use Exporter ();
  1         2  
  1         21  
170 1     1   4 use Scalar::Util qw(blessed);
  1         10  
  1         117  
171 1     1   4 use Carp qw(confess);
  1         2  
  1         55  
172 1     1   685 use namespace::clean;
  1         66808  
  1         7  
173              
174             our @ISA = qw(Exporter);
175             our @EXPORT_OK = qw(callbacks callback);
176             our %EXPORT_TAGS = ( all => \@EXPORT_OK, ALL => \@EXPORT_OK );
177              
178             =head1 METHODS
179              
180             =cut
181              
182             =head2 new
183              
184             Takes a list of scalar values, strips away any trailing callbacks and returns
185             a new list containing a blessed array reference (the callback chain) followed
186             by any values from the original list that weren't callbacks.
187              
188             A typical use case would be processing a function's argument list C<@_>:
189              
190             sub my_function
191             {
192             ( $callbacks, @params ) = Params::Callbacks->new(@_);
193             ...
194             }
195              
196             It is also possible to pass in a pre-prepared callback chain instead of
197             individual callbacks, in which case that value will be returned as the callback
198             chain, without inspecting the list for individual callbacks E this behaviour
199             is useful when the ability to efficiently forward callbacks onto a more deeply
200             nested call is required.
201              
202             The output list is packaged in such a way as to make parsing the argument list
203             as easy as possible.
204              
205             =cut
206              
207              
208             sub new
209             {
210 12     12 1 51 my ( $class, @params ) = @_;
211 12         19 my @callbacks;
212              
213 12 100       176 if ( blessed( $params[-1] ) ) {
214 10 50       78 if ( $params[-1]->isa(__PACKAGE__) ) {
215 0         0 my $callback_chain = pop(@params);
216 0         0 return ( bless( $callback_chain, $class ), @params );
217             }
218             else {
219 10   100     138 while ( @params
      66        
220             && blessed( $params[-1] )
221             && $params[-1]->isa('Params::Callbacks::Callback') )
222             {
223 14         113 unshift @callbacks, pop(@params);
224             }
225              
226             }
227             }
228              
229 12         69 return ( bless( \@callbacks, $class ), @params );
230             }
231              
232             =head2 transform
233              
234             Transform a result set by passing it through all the stages of the callbacks
235             pipeline. The transformation terminates if the result set is reduced to
236             nothing, and an empty result set is returned.
237              
238             Empty or not, this method always returns a list.
239              
240             =cut
241              
242              
243             sub transform
244             {
245 4     4 1 18 my ( $callbacks, @data ) = @_;
246              
247 4 50 33     41 confess 'E-PARAMS-CALLBACKS-001 Expected Params::Callbacks object reference '
248             . 'as first argument'
249             unless ref($callbacks) && $callbacks->isa(__PACKAGE__);
250              
251 4         11 for my $callback (@$callbacks) {
252 2 50       8 last unless @data;
253 2         5 @data = map { $callback->($_) } @data;
  4         18  
254             }
255              
256 4         25 return @data;
257             }
258              
259             =head2 smart_transform
260              
261             Transform a result set by passing it through all the stages of the callbacks
262             pipeline. The transformation terminates if the result set is reduced to
263             nothing, and an empty result set is returned.
264              
265             Empty or not, this method always returns a list if a list was wanted.
266              
267             If a scalar is required, a scalar is returned. If the result set contains a
268             single element then the value of that element will be returned, otherwise a
269             count of the number of elements is returned.
270              
271             =cut
272              
273              
274             sub smart_transform
275             {
276 1     1 1 7 my @data = &transform;
277              
278 1 50       9 unless (wantarray) {
279 1         2 my $result;
280              
281 1 50       4 if ( @data != 1 ) {
282 0         0 $result = scalar(@data);
283             }
284             else {
285 1         3 $result = $data[0];
286             }
287              
288 1         6 return $result;
289             }
290              
291 0         0 return @data;
292             }
293              
294             =head1 EXPORTS
295              
296             Nothing is exported by default.
297              
298             The following functions are exported individually upon request; they may all be
299             imported at once using the import tags C<:all> and C<:ALL>.
300              
301             =cut
302              
303             =head2 callbacks
304              
305             Takes a list of scalar values, strips away any trailing callbacks and returns
306             a new list containing a blessed array reference (the callback chain) followed
307             by any values from the original list that weren't callbacks. The typical
308             imagined use case is in processing a function's argument list C<@_>:
309              
310             sub my_function
311             {
312             ( $callbacks, @params ) = callbacks(@_);
313             ...
314             }
315              
316             sub my_function
317             {
318             ( $callbacks, @params ) = &callbacks;
319             ...
320             }
321              
322             It is also possible to pass in a pre-prepared callback chain instead of
323             individual callbacks, in which case this function will return that value
324             as its own callback chain, without inspecting the list for individual
325             callbacks. This behaviour is useful when forwarding callbacks onto a
326             more deeply nested call.
327              
328             The output list is packaged in such a way as to make parsing the argument list
329             as easy as possible.
330              
331             =cut
332              
333              
334             sub callbacks
335             {
336 8     8 1 1268 return __PACKAGE__->new(@_);
337             }
338              
339             =head2 callback
340              
341             A simple piece of syntactic sugar that announces a callback. The code
342             reference it precedes is blessed as a C
343             object, disambiguating it from unblessed subs that are being passed as
344             standard arguments.
345              
346             Multiple callbacks may be chained together with or without comma
347             separators:
348              
349             callback { ... }, callback { ... }, callback { ... } # Valid
350             callback { ... } callback { ... } callback { ... } # Valid, too!
351              
352             =cut
353              
354              
355             sub callback (&;@)
356             {
357 14     14 1 814 my ( $callback, @params ) = @_;
358 14         99 return ( bless( $callback, 'Params::Callbacks::Callback' ), @params );
359             }
360              
361             1;
362              
363             =head1 REPOSITORY
364              
365             =over 2
366              
367             =item * L
368              
369             =item * L
370              
371             =back
372              
373             =head1 BUG REPORTS
374              
375             Please report any bugs to L
376              
377             =head1 AUTHOR
378              
379             Iain Campbell
380              
381             =head1 COPYRIGHT AND LICENSE
382              
383             This software is copyright (c) 2012-2015 by Iain Campbell.
384              
385             This is free software; you can redistribute it and/or modify it under
386             the same terms as the Perl 5 programming language system itself.
387              
388             =cut