Commit f737956c4096b40c7d84e6ca509e04dfe2a387ee
- Diff rendering mode:
- inline
- side by side
eg/ThingWithCallbacks.pm
(40 / 0)
|   | |||
| 1 | package ThingWithCallbacks; | ||
| 2 | use Moose; | ||
| 3 | |||
| 4 | # A demo class that sends callbacks to its users. I wanted to go even | ||
| 5 | # more abstract than Reflex::Timer, partly to reduce confusion over | ||
| 6 | # the callbacks subproject's scope. Not all callback types are | ||
| 7 | # appropriate for timers, too. | ||
| 8 | |||
| 9 | use Reflex::Callbacks qw(gather_cb); | ||
| 10 | |||
| 11 | has cb => ( is => 'rw', isa => 'Reflex::Callbacks' ); | ||
| 12 | |||
| 13 | # This is interesting code from Reflex::Timer. | ||
| 14 | #has on_tick => ( | ||
| 15 | # isa => 'Reflex::Callback', | ||
| 16 | # is => 'ro', | ||
| 17 | # coerce => 1, | ||
| 18 | # default => "tick", | ||
| 19 | # default => sub { | ||
| 20 | # my $self = shift; | ||
| 21 | # Reflex::Callback::Emit->new( | ||
| 22 | # object => $self, | ||
| 23 | # event_name => "tick", | ||
| 24 | # ); | ||
| 25 | # }, | ||
| 26 | #); | ||
| 27 | |||
| 28 | sub BUILD { | ||
| 29 | my ($self, $arg) = @_; | ||
| 30 | |||
| 31 | # Gather the callbacks from the constructor parameters. | ||
| 32 | $self->cb(gather_cb($arg)); | ||
| 33 | } | ||
| 34 | |||
| 35 | sub run { | ||
| 36 | my $self = shift; | ||
| 37 | $self->cb()->send( on_event => {} ); | ||
| 38 | } | ||
| 39 | |||
| 40 | 1; |
eg/eg-19-nonexistent.pl
(6 / 0)
|   | |||
| 1 | #!/usr/bin/env perl | ||
| 2 | |||
| 3 | use warnings; | ||
| 4 | use strict; | ||
| 5 | |||
| 6 | die "$0 is intentionally nonexistent.\n"; |
eg/eg-20-rcb-coderef.pl
(60 / 0)
|   | |||
| 1 | #!/usr/bin/env perl | ||
| 2 | |||
| 3 | # This example illustrates implicit and explicit callbacks via plain | ||
| 4 | # coderefs. Coderef callbacks are clear and concise. They allow | ||
| 5 | # developers to take advantage of closure tricks, including | ||
| 6 | # implementing a form of continuation-passing style. | ||
| 7 | # | ||
| 8 | # They are less suitable for object-oriented programs. See most other | ||
| 9 | # forms of Reflex::Callback for more object oriented callbacks. | ||
| 10 | |||
| 11 | # Reflex::Callbacks and the Reflex::Callback helper classes will | ||
| 12 | # abstract callbacks to fulfill the following goals: | ||
| 13 | # | ||
| 14 | # 1. A module's consumer decides how it will be called back. | ||
| 15 | # 2. Module implementations will use a single interface that | ||
| 16 | # represents the abstract notion of callbacks. Consumers' chosen | ||
| 17 | # callback implementations will handle the rest. | ||
| 18 | # 3. Every known form of callback will be supported, so that module | ||
| 19 | # consumers aren't limited to a single, possibly undesirable | ||
| 20 | # callback mechanism. | ||
| 21 | # 4. Common callback mechanisms may be specified by concise, | ||
| 22 | # contextual syntax. | ||
| 23 | # 5. All callback mechanisms may be specified by slightly verbose but | ||
| 24 | # unambiguous syntax. | ||
| 25 | |||
| 26 | use warnings; | ||
| 27 | use strict; | ||
| 28 | use lib qw(../lib); | ||
| 29 | |||
| 30 | use ExampleHelpers qw(eg_say); | ||
| 31 | use Reflex::Callbacks qw(cb_coderef); | ||
| 32 | use ThingWithCallbacks; | ||
| 33 | |||
| 34 | # Create a thing that will invoke callbacks. This syntax uses | ||
| 35 | # contextually specified coderef callbacks. | ||
| 36 | |||
| 37 | my $thing_one = ThingWithCallbacks->new( | ||
| 38 | on_event => sub { eg_say("contextual callback invoked") }, | ||
| 39 | ); | ||
| 40 | |||
| 41 | $thing_one->run(); | ||
| 42 | |||
| 43 | # cb_coderef() reduces context sensitivity at the expense of | ||
| 44 | # verbosity. | ||
| 45 | |||
| 46 | my $thing_two = ThingWithCallbacks->new( | ||
| 47 | on_event => cb_coderef(sub { eg_say("explicit callback invoked") }), | ||
| 48 | ); | ||
| 49 | |||
| 50 | $thing_two->run(); | ||
| 51 | |||
| 52 | # cb_coderef is prototyped so it can replace "sub". | ||
| 53 | |||
| 54 | my $thing_three = ThingWithCallbacks->new( | ||
| 55 | on_event => cb_coderef { eg_say("explicit callback (no sub) invoked") }, | ||
| 56 | ); | ||
| 57 | |||
| 58 | $thing_three->run(); | ||
| 59 | |||
| 60 | exit; |
lib/Reflex/Callback.pm
(5 / 0)
|   | |||
| 1 | package Reflex::Callback; | ||
| 2 | |||
| 3 | use Moose; | ||
| 4 | |||
| 5 | 1; |
lib/Reflex/Callback/CodeRef.pm
(18 / 0)
|   | |||
| 1 | package Reflex::Callback::CodeRef; | ||
| 2 | |||
| 3 | use Moose; | ||
| 4 | extends 'Reflex::Callback'; | ||
| 5 | |||
| 6 | has code_ref => ( | ||
| 7 | is => 'ro', | ||
| 8 | isa => 'CodeRef', | ||
| 9 | required => 1, | ||
| 10 | ); | ||
| 11 | |||
| 12 | sub deliver { | ||
| 13 | my $self = shift; | ||
| 14 | $self->code_ref()->(@_); | ||
| 15 | } | ||
| 16 | |||
| 17 | |||
| 18 | 1; |
lib/Reflex/Callbacks.pm
(135 / 0)
|   | |||
| 1 | package Reflex::Callbacks; | ||
| 2 | |||
| 3 | # Reflex::Callbacks is a callback manager. It encapsulates the | ||
| 4 | # callbacks for an object. Via send(), it maps event names to the | ||
| 5 | # corresponding callbacks, then invokes them through the underlying | ||
| 6 | # callback system. | ||
| 7 | # | ||
| 8 | # On another level, it makes sure all the callback classes are loaded | ||
| 9 | # and relevant coercions are defined. | ||
| 10 | # | ||
| 11 | # TODO - Explore whether it's sensible for the underlying callback | ||
| 12 | # system to be pluggable. | ||
| 13 | |||
| 14 | use Moose; | ||
| 15 | use Moose::Util::TypeConstraints; | ||
| 16 | |||
| 17 | use Reflex::Callback; | ||
| 18 | #use Reflex::Callback::Class; | ||
| 19 | use Reflex::Callback::CodeRef; | ||
| 20 | #use Reflex::Callback::Emit; # For current Reflex compatibility | ||
| 21 | #use Reflex::Callback::Method; | ||
| 22 | #use Reflex::Callback::Object; | ||
| 23 | #use Reflex::Callback::Promise; | ||
| 24 | #use Reflex::Callback::Role; | ||
| 25 | |||
| 26 | use Exporter; | ||
| 27 | use base qw(Exporter); | ||
| 28 | our @EXPORT_OK = qw( | ||
| 29 | cb_class | ||
| 30 | cb_coderef | ||
| 31 | cb_method | ||
| 32 | cb_object | ||
| 33 | cb_promise | ||
| 34 | cb_role | ||
| 35 | gather_cb | ||
| 36 | ); | ||
| 37 | |||
| 38 | use Carp qw(croak); | ||
| 39 | |||
| 40 | has callback_map => ( | ||
| 41 | is => 'rw', | ||
| 42 | isa => 'HashRef[Reflex::Callback]', | ||
| 43 | default => sub { {} }, | ||
| 44 | ); | ||
| 45 | |||
| 46 | coerce 'Reflex::Callback' | ||
| 47 | => from 'CodeRef' | ||
| 48 | => via { Reflex::Callback::CodeRef->new( code_ref => $_ ) }; | ||
| 49 | |||
| 50 | coerce 'Reflex::Callback' | ||
| 51 | => from 'Str' | ||
| 52 | => via { | ||
| 53 | Reflex::Callback::Method->new( | ||
| 54 | method_name => $_, | ||
| 55 | ) | ||
| 56 | }; | ||
| 57 | |||
| 58 | coerce 'Reflex::Callback' | ||
| 59 | => from 'ArrayRef' | ||
| 60 | => via { | ||
| 61 | Reflex::Callback::Method->new( | ||
| 62 | object => $_->[0], | ||
| 63 | method_name => $_->[1], | ||
| 64 | ) | ||
| 65 | }; | ||
| 66 | |||
| 67 | sub cb_method { | ||
| 68 | die; | ||
| 69 | my ($object, $method_name) = @_; | ||
| 70 | return Reflex::Callback::Method->new( | ||
| 71 | object => $object, | ||
| 72 | method_name => $method_name, | ||
| 73 | ); | ||
| 74 | } | ||
| 75 | |||
| 76 | sub cb_object { | ||
| 77 | die; | ||
| 78 | } | ||
| 79 | |||
| 80 | sub cb_class { | ||
| 81 | die; | ||
| 82 | } | ||
| 83 | |||
| 84 | sub cb_role { | ||
| 85 | die; | ||
| 86 | } | ||
| 87 | |||
| 88 | sub cb_promise { | ||
| 89 | die; | ||
| 90 | } | ||
| 91 | |||
| 92 | sub cb_coderef (&) { | ||
| 93 | return Reflex::Callback::CodeRef->new(code_ref => shift); | ||
| 94 | } | ||
| 95 | |||
| 96 | sub gather_cb { | ||
| 97 | my ($arg, $match) = @_; | ||
| 98 | $match = qr/^on_/ unless defined $match; | ||
| 99 | |||
| 100 | my %return; | ||
| 101 | |||
| 102 | # TODO - Also analyze whether the value is a Reflex::Callack object. | ||
| 103 | foreach (grep /$match/, keys %$arg) { | ||
| 104 | die unless defined $arg->{$_}; | ||
| 105 | my $callback = $arg->{$_}; | ||
| 106 | |||
| 107 | if (blessed $callback) { | ||
| 108 | if ($callback->isa('Reflex::Callback')) { | ||
| 109 | $return{$_} = $callback; | ||
| 110 | next; | ||
| 111 | } | ||
| 112 | |||
| 113 | die "blessed callback $_"; | ||
| 114 | } | ||
| 115 | |||
| 116 | # Unblessed callback types must be coerced. | ||
| 117 | |||
| 118 | if (ref($callback) eq "CODE") { | ||
| 119 | $return{$_} = Reflex::Callback::CodeRef->new(code_ref => $callback); | ||
| 120 | next; | ||
| 121 | } | ||
| 122 | |||
| 123 | die "unblessed callback $_"; | ||
| 124 | } | ||
| 125 | |||
| 126 | return Reflex::Callbacks->new( callback_map => \%return ); | ||
| 127 | } | ||
| 128 | |||
| 129 | sub send { | ||
| 130 | my ($self, $event, $arg) = @_; | ||
| 131 | $arg //= {}; | ||
| 132 | $self->callback_map()->{$event}->deliver($arg); | ||
| 133 | } | ||
| 134 | |||
| 135 | 1; |

