Lately I've been developing a skunkworks project to standardize
POE::Component, code named POE::Stage.

I'm happy with its progress so far, and I thought I would share it
with a wider audience.  Subversion can fetch a copy of the source from
https://thirdlobe.com/svn/poe-stage/ .  You can also browse the source
there, but http://thirdlobe.com/projects/poe-stage/browser/ might be
nicer.

To be sure, I have ulterior motives for opening the project: I don't
have as much time to devote to it as I would like.  It's my hope that
people will fall in love with the idea and offer to help it see
fruition.

Please contact me for a developer account if my plan works.

To help whet your appetite, the rest of this message describes some of
POE::Stage's labor-saving features.  Barring any unforseen bugs, they
work now.

Standardize and simplify the calling convention for message handlers.

  sub begin_request {
    my ($self, $args) = @_;

    while (my ($arg, $value) = each %$args) {
      print "$arg = $value\n";
    }
  }

Maintain state between requests and responses, albeit at the expense
of using special data members.

  # $self->{req} refers to the current request being handled by this
  # object.  Things stored under $self->{req} persist for the duration
  # of the current request.

  sub begin_request {
    my ($self, $args) = @_;
    $self->{req}{foo} = POE::Request->new( ... );

    $self->{req}{foo}{data} = "persists for the request";
  }

  # $self->{rsp} refers to the current response being handled.  Data
  # beneath $self->{rsp} corresponds to data stored under the request
  # that prompted it.

  sub handle_response {
    my ($self, $args) = @_;

    print "$self->{rsp}{data}\n";  # "persists for the request"
  }

Requests can be used as response channels.

  sub handle_request {
    my ($self, $args) = @_;

    if ($args->{socket}) {
      $self->{req}->return(
        _type  => "success",
        socket => $args->{socket},
      );
      return;
    }

    $self->{req}->return(
      _type  => "failure",
      errnum => $args->{error_number},
      errstr => $args->{error_message},
    );
  }

Standardize request messages, and map response types to methods at
request time.

  sub begin_request {
    my ($self, $args) = @_;

    # Create a request.  On success, pass a successful response to my
    # interact() method.  On failure, pass the failure response to
    # log_error().

    $self->{req}{foo} = POE::Request->new(
      _stage => $object,
      _method => "method_name",
      _on_success => "interact",
      _on_failure => "log_error"
    );
  }

Everything is automatically cleaned up when a request is canceled or a
response is generated.

  POE::Stage wraps timers and I/O event watchers in POE::Watcher
  objects.  These objects automatically clean up the underlying
  POE::Kernel resources when they destruct.

  Canceling or concluding a request causes its data scope to be
  destroyed.  As long as POE::Watcher objects are scoped to the
  requests they belong to, they will self-destruct and clean up at the
  proper times.

    sub handle_request {
      my ($self, $args) = @_;
      $self->{req}{timeout} = POE::Watcher::Delay->new( ... );
    }

  $self->{req}->return(...) terminates the current request as a side
  effect, ensuring timers and I/O watchers associated with
  $self->{req} are cleaned up.

  Likewise a requester can cancel a request it has made by allowing it
  to destruct.  delete($self->{req}{foo}) cancels $self->{req}{foo}.

  Finally, the convention of storing new requests within the current
  request causes request destruction to be chained.  Canceling a
  top-level request will also cancel the tree of sub-requests beneath
  it.

Thanks for reading.

-- 
Rocco Caputo - http://poe.perl.org/

Reply via email to