#!/usr/bin/env perl -l # This is a little test program to see if we could implement # http://candygram.sourceforge.net/node6.html on MooseX::POE # using MX::Poe objects (aka POE::Sessions) to replace threads # Here is the relevant code from Canygram # # # >>> import candygram as cg # >>> import time # >>> def proc_func(): # ... r = cg.Receiver() # ... r.addHandler('land shark', shut_door, cg.Message) # ... r.addHandler('candygram', open_door, cg.Message) # ... for message in r: # ... print message # ... # >>> def shut_door(name): # ... return 'Go Away ' + name # ... # >>> def open_door(name): # ... return 'Hello ' + name # ... # >>> proc = cg.spawn(proc_func) # >>> proc.send('land shark') # >>> proc.send('candygram') # >>> # Give the proc a chance to print its messages before termination: # ... time.sleep(1) # # here is our version # sub main { sub proc_func { my $r = $_[0]->receiver; $r->add_handler( 'land_shark', \&shut_door ); $r->add_handler( 'candygram', \&open_door ); while (<$r>) { print; } } sub shut_door { return 'Go away ' . $_[1]; } sub open_door { return 'Hello ' . $_[1]; } my $proc = Candygram->spawn( \&proc_func ); $proc->send('land_shark'); $proc->send('candygram'); POE::Kernel->run; # we have to run the kernel manually } # # Implementation # { package Candygram; sub spawn { my ( $self, $func ) = splice @_, 0, 2; return Proc->new( func => $func, args => \@_ ); } } # # the Receiver object does all the real work # { package Receiver; use Moose; use overload '<>' => \&receive; has mailbox => ( isa => 'ArrayRef', is => 'ro', auto_deref => 1, default => sub { [] }, ); has handlers => ( isa => 'HashRef', is => 'ro', default => sub { {} }, ); sub add_handler { my ( $self, $state, $code ) = @_; return if exists $self->handlers->{$state}; $self->handlers->{$state} = $code; } # This could be cleaned up a bunch by MooseX::AttributeHelpers on the # attributes above, but I didn't wan't to have a dependency # for a example script sub receive { my $self = shift; my ( $state, $args ); return unless scalar @{ $self->mailbox }; for ( 0 .. $#{ $self->mailbox } ) { my $state = $self->mailbox->[$_]->[0]; next unless $state; next unless exists $self->handlers->{$state}; ( $state, $args ) = @{ splice @{ $self->mailbox }, $_, 1 }; if ( $state && $args ) { my $res = $self->handlers->{$state}->(@$args); return $res; } } return; } } { package Proc; use MooseX::POE; has func => ( isa => 'CodeRef', is => 'ro', default => sub { sub { } }, ); has args => ( isa => 'ArrayRef', is => 'ro', auto_deref => 1, default => sub { [] }, ); has receiver => ( is => 'ro', default => sub { Receiver->new() }, ); sub START { my ($self) = @_; $self->yield('loop'); } sub on_loop { my ($self) = @_; my $func = $self->func; $self->$func( $self->args ); } sub send { my ( $self, $message ) = @_; push @{ $self->receiver->mailbox }, [ $message, \@_ ]; $self->yield('loop'); } } main();