Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Implement abstract class method callbacks.

Perl uses identical syntax for class and object method invocation.
The only difference is the invocant---either a class or an object.
Implemented class method callbacks in terms of object method callbacks
for now.  The implementations may diverge in the future if the
semantics need to, but the interfaces should be forward compatible.
  • Loading branch information...
commit ce814c8ac75db3520d13ea18cc7ded5417d03a4c 1 parent 49d1ae1
@rcaputo authored
View
528 eg/eg-22-rcb-object.pl
@@ -43,7 +43,7 @@
sub on_event {
my ($self, $arg) = @_;
- eg_say("scalar object handled event");
+ eg_say("$self - scalar object handled event");
}
sub run_thing {
@@ -83,7 +83,7 @@
sub on_event {
my ($self, $arg) = @_;
- eg_say("array object handled event");
+ eg_say("$self - array object handled event");
}
sub run_thing {
@@ -124,7 +124,7 @@
sub handle_event {
my ($self, $arg) = @_;
- eg_say("hash object handled event");
+ eg_say("$self - hash object handled event");
}
sub run_thing {
@@ -135,525 +135,3 @@
my $hho = HashHandlerObject->new();
$hho->run_thing();
-
-__END__
-
-# cb_coderef() reduces context sensitivity at the expense of
-# verbosity.
-
-my $thing_two = ThingWithCallbacks->new(
- on_event => cb_coderef(sub { eg_say("explicit callback invoked") }),
-);
-
-$thing_two->run();
-
-# cb_coderef is prototyped so it can replace "sub".
-
-my $thing_three = ThingWithCallbacks->new(
- on_event => cb_coderef { eg_say("explicit callback (no sub) invoked") },
-);
-
-$thing_three->run();
-
-exit;
-
-__END__
-
-#!/usr/bin/env perl
-
-# As promised in eg-01-discrete-observer.pl, it's time to make the
-# syntax nicer and formal.
-#
-# Most syntaxes have two or three forms. The first is a simplified,
-# context-sensitive form for people who like concise and cryptic. The
-# second is a slightly more verbose, explicit form for people who
-# prefer clarity.
-
-use warnings;
-use strict;
-use lib qw(../lib);
-
-use ExampleHelpers qw(eg_say eg_object);
-
-# TODO - Some kind of :all or :default tag?
-use Reflex::Callbacks qw(
- cb_class cb_coderef cb_method cb_object cb_promise cb_role
-);
-
-# Objects need to be stored somewhere, but we don't really care about
-# them. Push them onto a list, and forget about them.
-
-my @things;
-
-####################
-# Coderef callbacks.
-#
-# The most flexible callbacks are simply coderefs. They are clear,
-# concise, and allow develpers to emulate continuation-passing style
-# by abusing closures.
-#
-# Coderef callbacks are less suitable for object-oriented programs.
-# Using closures, developers can certainly thunk from coderefs to
-# objects, but this puts a repetitive burden on developers. See
-# method callbacks below for a more convenient way.
-
-# The simplified contextual style is a plain coderef.
-
-push @things, ThingWithCallbacks->new(
- on_tick => sub { eg_say("simple coderef callback") },
-);
-
-# The explicit style uses cb_coderef() to identify the callback type.
-# Cb stands for Callback.
-
-push @things, ThingWithCallbacks->new(
- on_tick => cb_coderef( sub { eg_say("explicit coderef callback") } ),
-);
-
-# Here is a second variant of cb_coderef() using the (&) prototype to
-# eliminate some punctuation and the "sub" keyword.
-
-push @things, ThingWithCallbacks->new(
- on_tick => cb_coderef { eg_say("prototyped coderef callback") },
-);
-
-##########################
-# Object method callbacks.
-#
-# Invoking methods as callbacks is another popular choice. This is
-# often more convenient in object-oriented situations. Methods may be
-# invoked on objects or classes. The syntax is the same in Perl, so
-# there's no difference in Reflex.
-
-# The simplified contextual style uses an arrayref, containing the
-# object and method name. While it's a pair of values, we can't use a
-# hashref without invalidating the object by stringification.
-
-my $eg_object_1 = eg_object("simplified single event callback object");
-push @things, ThingWithCallbacks->new(
- on_tick => [ $eg_object_1, "handler_method" ],
-);
-
-# The explicit style uses cb_method() to identify the callback type.
-
-my $eg_object_2 = eg_object("explicit single event callback object");
-push @things, ThingWithCallbacks->new(
- on_tick => cb_method( $eg_object_1, "handler_method" ),
-);
-
-#############################
-# Multiple callbacks at once.
-#
-# The rest of the variants deal with assigning multiple callbacks to
-# a single object. The above forms will work well, but they involve
-# repetition that can feel tedious when a lot of events are handled.
-#
-# Consider the following example:
-#
-# my $bot = Reflex::IrcBot->new();
-# my $protocol = Reflex::Poco::IRC->new(
-# on_irc_001 => [ $bot, "handle_irc_connected" ],
-# on_irc_public => [ $bot, "handle_irc_public" ],
-# on_irc_msg => [ $bot, "handle_irc_private" ],
-# on_irc_notice => [ $bot, "handle_irc_notice" ],
-# # ... and a dozen other interesting IRC events ...
-# );
-#
-# The simplified syntax extends the simplified object syntx. The
-# scalar "method_name" is replaced by a list of method names or a map
-# of event names to method names.
-#
-# An arrayref is used when the handler methods and event names are
-# identical.
-#
-# This group of syntaxes specify multiple event names in their
-# callback definitions. They are all lumped under the "callbacks"
-# parameter.
-
-my $eg_object_3 = eg_object("simplified multiple method callbacks");
-push @things, ThingWithCallbacks->new(
- callbacks => [ $eg_object_3, [qw( event_a event_b event_c )] ],
-);
-
-# A hashref is used to map event names to method names.
-
-my $eg_object_4 = eg_object("simplified multiple mapped methods");
-push @things, ThingWithCallbacks->new(
- callbacks => [
- $eg_object_3, {
- event_a => "handler_method_a",
- event_b => "handler_method_b",
- event_c => "handler_method_c",
- },
- ],
-);
-
-# Multiple method callbacks may also be defined with explicit
-# syntaxes.
-
-my $eg_object_5 = eg_object("explicit multiple method callbacks");
-push @things, ThingWithCallbacks->new(
- callbacks => cb_object(
- $eg_object_5,
- [qw( event_a event_b event_c)]
- ),
-);
-
-my $eg_object_6 = eg_object("explicit multiple mapped methods");
-push @things, ThingWithCallbacks->new(
- callbacks => cb_object(
- $eg_object_6, {
- event_a => "handler_method_a",
- event_b => "handler_method_b",
- event_c => "handler_method_c",
- },
- ),
-);
-
-#########################
-# Class method callbacks.
-#
-# Class methods may be called using the same syntaxes as object
-# method. As of this writing, the mechanisms for invokving class
-# methods are identical in Perl to those of invoking object methods.
-# An cb_class() utility function is provided for forward
-# compatibility. If the mechanisms were to diverge in a future
-# version of Perl, cb_class() would updated to accommodate the
-# change.
-
-# Examples aren't shown since they would look nearly identical to
-# previous ones.
-
-#######################
-# Role based callbacks.
-#
-# Role-based callbacks map an object's responses to its destination's
-# methods using a simple algorithm. Method names consist of a prefix
-# ("handle"), the sub-object's role (perhaps "dns"), and the
-# sub-object's event name ("answer") joined by underscores to become:
-# handle_dns_answer().
-#
-# In theory, each object performs a task or role that contributes to
-# the program as a whole. Larger, more complex objects are built by
-# gluing together smaller objects that perform simpler roles. For
-# example, a simple HTTP client might glue together some generic
-# objects like so:
-#
-# HTTP client
-# Keep-alive connection manager ("keepalive" object)
-# Asynchronous DNS resolver ("resolver" object)
-# Asynchronous TCP connector ("connector" object)
-# HTTP stream ("httpstream" object)
-# Asynchronous stream ("stream" object)
-# HTTP protocol ("http" object)
-#
-# At each level, the container object knows the interfaces for the
-# smaller objects within it. It can therefore assign the smaller
-# objects roles and implicitly handle their events by defining methods
-# with predictable names.
-
-# Currently there is only the explicit cb_role() function to define
-# roles. Implicit syntax is left for a future release.
-#
-# $eg_object_7->handle_ticker_tick() is called in response to the
-# following Reflex::Timer's "tick" event.
-
-my $eg_object_7 = eg_object("explicit role, explicit prefix");
-push @things, ThingWithCallbacks->new(
- callbacks => cb_role($eg_object_7, "ticker", "handle"),
-);
-
-# The third parameter to cb_role() is the method prefix, which
-# defaults to "handle" if omitted. $eg_object_8's method
-# handle_ticker_tick() is called below. The "handle" is implied by
-# default.
-
-my $eg_object_8 = eg_object("explicit role, implicit prefix");
-push @things, ThingWithCallbacks->new(
- callbacks => cb_role($eg_object_8, "ticker"),
-);
-
-######################
-# Promises or futures.
-#
-# Promises are the final callback mechanism Reflex supports. They are
-# defined by either not defining callbacks at all, or by defining
-# cb_promise() as the callbacks mechanism.
-#
-# Note however that this code will block. Nothing beyond it runs
-# until the while() loop finishes. Which may be "never". Other
-# caveats may apply.
-
-my $implicit_promisory_timer = ThingWithCallbacks->new();
-
-while (my $next_event = $implicit_promisory_timer->next_event()) {
- eg_tell("implicit promisory timer generated event $next_event");
-}
-
-# People who dislike invisible logic might prefer cb_promise().
-#
-my $explicit_promisory_timer = ThingWithCallbacks->new(
- callbacks => cb_promise(),
-);
-
-while (my $next_event = $explicit_promisory_timer->next_event()) {
- eg_tell("explicit promisory timer generated event $next_event");
-}
-
-###############
-# Run the demo.
-
-Reflex::Object->run_all();
-exit;
-
-
-
-
-__END__
-
-#!/usr/bin/env perl
-
-# As promised in eg-01-discrete-observer.pl, it's time to make the
-# syntax nicer and formal.
-#
-# Most syntaxes have two or three forms. The first is a simplified,
-# context-sensitive form for people who like concise and cryptic. The
-# second is a slightly more verbose, explicit form for people who
-# prefer clarity.
-
-use warnings;
-use strict;
-use lib qw(../lib);
-
-use ExampleHelpers qw(eg_say eg_object);
-
-# TODO - Some kind of :all or :default tag?
-use Reflex::Callbacks qw(
- cb_class cb_coderef cb_method cb_object cb_promise cb_role
-);
-
-# Objects need to be stored somewhere, but we don't really care about
-# them. Push them onto a list, and forget about them.
-
-my @things;
-
-####################
-# Coderef callbacks.
-#
-# The most flexible callbacks are simply coderefs. They are clear,
-# concise, and allow develpers to emulate continuation-passing style
-# by abusing closures.
-#
-# Coderef callbacks are less suitable for object-oriented programs.
-# Using closures, developers can certainly thunk from coderefs to
-# objects, but this puts a repetitive burden on developers. See
-# method callbacks below for a more convenient way.
-
-# The simplified contextual style is a plain coderef.
-
-push @things, ThingWithCallbacks->new(
- on_tick => sub { eg_say("simple coderef callback") },
-);
-
-# The explicit style uses cb_coderef() to identify the callback type.
-# Cb stands for Callback.
-
-push @things, ThingWithCallbacks->new(
- on_tick => cb_coderef( sub { eg_say("explicit coderef callback") } ),
-);
-
-# Here is a second variant of cb_coderef() using the (&) prototype to
-# eliminate some punctuation and the "sub" keyword.
-
-push @things, ThingWithCallbacks->new(
- on_tick => cb_coderef { eg_say("prototyped coderef callback") },
-);
-
-##########################
-# Object method callbacks.
-#
-# Invoking methods as callbacks is another popular choice. This is
-# often more convenient in object-oriented situations. Methods may be
-# invoked on objects or classes. The syntax is the same in Perl, so
-# there's no difference in Reflex.
-
-# The simplified contextual style uses an arrayref, containing the
-# object and method name. While it's a pair of values, we can't use a
-# hashref without invalidating the object by stringification.
-
-my $eg_object_1 = eg_object("simplified single event callback object");
-push @things, ThingWithCallbacks->new(
- on_tick => [ $eg_object_1, "handler_method" ],
-);
-
-# The explicit style uses cb_method() to identify the callback type.
-
-my $eg_object_2 = eg_object("explicit single event callback object");
-push @things, ThingWithCallbacks->new(
- on_tick => cb_method( $eg_object_1, "handler_method" ),
-);
-
-#############################
-# Multiple callbacks at once.
-#
-# The rest of the variants deal with assigning multiple callbacks to
-# a single object. The above forms will work well, but they involve
-# repetition that can feel tedious when a lot of events are handled.
-#
-# Consider the following example:
-#
-# my $bot = Reflex::IrcBot->new();
-# my $protocol = Reflex::Poco::IRC->new(
-# on_irc_001 => [ $bot, "handle_irc_connected" ],
-# on_irc_public => [ $bot, "handle_irc_public" ],
-# on_irc_msg => [ $bot, "handle_irc_private" ],
-# on_irc_notice => [ $bot, "handle_irc_notice" ],
-# # ... and a dozen other interesting IRC events ...
-# );
-#
-# The simplified syntax extends the simplified object syntx. The
-# scalar "method_name" is replaced by a list of method names or a map
-# of event names to method names.
-#
-# An arrayref is used when the handler methods and event names are
-# identical.
-#
-# This group of syntaxes specify multiple event names in their
-# callback definitions. They are all lumped under the "callbacks"
-# parameter.
-
-my $eg_object_3 = eg_object("simplified multiple method callbacks");
-push @things, ThingWithCallbacks->new(
- callbacks => [ $eg_object_3, [qw( event_a event_b event_c )] ],
-);
-
-# A hashref is used to map event names to method names.
-
-my $eg_object_4 = eg_object("simplified multiple mapped methods");
-push @things, ThingWithCallbacks->new(
- callbacks => [
- $eg_object_3, {
- event_a => "handler_method_a",
- event_b => "handler_method_b",
- event_c => "handler_method_c",
- },
- ],
-);
-
-# Multiple method callbacks may also be defined with explicit
-# syntaxes.
-
-my $eg_object_5 = eg_object("explicit multiple method callbacks");
-push @things, ThingWithCallbacks->new(
- callbacks => cb_object(
- $eg_object_5,
- [qw( event_a event_b event_c)]
- ),
-);
-
-my $eg_object_6 = eg_object("explicit multiple mapped methods");
-push @things, ThingWithCallbacks->new(
- callbacks => cb_object(
- $eg_object_6, {
- event_a => "handler_method_a",
- event_b => "handler_method_b",
- event_c => "handler_method_c",
- },
- ),
-);
-
-#########################
-# Class method callbacks.
-#
-# Class methods may be called using the same syntaxes as object
-# method. As of this writing, the mechanisms for invokving class
-# methods are identical in Perl to those of invoking object methods.
-# An cb_class() utility function is provided for forward
-# compatibility. If the mechanisms were to diverge in a future
-# version of Perl, cb_class() would updated to accommodate the
-# change.
-
-# Examples aren't shown since they would look nearly identical to
-# previous ones.
-
-#######################
-# Role based callbacks.
-#
-# Role-based callbacks map an object's responses to its destination's
-# methods using a simple algorithm. Method names consist of a prefix
-# ("handle"), the sub-object's role (perhaps "dns"), and the
-# sub-object's event name ("answer") joined by underscores to become:
-# handle_dns_answer().
-#
-# In theory, each object performs a task or role that contributes to
-# the program as a whole. Larger, more complex objects are built by
-# gluing together smaller objects that perform simpler roles. For
-# example, a simple HTTP client might glue together some generic
-# objects like so:
-#
-# HTTP client
-# Keep-alive connection manager ("keepalive" object)
-# Asynchronous DNS resolver ("resolver" object)
-# Asynchronous TCP connector ("connector" object)
-# HTTP stream ("httpstream" object)
-# Asynchronous stream ("stream" object)
-# HTTP protocol ("http" object)
-#
-# At each level, the container object knows the interfaces for the
-# smaller objects within it. It can therefore assign the smaller
-# objects roles and implicitly handle their events by defining methods
-# with predictable names.
-
-# Currently there is only the explicit cb_role() function to define
-# roles. Implicit syntax is left for a future release.
-#
-# $eg_object_7->handle_ticker_tick() is called in response to the
-# following Reflex::Timer's "tick" event.
-
-my $eg_object_7 = eg_object("explicit role, explicit prefix");
-push @things, ThingWithCallbacks->new(
- callbacks => cb_role($eg_object_7, "ticker", "handle"),
-);
-
-# The third parameter to cb_role() is the method prefix, which
-# defaults to "handle" if omitted. $eg_object_8's method
-# handle_ticker_tick() is called below. The "handle" is implied by
-# default.
-
-my $eg_object_8 = eg_object("explicit role, implicit prefix");
-push @things, ThingWithCallbacks->new(
- callbacks => cb_role($eg_object_8, "ticker"),
-);
-
-######################
-# Promises or futures.
-#
-# Promises are the final callback mechanism Reflex supports. They are
-# defined by either not defining callbacks at all, or by defining
-# cb_promise() as the callbacks mechanism.
-#
-# Note however that this code will block. Nothing beyond it runs
-# until the while() loop finishes. Which may be "never". Other
-# caveats may apply.
-
-my $implicit_promisory_timer = ThingWithCallbacks->new();
-
-while (my $next_event = $implicit_promisory_timer->next_event()) {
- eg_tell("implicit promisory timer generated event $next_event");
-}
-
-# People who dislike invisible logic might prefer cb_promise().
-#
-my $explicit_promisory_timer = ThingWithCallbacks->new(
- callbacks => cb_promise(),
-);
-
-while (my $next_event = $explicit_promisory_timer->next_event()) {
- eg_tell("explicit promisory timer generated event $next_event");
-}
-
-###############
-# Run the demo.
-
-Reflex::Object->run_all();
-exit;
View
137 eg/eg-23-rcb-class.pl
@@ -0,0 +1,137 @@
+#!/usr/bin/env perl
+
+# This example illustrates explicit callbacks via classes, where
+# callback events are mapped to class methods by name. Methods may be
+# named after the events they handle, or they may differ.
+
+# Reflex::Callbacks and the Reflex::Callback helper classes will
+# abstract callbacks to fulfill a number of goals. The goals are
+# detailed in docs/requirements.otl and summarized in
+# eg/eg-20-rcb-callback.pl
+
+use warnings;
+use strict;
+use lib qw(../lib);
+
+# Create a thing that will invoke callbacks. This syntax uses
+# explicitly specified cb_class() callbacks and a scalar for the
+# methods list. cb_method() would be slightly more efficient in this
+# case, but cb_class() also works.
+#
+# There is no nonambiguous implicit syntax at this time. Suggestions
+# for one are welcome.
+
+{
+ package ScalarHandlerClass;
+ use Moose;
+
+ use ExampleHelpers qw(eg_say);
+ use Reflex::Callbacks qw(cb_class);
+ use ThingWithCallbacks;
+
+ has callback_thing => ( is => 'rw', isa => 'ThingWithCallbacks' );
+
+ sub BUILD {
+ my $self = shift;
+
+ $self->callback_thing(
+ ThingWithCallbacks->new(
+ cb_class(__PACKAGE__, "on_event"),
+ )
+ );
+ }
+
+ sub on_event {
+ my ($self, $arg) = @_;
+ eg_say("$self - scalar class handled event");
+ }
+
+ sub run_thing {
+ my $self = shift;
+ $self->callback_thing()->run();
+ }
+}
+
+my $sho = ScalarHandlerClass->new();
+$sho->run_thing();
+
+# In this case, a class handles a list of callbacks. Each callback
+# method is named after the event it handles.
+#
+# There is no nonambiguous implicit syntax for this either, but
+# suggestions are welcome.
+
+{
+ package ArrayHandlerClass;
+ use Moose;
+
+ use ExampleHelpers qw(eg_say);
+ use Reflex::Callbacks qw(cb_class);
+ use ThingWithCallbacks;
+
+ has callback_thing => ( is => 'rw', isa => 'ThingWithCallbacks' );
+
+ sub BUILD {
+ my $self = shift;
+
+ $self->callback_thing(
+ ThingWithCallbacks->new(
+ cb_class(__PACKAGE__, ["on_event"]),
+ )
+ );
+ }
+
+ sub on_event {
+ my ($self, $arg) = @_;
+ eg_say("$self - array class handled event");
+ }
+
+ sub run_thing {
+ my $self = shift;
+ $self->callback_thing()->run();
+ }
+}
+
+my $aho = ArrayHandlerClass->new();
+$aho->run_thing();
+
+# In this case, a class handles a hash of callbacks. Hash keys are
+# event names, and the values are the corresponding handler method
+# names. The hash gives classes flexibility in the methods they use.
+#
+# There is no nonambiguous implicit syntax for this either, but
+# suggestions are welcome.
+
+{
+ package HashHandlerClass;
+ use Moose;
+
+ use ExampleHelpers qw(eg_say);
+ use Reflex::Callbacks qw(cb_class);
+ use ThingWithCallbacks;
+
+ has callback_thing => ( is => 'rw', isa => 'ThingWithCallbacks' );
+
+ sub BUILD {
+ my $self = shift;
+
+ $self->callback_thing(
+ ThingWithCallbacks->new(
+ cb_class(__PACKAGE__, { on_event => "handle_event" }),
+ )
+ );
+ }
+
+ sub handle_event {
+ my ($self, $arg) = @_;
+ eg_say("$self - hash class handled event");
+ }
+
+ sub run_thing {
+ my $self = shift;
+ $self->callback_thing()->run();
+ }
+}
+
+my $hho = HashHandlerClass->new();
+$hho->run_thing();
View
3  lib/Reflex/Callback/Method.pm
@@ -3,9 +3,10 @@ package Reflex::Callback::Method;
use Moose;
extends 'Reflex::Callback';
+# It's a class if it's a Str.
has object => (
is => 'ro',
- isa => 'Object',
+ isa => 'Object|Str',
weak_ref => 1,
);
View
2  lib/Reflex/Callbacks.pm
@@ -93,7 +93,7 @@ sub cb_object {
}
sub cb_class {
- die;
+ cb_object(@_);
}
sub cb_role {
Please sign in to comment.
Something went wrong with that request. Please try again.