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