POE::Component::Client::REST - Low-level interface for REST calls


POE-Component-Client-CouchDB documentation Contained in the POE-Component-Client-CouchDB distribution.

Index


Code Index:

NAME

Top

POE::Component::Client::REST - Low-level interface for REST calls

VERSION

Top

Version 0.05

SYNOPSIS

Top

This class abstracts away some of the nastier details of talking to a REST service.

    use POE qw(Component::Client::REST::JSON);

    # simple CouchDB example

    POE::Session->create(inline_states => {
      _start => sub {
        $poe_kernel->alias_set('foo');
        my $rest = $_[HEAP]->{rest} = POE::Component::Client::REST::JSON->new;
        $rest->call(GET => 'http://localhost:5984/_all_dbs', callback =>
          [$_[SESSION], 'response']);
      },

      response => sub {
        my ($data, $response) = @_[ARG0, ARG1];
        die $response->status_line unless $response->code == 200;

        print 'Databases: ' . join(', ', @$data) . "\n";
        $poe_kernel->alias_remove('foo');
        $_[HEAP]->{rest}->shutdown();
      },
    });

    $poe_kernel->run();

ATTRIBUTES

Top

Alias

The string to use as the alias for the internal session.

http

The alias of the spawned HTTP object's session. Defaults to something reasonable.

request_cooker

A function which takes a request and cooks it in some fashion, returning a new one (or the same one, modified). Undefined by default, but see POE::Component::Client::REST::JSON.

response_cooker

Similar to the above, but returns a list of things to be passed to the callbacks when a response is received for a particular request.

METHODS

Top

call method, url, keywords

Makes an HTTP request to url via method. The following keyword arguments are accepted (either as a hashref or just as extra argument pairs).

request_cooker
response_cooker

Overrides the object defaults for these values. Explicitly pass undef to specify that no cooking should be done.

query

A hashref of query parameters to be appended as a query string.

content

Data (which may or may not get cooked) to be passed as the request body.

headers

Specify arbitrary headers as an arrayref of key-value pairs.

callback

Either a coderef or an arrayref of two elements (session and state name) to call (or post to) with the (possibly cooked) response to the request.

You can also post to this session's 'call' state for the same result, although in this form the keywords must be a hashref.

spawn kwargs or hashref

new kwargs or hashref

Passes all options to POE::Component::Client::HTTP except for Alias, which is used for our own session. The HTTP session has an alias of "$Alias-HTTP". The default session alias for the internal session is REST-JSON-Session.

shutdown

Calls shutdown on the HTTP client and stops this session. You can also post to the 'shutdown' state for the same result.

AUTHOR

Top

Paul Driver, <frodwith at cpan.org>

BUGS

Top

This was written for use with POE::Component::Client::CouchDB, so it's sort of tailored to that API and probably unsuitable without modification for other purposes. Patches welcome!

COPYRIGHT & LICENSE

Top


POE-Component-Client-CouchDB documentation Contained in the POE-Component-Client-CouchDB distribution.

package POE::Component::Client::REST;
use POE qw(Component::Client::HTTP);
use Moose;
use HTTP::Request;

our $VERSION = '0.05';

has Alias => (
  is      => 'ro',
  default => 'REST-Session',
);

has http => (
  is      => 'ro',
  lazy    => 1,
  default => sub { my $str = $_[0]->Alias; "$str-HTTP" },
);

has request_cooker => (
  is        => 'ro',
  isa       => 'Maybe[CodeRef]',
);

has response_cooker => (
  is        => 'ro',
  isa       => 'Maybe[CodeRef]',
);

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

  my %object_states = map { $_ => "_$_" } qw(call shutdown respond);

  POE::Session->create(
    inline_states => {_start => sub { $poe_kernel->alias_set($self->Alias) }},
    object_states => [$self => \%object_states]
  );

  $args->{Alias} = $self->http;
  POE::Component::Client::HTTP->spawn(%$args);
}

sub spawn { my $self = shift; $self->new(@_) }

sub call {
  my ($self, $method, $url, @rest) = @_;
  my $opts = (@rest > 0 
    ? $rest[0] eq 'HASH' 
      ? $rest[0] 
      : {@rest}
    : {});
  $poe_kernel->post($self->Alias, call => $method, $url, $opts);
}

sub _call {
  my ($self, $heap, $method, $url, $opts) = 
    @_[OBJECT, HEAP, ARG0..ARG3];

  if (my $query = $opts->{query}) {
    my @pairs = map { my ($k, $v) = ($_, $query->{$_}); "$k=$v" }
                keys (%$query);

    $url = join('?', $url, join('&', @pairs));
  }

  my $request = HTTP::Request->new(
    $method, $url, $opts->{headers}, $opts->{content});

  my $cooker = exists $opts->{request_cooker} 
    ? $opts->{request_cooker}
    : $self->request_cooker;

  $request = $cooker->($request) if $cooker;

  if(my $cont = $opts->{callback}) {
    $heap->{request_info}->{$request}->{continuation} = $cont;
  }

  $heap->{request_info}->{$request}->{cooker} = 
    exists $opts->{response_cooker}
    ? $opts->{response_cooker}
    : $self->response_cooker;

  $poe_kernel->post($self->http, request => respond => $request);
}

sub _respond {
  my ($heap, $request_packet, $response_packet) = @_[HEAP, ARG0..ARG1];
  my $req = $request_packet->[0];
  my @res = ($response_packet->[0]);

  my $info = delete $heap->{request_info}->{$req};
  my $c = $info->{continuation} || return;
  my $cooker = $info->{cooker};
  @res = $cooker->(@res) if $cooker;

  if (ref $c eq 'CODE') {
    $c->(@res);
  }
  else {
    $poe_kernel->post($c->[0], $c->[1], @res);
  }
}

sub shutdown {
  $poe_kernel->post($_[0]->Alias, 'shutdown');
}

sub _shutdown {
  my $self = $_[OBJECT];
  $poe_kernel->post($self->http, 'shutdown');
  $poe_kernel->alias_remove($self->Alias);
}

1;

__END__