File Coverage

blib/lib/Reflex/Callbacks.pm
Criterion Covered Total %
statement 77 105 73.3
branch 16 22 72.7
condition n/a
subroutine 19 25 76.0
pod 7 12 58.3
total 119 164 72.5


line stmt bran cond sub pod time code
1             package Reflex::Callbacks;
2             # vim: ts=2 sw=2 noexpandtab
3             $Reflex::Callbacks::VERSION = '0.100';
4             # Reflex::Callbacks is a callback manager. It encapsulates the
5             # callbacks for an object. Via deliver(), it maps event names to the
6             # corresponding callbacks, then invokes them through the underlying
7             # callback system.
8             #
9             # On another level, it makes sure all the callback classes are loaded
10             # and relevant coercions are defined.
11             #
12             # TODO - Explore whether it's sensible for the underlying callback
13             # system to be pluggable.
14              
15 13     13   1737604 use Moose;
  13         301933  
  13         67  
16 13     13   57140 use Moose::Util::TypeConstraints;
  13         21  
  13         103  
17              
18 13     13   22113 use Reflex::Callback;
  13         45  
  13         688  
19 13     13   7110 use Reflex::Callback::CodeRef;
  13         36  
  13         444  
20 13     13   6451 use Reflex::Callback::Method;
  13         34  
  13         429  
21 13     13   5831 use Reflex::Callback::Promise;
  13         35  
  13         461  
22              
23 13     13   99 use Moose::Exporter;
  13         19  
  13         100  
24             Moose::Exporter->setup_import_methods(
25             as_is => [
26             qw(
27             cb_class
28             cb_coderef
29             cb_method
30             cb_object
31             cb_promise
32             cb_role
33             gather_cb
34             )
35             ],
36             with_caller => [
37             qw(
38             make_emitter
39             make_terminal_emitter
40             make_error_handler
41             make_null_handler
42             )
43             ],
44             );
45              
46 13     13   880 use Carp qw(croak);
  13         35  
  13         14411  
