Author: bdonlan
Date: 2004-07-20 20:41:17 -0400 (Tue, 20 Jul 2004)
New Revision: 320
Modified:
branches/new-poe-client/main/client/lib/Haver/Client/POE.pm
Log:
* main/client/lib/Haver/Client/POE.pm: Redid event APIs, partial implementation.
Modified: branches/new-poe-client/main/client/lib/Haver/Client/POE.pm
===================================================================
--- branches/new-poe-client/main/client/lib/Haver/Client/POE.pm 2004-07-19
01:01:57 UTC (rev 319)
+++ branches/new-poe-client/main/client/lib/Haver/Client/POE.pm 2004-07-21
00:41:17 UTC (rev 320)
@@ -21,19 +21,19 @@
# self => {
# [persistent data]
# options => { key => value },
-# wants => {
-# want => {
-# hhash => {
-# stringified $handler => $index_of_handler
-# },
-# harray => [
-# $handler
-# ],
-# },
-# }, # wants =>
# handlers => {
-# [same as wants]
-# },
+# type => {
+# event name => {
+# reverse => {
+# stringified $subref => index
+# },
+# forward => [
+# $subref,
+# ...
+# ]
+# } # event name =>
+# } # type =>
+# }, # handlers =>
# connection => {
# [connection data]
# UID => $uid,
@@ -120,27 +120,159 @@
$ehash = {qw{
_start _start
_default _default
- }), %$ehash};
+ }, %$ehash};
return $self->SUPER::_object_states($ehash);
}
### SETUP
-=head2 new($Z<>alias [, option => value ...])
+=head2 new([option => value ...])
-Creates a new Haver::Client::POE session with alias $alias, and optionally
-sets one or more options (see C<setoptions>)
+Creates a new Haver::Client::POE session, and optionally sets one or more
+options (see C<setoptions>)
=cut
sub new {
- die "STUB";
+ my ($class, %options) = @_;
+ $class = ref $class || $class;
+
+ my $self = {
+ options => {
+ version => "Haver::Client::POE/$VERSION",
+ },
+ handlers => {
+ },
+ };
+ bless $self, $class;
+ $self->_init_wants;
+ $self->_init_msgs;
}
sub _start {
die "STUB";
}
+sub _init_event_type {
+ my $self = shift;
+ $self->{handlers}{shift} = {
+ hhash => {},
+ byname => {},
+ }
+}
+
+=head1 HANDLERS
+
+Handlers are subroutines called to handle some event from the server. Any
+number of handlers may be registered to an event, and they will be called
+in reverse order of registration until one indicates that it has handled the
+event.
+
+The handler prototype is as follows:
+
+ sub handler {
+ my ($type, $event, @args) = @_;
+ # ...
+ return @args; # To pass the event
+ return undef; # To handle the event
+ }
+
+If the handler returns a list, it is used for the @args of the next handler in
+the chain. If it returns undef, the chain is terminated.
+
+=head2 TYPES OF EVENTS
+
+=head3 want
+
+Events of type 'want' are sent when the server sends a S: WANT message. The
+first argument of the S: WANT is the event name, the rest are used as
+arguments for the first handler.
+
+The default action, should all handlers pass the event, is to send a
+C: CANT $event to the server.
+
+=head3 smsg
+
+Events of type 'smsg' are sent whenever a command is received from the server.
+The command name is used for the event name, and the arguments are passed for
+the first handler's @args.
+
+Note that adding a handler with $type of smsg, and $event of 'WANT' may
+prevent handlers of $type want from being called.
+
+=head2 METHODS FOR EVENT MANIPULATION
+
+=head3 register_handler($Z<>type, $Z<>event, $Z<>handler)
+
+Register a handler $handler for an event of type $type, name $event. If such
+a handler already exists, does nothing.
+
+=cut
+
+sub register_handler {
+ my ($self, $type, $event, $handler) = @_;
+ my $htype = $self->{handlers}{$type}
+ or croak "Unknown event type $type";
+ my $hevent = $htype->{$event};
+ if (!$hevent) {
+ $hevent = $htype->{$event} = {
+ forward => {},
+ reverse => {},
+ };
+ }
+ return if (exists $hevent->{reverse}{$handler});
+ my $index = @{$hevent->{forward}};
+ push @{$hevent->{forward}}, $handler;
+ $hevent->{reverse}{$handler} = $index;
+}
+
+=head3 unregister_handler($Z<>type, $Z<>event, $Z<>handler)
+
+Unregisters a handler previously registered by passing the same arguments
+to register_handler. If such a handler does not exist, does nothing.
+
+=cut
+
+sub unregister_handler {
+ my ($self, $type, $event, $handler) = @_;
+ my $htype = $self->{handlers}{$type}
+ or croak "Unknown event type $type";
+ my $hevent = $self->{handlers}{$type}{$event} or return;
+ my $index = $hevent->{reverse}{$handler} or return;
+ # Remove the item
+ splice @{$hevent->{forward}}, $index, 1;
+ # and renumber the ones afterward
+ my $len = @{$hevent->{forward}};
+ for (; $index < $len; $index++) {
+ my $handler = $hevent->{forward}[$index];
+ $hevent->{reverse}{$handler} = $index;
+ }
+ # and delete the reverse mapping
+ delete $hevent->{reverse}{$handler};
+}
+
+sub _init_wants {
+ my $self = shift;
+ my %wants = {
+ # TODO ...
+ };
+ $self->_init_event_type('wants');
+ for (my ($event, $handler) = each %wants) {
+ $self->register_handler($self, 'want', $event, $handler);
+ }
+}
+
+sub _init_smsg {
+ my $self = shift;
+ my %smsgs = {
+ # TODO ...
+ };
+ $self->_init_event_type('smsg');
+ for (my ($event, $handler) = each %smsgs) {
+ $self->register_handler($self, 'smsg', $event, $handler);
+ }
+}
+
=head1 MESSAGES
=head2 setoptions(option => value [, ...])
@@ -178,7 +310,7 @@
Unregisters the given handler from receiving messages.
-=head2 register_want($Z<>wantname => sub { ... } [,...])
+=head2 register_handler($Z<>wantname => sub { ... } [,...])
Registers handlers to be called if the given command is requested from the
server. The most recently registered handler is called first, with its