You can do it one of two ways.
- Check that the thing is valid before hand (as ref $coderef in my class below does)
- Check when you call that the result is valid (as the trigger_event sub will do if you remove the test in the first point)
At the risk of getting a TL;DR, here's something that takes coderefs and then dispatches events with them, as I wrote this, I realised that it's surprisingly long for a simple example ;)
This is the class that takes care of dispatching the events (to a single handler for each event type)
package f00li5h::Events;
use Class::Std;
{
use strict;
my %callbacks :ATTR();
=head1 register_callback
$object->register_callback( event_name => $subref );
the subref will be passed a list of args provided when the event is tr
+iggered, and will be protected in an eval block.
=cut
sub register_callback {
my($self, $name, $subref) = @_;
unless ( ref $subref eq 'CODE' ) {
use Data::Dumper;
warn "You call that a callback? it's not a real coderef, s
+o i'm going to ignore it! :" . Dumper( $subref );
return;
}
$callbacks{ ident $self }->{ $name } = $subref;
}
=head1 trigger_event
trigger an event, will return the exception or the result of the sub
$object->trigger_event( event_name => $argref);
$object->trigger_event( event_name => [ $user, $data, $foos ...] );
=cut
sub trigger_event {
my($self, $name, @data) = @_;
warn "Callback '$name' not registered"
if not defined $callbacks{ ident $self }-> { $name };
my $result;
eval {
$result = $callbacks{ ident $self }->{ $name }->( @data );
};
return $@ if $@; # if calling the handler fails, return the e
+xception
return $result; # toherwise return what the sub returned
}
}
This is the client code, that registeres event handlers (to be used as callbacks) and triggers the actual events on the object. Normally, the object would find it's own events, and handle them within a run() type method (Tl's MainLoop for example)
package main;
use strict;
{
# these are the events to be triggered
my @events= (
[moose => q/Moose saves thousands from terror attack!/] ,
[antelope => q/Antelope wins baseball game /],
[moose => q/Moose alerts medial over email scams!/] ,
);
sub events_happening {
# this sub will just iterate over @events, using closure funky
+ness
# it will return an empty list when there are none left ( henc
+e the [] )
@{ shift @events || [] };
}
}
# here are some callbacks
sub moose { print "[HANDLED moosed ->", @_ , " for you ]\n"; }
sub antelope { print "[ANTELOPE HANDLER WON'T BE CALLED->", @_ , "]\n"
+; }
# set up our event handling object
my $handler = f00li5h::Events->new();
# we pass in a proper coderef here
$handler->register_callback( 'moose' => \&moose );
# here, we just pass in a name, the class will reject it...
$handler->register_callback( 'antelope' => 'antelope' );
# this one uses an anonymous sub (which is a coderef)
# it would run happily, if i had a 'llama' event in the list
# you can add that as an exercise if you want ;)
$handler->register_callback(
'llama' => sub { print "I'm there is no event to trigger me, because f
+00li5h thought of adding me after he'd written and run the code"; }
);
use Data::Dumper;
# neat, an event loop, normally, you would $f00li5h->run() and have th
+e object get it's events from somewhere
while( my($event_name, @event_args) = events_happening ){
my $result = $handler->trigger_event($event_name, @event_args);
warn "Triggering $event_name with ("
. join( ",", @event_args )
. ") resulted in ". Dumper($result) if $result;
}
You call that a callback? it's not a real coderef, so i'm going to ign
+ore it! :$VAR1 = 'antelope';
[HANDLED moosed ->Moose saves thousands from terror attack! for you ]
Triggering moose with (Moose saves thousands from terror attack!) resu
+lted in $VAR1 = 1;
Callback 'antelope' not registered at callback line 29.
Triggering antelope with (Antelope wins baseball game ) resulted in $V
+AR1 = 'Can\'t use string ("") as a subroutine ref while "strict refs"
+ in use at callback line 33.
';
[HANDLED moosed ->Moose alerts medial over email scams! for you ]
Triggering moose with (Moose alerts medial over email scams!) resulted
+ in $VAR1 = 1;
The class will reject 'antelope' because it's not a real coderef, although if you were to pass in 'main::antelope' as the name, and remove the check for ref $coderef, you should be able to use just names of subs. I don't see why you would want to do this, since it's the same number of characters to write \& as it is to enclose the sub's name quote likes
Justification: this does answer your question about checking if it's callable before hand (because it uses ref $subref to decide if it can be callled (long before the events actually turn up). This is a fairly painless way to check if something is callable, you can also use anonymous subs if you want
Hope this helps.
@_=qw; ask f00li5h to appear and remain for a moment of pretend better than a lifetime;;s;;@_[map hex,split'',B204316D8C2A4516DE];;y/05/os/&print;