47              
48             has callback_map => (
49             is => 'rw',
50             isa => 'HashRef[Reflex::Callback]',
51             default => sub { {} },
52             );
53              
54             coerce 'Reflex::Callback'
55             => from 'CodeRef'
56             => via { die; Reflex::Callback::CodeRef->new( code_ref => $_ ) };
57              
58             coerce 'Reflex::Callback'
59             => from 'Str'
60             => via {
61             die;
62             Reflex::Callback::Method->new(
63             method_name => $_,
64             )
65             };
66              
67             coerce 'Reflex::Callback'
68             => from 'ArrayRef'
69             => via {
70             die;
71             Reflex::Callback::Method->new(
72             object => $_->[0],
73             method_name => $_->[1],
74             )
75             };
76              
77             sub cb_method {
78 41     41 1 4138 my ($object, $method_name) = @_;
79 41         1210 return Reflex::Callback::Method->new(
80             object => $object,
81             method_name => $method_name,
82             );
83             }
84              
85             sub cb_object {
86 19     19 1 10741 my ($object, $methods) = @_;
87              
88             # They passed us a scalar. Emulate cb_methods().
89 19 100       74 return("on_$methods" => cb_method($object, $methods)) unless ref $methods;
90              
91             # Events match method names.
92 17 100       62 return( map { ("on_$_" => cb_method($object, $_)) } @$methods ) if (
  2         7  
93             ref($methods) eq "ARRAY"
94             );
95              
96             return (
97 15 50       80 map { ("on_$_" => cb_method($object, $methods->{$_})) }
  25         73  
98             keys %$methods
99             ) if ref($methods) eq "HASH";
100              
101 0         0 croak "cb_object with unknown methods type: $methods";
102             }
103              
104             # A bit of a cheat. Goes with the Object|Str type constraint in
105             # Reflex::Callback::Method.
106             sub cb_class {
107 3     3 1 10704 cb_object(@_);
108             }
109              
110             # Role callbacks inspect the handler object or class methods and
111             # determine the events being handled by their names.
112             sub cb_role {
113 13     13 1 7851 my ($invocant, $role, $prefix) = @_;
114 13 50       49 $prefix = "on" unless defined $prefix;
115              
116 13         313 my $method_prefix = qr/^${prefix}_${role}_(\S+)/;
117              
118             my @class_methods = (
119             grep /$method_prefix/,
120 13         62 map { $_->name() }
  502         17519  
121             $invocant->meta()->get_all_methods()
122             );
123              
124             my @events = (
125 13         58 map { /$method_prefix/; $1 }
  23         67  
  23         57  
126             @class_methods
127             );
128              
129 13         19 my %methods;
130 13         56 @methods{@events} = @class_methods;
131              
132 13         49 return cb_object($invocant, \%methods);
133             }
134              
135             sub cb_promise {
136 1     1 1 3970 my $promise_ref = shift;
137              
138 1         39 $$promise_ref = Reflex::Callback::Promise->new();
139 1         47 return( on_promise => $$promise_ref );
140             }
141              
142             sub cb_coderef (&) {
143 4     4 1 431 return Reflex::Callback::CodeRef->new(code_ref => shift);
144             }
145              
146             sub gather_cb {
147 13     13 1 4546 my ($owner, $arg, $match) = @_;
148 13 50       76 $match = qr/^on_/ unless defined $match;
149              
150 13         19 my %return;
151              
152             # TODO - Also analyze whether the value is a Reflex::Callack object.
153 13         100 foreach (grep /$match/, keys %$arg) {
154 13 50       37 die unless defined $arg->{$_};
155 13         20 my $callback = $arg->{$_};
156              
157 13 100       89 if (blessed $callback) {
158 12 100       78 if ($callback->isa('Reflex::Callback::Promise')) {
159 1         20 return $callback;
160             }
161              
162 11 50       37 if ($callback->isa('Reflex::Callback')) {
163 11 100       301 $callback->object($owner) unless $callback->object();
164 11         19 $return{$_} = $callback;
165 11         28 next;
166             }
167              
168 0         0 die "blessed callback $_";
169             }
170              
171             # Unblessed callback types must be coerced.
172              
173 1 50       3 if (ref($callback) eq "CODE") {
174 1         32 $return{$_} = Reflex::Callback::CodeRef->new(
175             object => $owner,
176             code_ref => $callback,
177             );
178 1         2 next;
179             }
180              
181 0         0 die "unblessed callback $_";
182             }
183              
184 12         290 return Reflex::Callbacks->new( callback_map => \%return );
185             }
186              
187             sub deliver {
188 12     12 0 24 my ($self, $event) = @_;
189              
190 12         270 my $event_name = $event->_name();
191 12         39 $event_name =~ s/^(on_)?/on_/;
192              
193 12         272 $self->callback_map()->{$event_name}->deliver($event);
194             }
195              
196             sub make_emitter {
197 7     7 0 42 my $caller = shift();
198              
199 7         24 my $meta = Class::MOP::class_of($caller);
200              
201 7         47 my ($method_name, $event_name) = @_;
202              
203             my $method = $meta->method_metaclass->wrap(
204             package_name => $caller,
205             name => $method_name,
206             body => sub {
207 22     22   653 my ($self, $event) = @_;
        2      
208 22         216 $self->re_emit( $event, -name => $event_name );
209             },
210 7         81 );
211              
212 7         443 $meta->add_method($method_name => $method);
213              
214 7         369 return $method_name;
215             }
216              
217             sub make_terminal_emitter {
218 0     0 0   my $caller = shift();
219              
220 0           my $meta = Class::MOP::class_of($caller);
221              
222 0           my ($method_name, $event_name) = @_;
223              
224             my $method = $meta->method_metaclass->wrap(
225             package_name => $caller,
226             name => $method_name,
227             body => sub {
228 0     0     my ($self, $event) = @_;
229 0           $self->re_emit( $event, -name => $event_name );
230 0           $self->stopped();
231             },
232 0           );
233              
234 0           $meta->add_method($method_name => $method);
235              
236 0           return $method_name;
237             }
238              
239             sub make_error_handler {
240 0     0 0   my $caller = shift();
241              
242 0           my $meta = Class::MOP::class_of($caller);
243              
244 0           my ($method_name, $event_name) = @_;
245              
246             my $method = $meta->method_metaclass->wrap(
247             package_name => $caller,
248             name => $method_name,
249             body => sub {
250 0     0     my ($self, $event) = @_;
251 0           warn $event->formatted(), "\n";
252 0           $self->stopped();
253             },
254 0           );
255              
256 0           $meta->add_method($method_name => $method);
257              
258 0           return $method_name;
259             }
260              
261             sub make_null_handler {
262 0     0 0   my $caller = shift();
263              
264 0           my $meta = Class::MOP::class_of($caller);
265              
266 0           my ($method_name, $event_name) = @_;
267              
268             my $method = $meta->method_metaclass->wrap(
269             package_name => $caller,
270             name => $method_name,
271 0     0     body => sub { undef },
272 0           );
273              
274 0           $meta->add_method($method_name => $method);
275              
276 0           return $method_name;
277             }
278              
279             __PACKAGE__->meta->make_immutable;
280              
281             1;
282              
283             __END__
284              
285             =pod
286              
287             =encoding UTF-8
288              
289             =for :stopwords Rocco Caputo
290              
291             =head1 NAME
292              
293             Reflex::Callbacks - Convenience functions for creating and using callbacks
294              
295             =head1 VERSION
296              
297             This document describes version 0.100, released on April 02, 2017.
298              
299             =head1 SYNOPSIS
300              
301             This package contains several helper functions, each with its own use
302             case. Please see individual examples.
303              
304             =head1 DESCRIPTION
305              
306             Reflex::Callback and its subclasses implement the different types of
307             calbacks that Reflex supports. Reflex::Callbacks provides convenience
308             functions that are almost always used instead of Reflex::Callback
309             objects.
310              
311             Reflex::Callback's generic interface is a constructor and a single
312             method, deliver(), which routes its parameters to their destination.
313             Subclasses may implement additional methods to support specific use
314             cases.
315              
316             =head2 cb_method
317              
318             Creates and returns Reflex::Callback::Method object. Accepts two
319             positional parameters: the object reference and method name to invoke
320             when the callback is delivered.
321              
322             Relex::Callback::Method's SYNOPSIS has an example, as does the eg
323             directory in Reflex's distribution.
324              
325             =head2 cb_object
326              
327             cb_object() converts the specification of multiple callbacks into a
328             list of callback parameter names and their Reflex::Callback::Method
329             objects. The returned list is in a form suitable for a Reflex::Base
330             constructor.
331              
332             cb_object() takes two positional parameters. The first is the object
333             reference that will handle callbacks. The second describes the events
334             and methods that will handle them. It may be a scalar string, an
335             array reference, or a hash reference.
336              
337             If the second parameter is a scalar string, then a single method will
338             handle a single event. The event and method names will be identical.
339             cb_object() will then return two values: the event name, and the
340             Reflex::Callback::Method to invoke the corresponding object method.
341              
342             use Reflex::Callbacks qw(cb_object);
343             my $object = bless {};
344             my @cbs = cb_object($object, "event");
345              
346             # ... is equivalent to:
347              
348             use Reflex::Callback::Method;
349             my $object = bless {};
350             my @cbs = (
351             on_event => Reflex::Callback::Method->new(
352             object => $object, method_name => "event"
353             )
354             );
355              
356             If the second parameter is an array reference of event names, then one
357             Reflex::Callback::Method will be created for each event. The event
358             names and method names will be identical.
359              
360             use Reflex::Callbacks qw(cb_object);
361             my $object = bless {};
362             my @cbs = cb_object($object, ["event_one", "event_two"]);
363              
364             # ... is equivalent to:
365              
366             use Reflex::Callback::Method;
367             my $object = bless {};
368             my @cbs = (
369             on_event_one => Reflex::Callback::Method->new(
370             object => $object, method_name => "event_one"
371             ),
372             on_event_two => Reflex::Callback::Method->new(
373             object => $object, method_name => "event_two"
374             ),
375             );
376              
377             If the second parameter is a hash reference, then it should be keyed
378             on event name. The corresponding values should be method names. This
379             syntax allows event and method names to differ.
380              
381             use Reflex::Callbacks qw(cb_object);
382             my $object = bless {};
383             my @cbs = cb_object($object, { event_one => "method_one" });
384              
385             # ... is equivalent to:
386              
387             use Reflex::Callback::Method;
388             my $object = bless {};
389             my @cbs = (
390             on_event_one => Reflex::Callback::Method->new(
391             object => $object, method_name => "method_one"
392             )
393             );
394              
395             =head2 cb_class
396              
397             cb_class() is an alias for cb_object(). Perl object and class methods
398             currently behave the same, so there is no need for additional code at
399             this time.
400              
401             =head2 cb_role
402              
403             cb_role() implements Reflex's role-based callbacks. These callbacks
404             rely on method names to contain clues about the objects and events
405             being handled. For instance, a method named on_resolver_answer()
406             hints that it handles the "answer" events from a sub-object with the
407             role of "resolver".
408              
409             cb_role() requires two parameters and has a third optional one. The
410             first two parameters are the callback object reference and the role of
411             the object for which it handles events. The third optional parameter
412             overrides the "on" prefix with a different one.
413              
414             {
415             package Handler;
416             sub on_resolver_answer { ... }
417             sub on_resolver_failure { ... }
418             }
419              
420             # This role-based definition:
421              
422             use Reflex::Callbacks qw(cb_role);
423             my $object = Handler->new();
424             my @cbs = cb_role($object, "resolver");
425              
426             # ... is equivalent to:
427              
428             use Reflex::Callbacks qw(cb_object);
429             my $object = Handler->new();
430             my @cbs = cb_object(
431             $object, {
432             answer => "on_resolver_answer",
433             failure => "on_resolver_failure",
434             }
435             );
436              
437             # ... or:
438              
439             use Reflex::Callbacks qw(cb_method);
440             my $object = Handler->new();
441             my @cbs = (
442             on_answer => Reflex::Callback::Method->new(
443             object => $object, method_name => "on_resolver_answer"
444             ),
445             on_failure => Reflex::Callback::Method->new(
446             object => $object, method_name => "on_resolver_failure"
447             ),
448             );
449              
450             =head2 cb_promise
451              
452             cb_promise() takes a scalar reference. This reference will be
453             populated with a Reflex::Callback::Promise object.
454              
455             cb_promise() returns two values that are suitable to insert onto a
456             Reflex::Base's constructor. The first value is a special event name,
457             "on_promise", that tells Reflex::Base objects they may be used inline
458             as promises. The second return value is the same
459             Reflex::Callback::Promise object that was inserted into cb_promise()'s
460             parameter.
461              
462             use Reflex::Callbacks qw(cb_promise);
463             my $promise;
464             my @cbs = cb_promise(\$promise);
465              
466             # ... is eqivalent to:
467              
468             use Reflex::Callback::Promise;
469             my $promise = Reflex::Callback::Promise->new();
470             @cbs = ( on_promise => $promise );
471              
472             =head2 cb_coderef
473              
474             cb_coderef() takes a single parameter, a coderef to callback. It
475             returns a single value: a Reflex::Callback::Coderef object that will
476             deliver events to the callback.
477              
478             cb_coderef() neither takes nor returns an event name. As such, the
479             Reflex::Base parameter name must be supplied outside cb_coderef().
480              
481             my $timer = Reflex::Interval->new(
482             interval => 1,
483             auto_repeat => 1,
484             on_tick => cb_coderef { print "tick!\n" },
485             );
486              
487             As shown above, cb_coderef() is prototyped to make the callback's
488             C<sub> declaration optional.
489              
490             =for Pod::Coverage deliver make_emitter make_error_handler make_null_handler make_terminal_emitter
491              
492             =head1 Usages Outside Reflex
493              
494             Reflex callbacks are designed to be independent of any form of
495             concurrency. Reflex::Callbacks provides two convenience functions
496             that other class libraries may find useful but Reflex doesn't use.
497              
498             Please contact the authors if there's interest in using these
499             functions, otherwise they may be deprecated.
500              
501             =head2 gather_cb
502              
503             The gather_cb() function extracts callbacks from an object's
504             constructor parameters and encapsulates them in a Reflex::Callbacks
505             object.
506              
507             gather_cb() takes three parameters: The object that will own the
508             callbacks, a hash reference containing a constructor's named
509             parameters, and an optional regular expression to match callback
510             parameter names. By default, gather_cb() will collect
511             parameters matching C</^on_/>.
512              
513             package ThingWithCallbacks;
514             use Moose;
515              
516             use Reflex::Callbacks qw(gather_cb);
517              
518             has cb => ( is => 'rw', isa => 'Reflex::Callbacks' );
519              
520             sub BUILD {
521             my ($self, $arg) = @_;
522             $self->cb(gather_cb($self, $arg));
523             }
524              
525             sub run {
526             my $self = shift;
527             $self->cb()->deliver( event => {} );
528             }
529              
530             =head1 deliver
531              
532             deliver() is a method of Reflex::Callback, not a function. It takes
533             two parameters: the name of an event to deliver, and a hash reference
534             containing named values to include with the event.
535              
536             deliver() finds the callback that corresponds to its event. It then
537             delivers the event to that callback. The callback must have been
538             collected by gather_cb().
539              
540             See the example for gather_cb(), which also invokes deliver().
541              
542             =head1 SEE ALSO
543              
544             Please see those modules/websites for more information related to this module.
545              
546             =over 4
547              
548             =item *
549              
550             L<Reflex|Reflex>
551              
552             =item *
553              
554             L<Reflex>
555              
556             =item *
557              
558             L<Reflex::Callback::CodeRef>
559              
560             =item *
561              
562             L<Reflex::Callback::Method>
563              
564             =item *
565              
566             L<Reflex::Callback::Promise>
567              
568             =item *
569              
570             L<L<Reflex::Callbacks> documents callback convenience functions.|L<Reflex::Callbacks> documents callback convenience functions.>
571              
572             =item *
573              
574             L<Reflex/ACKNOWLEDGEMENTS>
575              
576             =item *
577              
578             L<Reflex/ASSISTANCE>
579              
580             =item *
581              
582             L<Reflex/AUTHORS>
583              
584             =item *
585              
586             L<Reflex/BUGS>
587              
588             =item *
589              
590             L<Reflex/BUGS>
591              
592             =item *
593              
594             L<Reflex/CONTRIBUTORS>
595              
596             =item *
597              
598             L<Reflex/COPYRIGHT>
599              
600             =item *
601              
602             L<Reflex/LICENSE>
603              
604             =item *
605              
606             L<Reflex/TODO>
607              
608             =back
609              
610             =head1 BUGS AND LIMITATIONS
611              
612             You can make new bug reports, and view existing ones, through the
613             web interface at L<http://rt.cpan.org/Public/Dist/Display.html?Name=Reflex>.
614              
615             =head1 AUTHOR
616              
617             Rocco Caputo <rcaputo@cpan.org>
618              
619             =head1 COPYRIGHT AND LICENSE
620              
621             This software is copyright (c) 2017 by Rocco Caputo.
622              
623             This is free software; you can redistribute it and/or modify it under
624             the same terms as the Perl 5 programming language system itself.
625              
626             =head1 AVAILABILITY
627              
628             The latest version of this module is available from the Comprehensive Perl
629             Archive Network (CPAN). Visit L<http://www.perl.com/CPAN/> to find a CPAN
630             site near you, or see L<https://metacpan.org/module/Reflex/>.
631              
632             =head1 DISCLAIMER OF WARRANTY
633              
634             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
635             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT
636             WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
637             PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND,
638             EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
639             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
640             PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
641             SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME
642             THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
643              
644             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
645             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
646             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE
647             TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR
648             CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
649             SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
650             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
651             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
652             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
653             DAMAGES.
654              
655             =cut