Permalink
Browse files

Support POE components that require registration, such as POE::Compon…

…ent::IRC.
  • Loading branch information...
1 parent 9bd513c commit b64e34a427198aa43a1839507e42ce9a6ef0cdce @rcaputo committed Sep 18, 2009
Showing with 181 additions and 26 deletions.
  1. +44 −0 docs/PoeSession.pm
  2. +1 −1 docs/Postback.pm
  3. +44 −3 docs/StageRole.pm
  4. +77 −0 docs/eg-13-irc-bot.pl
  5. +15 −22 docs/requirements.otl
View
@@ -0,0 +1,44 @@
+package PoeSession;
+
+use Moose;
+extends 'Stage';
+use Scalar::Util qw(weaken);
+use POE::Session; # for ARG0
+
+my %session_id_to_object;
+
+has sid => (
+ isa => 'Str',
+ is => 'ro',
+);
+
+sub BUILD {
+ my $self = shift;
+
+ $session_id_to_object{$self->sid()}{$self} = $self;
+ weaken $session_id_to_object{$self->sid()}{$self};
+}
+
+sub DEMOLISH {
+ my $self = shift;
+ delete $session_id_to_object{$self->sid()}{$self};
+ delete $session_id_to_object{$self->sid()} unless (
+ keys %{$session_id_to_object{$self->sid()}}
+ );
+}
+
+sub deliver {
+ my ($class, $sender_id, $event, $args) = @_;
+
+ # Not a session anyone is interested in.
+ return unless exists $session_id_to_object{$sender_id};
+
+ foreach my $self (values %{$session_id_to_object{$sender_id}}) {
+ $self->emit(
+ event => $event,
+ args => [ @$args ],
+ );
+ }
+}
+
+1;
View
@@ -16,7 +16,7 @@ sub new {
my $self = bless sub {
$POE::Kernel::poe_kernel->post(
- $object->session_id(), "call_gate", $object, $method, {
+ $object->session_id(), "call_gate_method", $object, $method, {
passthrough => $passthrough_args,
callback => [ @_ ],
},
View
@@ -73,9 +73,27 @@ my $singleton_session_id = POE::Session->create(
# call_gate() uses this to call methods in the right session.
- call_gate => sub {
+ call_gate_method => sub {
my ($stage, $method, @args) = @_[ARG0..$#_];
- $stage->$method(@args);
+ return $stage->$method(@args);
+ },
+
+ call_gate_coderef => sub {
+ my ($coderef, @args) = @_[ARG0..$#_];
+ return $coderef->(@args);
+ },
+
+ # Catch dynamic events.
+
+ _default => sub {
+ my ($event, $args) = @_[ARG0, ARG1];
+
+ return $event->deliver($args) if "$event" =~ /^PoeEvent(?:::|=)/;
+
+ return if PoeSession->deliver($_[SENDER]->ID, $event, $args);
+
+ # Unhandled event.
+ # TODO - Anything special?
},
### Support POE::Wheel classes.
@@ -512,11 +530,34 @@ sub call_gate {
);
$POE::Kernel::poe_kernel->call(
- $self->session_id(), "call_gate", $self, $method, @_[2..$#_]
+ $self->session_id(), "call_gate_method", $self, $method, @_[2..$#_]
);
return 0;
}
+sub run_within_session {
+ my ($self, $method) = @_;
+
+ if (
+ $self->session_id() eq $POE::Kernel::poe_kernel->get_active_session()->ID()
+ ) {
+ if (ref($method) =~ /^CODE/) {
+ return $method->(@_[2..$#_]);
+ }
+ return $self->$method(@_[2..$#_]);
+ }
+
+ if (ref($method) =~ /^CODE/) {
+ return $POE::Kernel::poe_kernel->call(
+ $self->session_id(), "call_gate_coderef", $method, @_[2..$#_]
+ );
+ }
+
+ return $POE::Kernel::poe_kernel->call(
+ $self->session_id(), "call_gate_method", $self, $method, @_[2..$#_]
+ );
+}
+
sub run_all {
POE::Kernel->run();
}
View
@@ -0,0 +1,77 @@
+#!/usr/bin/env perl
+
+# Using POE::Component::IRC. That component requires the user to
+# register for events. The new PoeSession watcher is used to receive
+# all events from the component.
+
+use strict;
+use warnings;
+
+{
+ package Bot;
+ use Moose;
+ extends 'Stage';
+ use ObserverTrait;
+ use PoeSession;
+
+ use POE qw(Component::IRC);
+
+ has component => (
+ isa => 'Object|Undef',
+ is => 'rw',
+ );
+
+ has poco_watcher => (
+ isa => 'PoeSession',
+ is => 'rw',
+ traits => ['Observer'],
+ role => 'poco',
+ );
+
+ sub BUILD {
+ my $self = shift;
+
+ $self->component(
+ POE::Component::IRC->spawn(
+ nick => "stage$$",
+ ircname => "POE::Stage Test Bot",
+ server => "10.0.0.25",
+ ) || die "Drat: $!"
+ );
+
+ $self->poco_watcher(
+ PoeSession->new(
+ sid => $self->component()->session_id(),
+ )
+ );
+
+ $self->run_within_session(
+ sub {
+ $self->component()->yield(register => "all");
+ $self->component()->yield(connect => {});
+ }
+ )
+ }
+
+ sub on_poco_irc_001 {
+ my $self = shift;
+ print "Connected. Joining a channel...\n";
+ $self->component->yield(join => "#room");
+ }
+
+ sub on_poco_irc_public {
+ my ($self, $args) = @_;
+ my ($who, $where, $what) = @$args;
+
+ my $nick = (split /!/, $who)[0];
+ my $channel = $where->[0];
+
+ if (my ($rot13) = $what =~ /^rot13 (.+)/) {
+ $rot13 =~ tr[a-zA-Z][n-za-mN-ZA-M];
+ $self->component->yield(privmsg => $channel => "$nick: $rot13");
+ }
+ }
+}
+
+Bot->new()->run_all();
+exit;
View
@@ -1,4 +1,4 @@
-[_] 41% Framework Requirements
+[_] 42% Framework Requirements
About
This document summarizes the best ideas from the patterns document.
The patterns document tries to enumerate all available options.
@@ -147,7 +147,7 @@
[_] 0% Receiver data is not visible to the sender.
[_] 0% Message-scoped resources should be stored in the message's continuation.
[_] 0% Message cancelation triggers associated resource cleanup.
- [_] 46% Common primitive classes must be provided.
+ [_] 54% Common primitive classes must be provided.
[_] 54% Low-level event watchers.
[X] 100% I/O
[X] 100% Handle
@@ -170,9 +170,9 @@
[_] 0% TcpServer
[X] 100% UdpPeer
[_] 0% What else? Probably a lot!
- [_] 62% POE Interfaces
+ [_] 87% POE Interfaces
[X] 100% Wheel wrappers.
- [_] 25% Generic Component shims.
+ [_] 75% Generic Component shims.
[X] 100% Create a postback analog for components that expect postbacks.
About
Creates a coderef that, when called, posts a message to the object's session, with routing information back to the object.
@@ -190,7 +190,7 @@
It could be a dynamically created session for the purpose of interfacing.
Indirection would be heavy.
Consider it for a future revision.
- [_] 0% Create an event analog for components that expect events.
+ [X] 100% Create an event analog for components that expect events.
About
Many components allow callers to specify return events.
We create a unique, anonymous event that calls a specific object and method upon dispatch.
@@ -199,34 +199,27 @@
my $event = $self->anon_event($method_name, @passthru_params);
$_[KERNEL]->post($event, @callback_params);
Syntax if events may not be blessed
- [_] 0% TODO
+ [X] 100% TODO
Some explicit cleanup must be provided and adhered to.
- Cannot rely on object DESTROY or DEMOLISH to automatically clean up for us.
+ Rely on object DEMOLISH to automatically clean up for us.
+ CAVEAT: Components that stringify event names will fail.
Dispatch Mechanism
_default
- Unknown events are passed through a lookup table.
- If they exist in the table, they are dispatched to the appropriate $object->$method().
- Dispatch goes through an additional table.
- state()
- POE::Kernel->state() is used to register handlers for each anonymous event.
- Failure to clean up will result in memory leaks.
- There is less runtime indirection.
- [_] 0% Session and optional event subscription.
+ Events that are objects in the PoeEvent class are invoked to deliver themselves.
+ [X] 100% Session and optional event subscription.
About
- A client session creates the component, to be used as a service.
- The client session registers interest in one or more predefined events.
- The evens are posted to the session of interest.
+ A client stage creates the component, to be used as a service.
+ The client stage registers interest in the service's events.
+ The service's events are posted to all interested stages.
POE::Component::IRC will be a good example component.
Syntax
- [_] 0% TODO
+ PoeSession watcher.
Dispatch Mechanism
Sender Interest
The object creates the component.
The object registers interest in all events from the component.
- The object registers session-interest in one or more events from the component.
- Others?
- [_] 0% TODO
[_] 0% Components that emit specific events require Wheel-like wrappers.
+ [_] 0% Is this worth supporting?
[_] 50% Optimizations.
[X] 100% Only $kernel->call() when we need to switch sessions.
[_] 0% Consolidate POE-specific code out into a single role.

0 comments on commit b64e34a

Please sign in to comment.