1 package Thrasher
::Callbacks
;
9 Thrasher::Callbacks - abstract out recurring callback patterns
13 Some recurring callback patterns keep arising; this centralizes them
14 so everything can freely use them.
16 Two recurring patterns arise in the Jabber client code:
22 A need to perform a given task only after some criteria has been met,
23 which may succeed or fail, which may need different actions depending
24 on whether there was success or failure, and which, after the
25 condition has been passed, should be immediately acted upon.
27 For example, a directed presence at a legacy user should be held until
28 the user has finished logging in to the legacy service. If the log in
29 fails, the presence should be forgotten.
33 A need to call a callback with a certain name, which may be aborted
34 if a callback requests for the rest of the chain to be aborted.
38 This centralizes these actions into a single central interface.
44 our @EXPORT_OK = qw(do_when succeeded failed event_superceded
46 callbacks register_callback unregister_callback);
47 our %EXPORT_TAGS = (all
=> \
@EXPORT_OK);
49 use Thrasher
::Log
qw(log);
54 # $DO_WHEN{x} => [] means stacked up actions
55 # $DO_WHEN{x} => 1 means the action has succeeded
56 # $DO_WHEN{x} => 0 means the action failed.
57 # $DO_WHEN{x} => undef means no callbacks registered
61 my $action_name = shift;
62 my $success_action = shift;
63 my $failure_action = shift;
65 my $do_when_data = $DO_WHEN{$action_name};
66 if (!defined($do_when_data)) {
67 $DO_WHEN{$action_name} = [];
68 return do_when
($action_name, $success_action, $failure_action);
69 } elsif (ref($do_when_data) eq 'ARRAY') {
70 my $spec = {success
=> $success_action};
71 if ($failure_action) {
72 $spec->{failure
} = $failure_action;
74 push @
$do_when_data, $spec;
76 } elsif ($do_when_data == 1) {
81 log "Failure in success callback: $@" if $@
;
85 die "Bad call to do_when: " .
86 Dumper
([$action_name, $success_action, $failure_action, @_]);
90 process_actions
($_[0], 'success');
94 process_actions
($_[0], 'failure');
97 # Call this when you were using an event, but now it will never
99 sub event_superceded
{
100 delete $DO_WHEN{$_[0]};
103 # Note: Not exported; use succeeded or failed
104 sub process_actions
{
105 my $action_name = shift;
108 my $deferred_actions = $DO_WHEN{$action_name};
110 if (defined($deferred_actions)) {
111 if (!ref($deferred_actions)) {
115 for my $deferred (@
$deferred_actions) {
116 my $success_action = $deferred->{$type};
117 if (ref($success_action) eq 'CODE') {
122 log("For action $action_name ($type), failure: $@") if $@
;
127 $DO_WHEN{$action_name} = $type eq 'success';
132 sub register_callback
{
135 my $callback = shift;
137 $CALLBACKS{$type}->{$name} = $callback;
140 sub unregister_callback
{
143 delete $CALLBACKS{$type}->{$name};
146 # Returning undef cancels the rest of the callbacks and of the
147 # processing. Returning anything defined continues the chain.
148 # It is expected that if you need to make changes that you
149 # make them directly, since everything is passed by ref.
151 my $callback_type = shift;
153 my $final_action = shift;
156 my $callbacks = $CALLBACKS{$callback_type};
157 if (!defined($callbacks)) {
158 if (defined($final_action) && ref($final_action) ne 'CODE') {
159 confess
"Not a code ref passed to callbacks.";
163 return $final_action->(@data);
169 for my $name (keys %$callbacks) {
170 my $callback = $callbacks->{$name};
174 $result = $callback->($extra, @data);
177 my $message = "Failure in presence callback $name: $@";
178 if ($Thrasher::TESTING
) {
184 if (!defined($result)) {
189 # Passed the callback chain
191 return $final_action->(@data);