Skip to content

Commit

Permalink
Begin exploring parameterized Moose roles for static composition.
Browse files Browse the repository at this point in the history
Should be faster and more Moose-friendly than Reflex dynamic roles,
although dynamic roles may still be useful for managing collections.
Pure Moose roles should also be cleaner for people who like that sort
of thing.
  • Loading branch information
rcaputo committed May 8, 2010
1 parent ec29ac5 commit b1d4291
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 0 deletions.
76 changes: 76 additions & 0 deletions eg/roles/Proxy.pm
@@ -0,0 +1,76 @@
package Proxy;
use Moose;
extends 'Reflex::Object';

has handle_a => ( is => 'rw', isa => 'FileHandle', required => 1 );
has handle_b => ( is => 'rw', isa => 'FileHandle', required => 1 );

with 'Readable' => {
handle => 'handle_a',
active => 1,
};

with 'Readable' => {
handle => 'handle_b',
active => 1,
};

# TODO - Next two roles should be Writable, but I haven't figured out
# the syntax for subclassing parameterized roles and overriding their
# default parameters. "knob_suffix", "select_method" and "event"
# ought to be hidden in the defaults for a Writable role.

with 'Readable' => {
handle => 'handle_a',
knob_suffix => '_wr',
select_method => 'select_write',
event => 'writable',
};

with 'Readable' => {
handle => 'handle_b',
knob_suffix => '_wr',
select_method => 'select_write',
event => 'writable',
};

# TODO - put() methods.
# TODO - Write buffering to be provided by a Streamable role.

sub on_readable_handle_a_readable {
my ($self, $arg) = @_;
my $octet_count = sysread($self->handle_a(), my $buffer = "", 65536);
if ($octet_count) {
$self->emit(
event => "handle_a_data",
args => {
data => $buffer,
handle => $self->handle_a()
},
);
return;
}

return if defined $octet_count;
warn $!;
}

sub on_readable_handle_b_readable {
my ($self, $arg) = @_;
my $octet_count = sysread($self->handle_b(), my $buffer = "", 65536);
if ($octet_count) {
$self->emit(
event => "handle_b_data",
args => {
data => $buffer,
handle => $self->handle_b(),
}
);
return;
}

return if defined $octet_count;
warn $!;
}

1;
103 changes: 103 additions & 0 deletions eg/roles/Readable.pm
@@ -0,0 +1,103 @@
package Readable;
use MooseX::Role::Parameterized;

use Scalar::Util qw(weaken);

parameter handle => (
isa => 'Str',
default => 'handle',
);

parameter knob_suffix => (
isa => 'Str',
default => '_rd',
);

parameter knob => (
isa => 'Str',
default => sub {
my $self = shift;
$self->handle() . $self->knob_suffix();
},
lazy => 1,
);

parameter select_method => (
isa => 'Str',
default => 'select_read',
);

parameter event => (
isa => 'Str',
default => 'readable',
);

parameter active => (
isa => 'Bool',
default => 0,
);

role {
my $p = shift;

my $h = $p->handle();
my $k = $p->knob();
my $m = $p->select_method();
my $e = $p->event();
my $active = $p->active();
my $trigger_name = "_${k}_changed";
my $emit_name = "${h}_${e}";

has $k => (
is => 'rw',
isa => 'Bool',
default => $active,
trigger => sub {
my $self = shift;
$self->$trigger_name(@_);
},
initializer => sub {
my $self = shift;
$self->$trigger_name(@_);
},
);

method $trigger_name => sub {
my ($self, $value) = @_;

# Must be run in the right POE session.
return unless $self->call_gate($trigger_name, $value);

# Turn on watcher.
if ($value) {
my $envelope = [ $self ];
weaken $envelope->[0];
$POE::Kernel::poe_kernel->$m(
$self->$h(), 'select_ready', $envelope, $e, $emit_name
);
return;
}

# Turn off watcher.
$POE::Kernel::poe_kernel->$m($self->$h(), undef);
};

# Turn off watcher during destruction.
after DESTROY => sub {
my $self = shift;
$self->$k(0) if $self->$k();
};

# Part of the POE/Reflex contract.
method _deliver => sub {
my ($self, $handle, $mode, $event_name) = @_;
$self->emit(
event => $event_name,
args => {
handle => $handle,
}
);
};
};

1;
18 changes: 18 additions & 0 deletions eg/roles/Writable.pm
@@ -0,0 +1,18 @@
package Writable;
use MooseX::Role::Parameterized;

die die die "doesn't work, see source";

# TODO - Figure out how to subclass parameterized roles and override
# attributes of their parameters. For instance, Writable only differs
# from Readable in the default parameters!

with 'Readable';

parameter '+knob_suffix' => ( default => '_wr' );
# TODO - Etc.

# Don't define anything; use what Readable gives us?
role { };

1;
28 changes: 28 additions & 0 deletions eg/roles/proxy.pl
@@ -0,0 +1,28 @@
use Moose;
use POE::Pipe::TwoWay;
use Socket qw(AF_UNIX SOCK_STREAM PF_UNSPEC);
use Proxy;

# Need two sockets to test passing data back and forth.
my ($socket_a, $socket_b);
socketpair($socket_a, $socket_b, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die $!;

my $p = Proxy->new(
handle_a => $socket_a,
handle_b => $socket_b,
);

# Write test data to one end of the proxy.
print $socket_a "test request\n";

# Wait for it to arrive at the other end.
# Send something back.
{
my $e = $p->wait();
warn $e->{name}, ": ", $e->{arg}{data};
print {$e->{arg}{handle}} "test response\n";
}

# Wait for it to arrive back.
my $e = $p->wait();
warn $e->{name}, ": ", $e->{arg}{data};

0 comments on commit b1d4291

Please sign in to comment.