File Coverage

blib/lib/Params/Callbacks.pm
Criterion Covered Total %
statement 41 45 91.1
branch 7 12 58.3
condition 6 9 66.6
subroutine 11 11 100.0
pod 5 5 100.0
total 70 82 85.3


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