The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Wurm::Grub::REST - Wurm::let grub for generating RESTful services.

SYNOPSIS

  use Wurm qw(mob let);
  use Wurm::Grub::REST;
  use Data::UUID;
  use JSON::XS;
  use Tenjin;

  my $grub = Wurm::Grub::REST->new
  ->get(
    sub {
      my $meal = shift;

      my $item = $meal->mind->{meld}{$meal->grit->{id}};
      return
        unless defined $item;

      $meal->grit->{item} = $item;
      return $meal->vent->{json}
        ? to_json($meal)
        : to_html($meal, 'item.html')
      ;
    },
    sub {
      my $meal = shift;

      $meal->grit->{items} = $meal->mind->{meld};
      return $meal->vent->{json}
        ? to_json($meal)
        : to_html($meal, 'index.html')
      ;
    },
  )
  ->post(sub {
    my $meal = shift;

    my $text = $meal->req->parameters->{text};
    return
      unless defined $text;

    $meal->grit->{id} = gen_uuid($meal);
    $meal->mind->{meld}{$meal->grit->{id}} = $text;
    return Wurm::_302($meal->env->{PATH_INFO});
  })
  ->patch(sub {
    my $meal = shift;

    return
      unless exists $meal->mind->{meld}{$meal->grit->{id}};

    my $text = $meal->req->parameters->{text};
    return
      unless defined $text;

    $meal->mind->{meld}{$meal->grit->{id}} = $text;
    return Wurm::_204;
  })
  ->delete(sub {
    my $meal = shift;

    return
      unless exists $meal->mind->{meld}{$meal->grit->{id}};

    delete $meal->mind->{meld}{$meal->grit->{id}};
    return Wurm::_204;
  })
  ;

  sub gen_uuid {
    my $meal = shift;
    my $uuid = $meal->mind->{uuid};
    return lc $uuid->to_string($uuid->create);
  }

  sub to_html {
    my $meal = shift;
    my $file = shift;
    my $html = $meal->mind->{html}->render($file, $meal->grit);
    return Wurm::_200('text/html', $html);
  }

  sub to_json {
    my $meal = shift;
    my $json = $meal->mind->{json}->encode($meal->grit);
    return Wurm::_200('application/json', $json);
  }

  my $mind = {
    meld => {'Wurm::Grub::REST' => 'Hello, Wurm!'},
    uuid => Data::UUID->new,
    json => JSON::XS->new->utf8,
    html => Tenjin->new({
      path => ['./examples/html/rest'], strict => 1, cache => 0
    }),
  };

  my $app = Wurm::wrapp($grub->molt, $mind);
  $app

DESCRIPTION

Wurm::Grub::REST is a Wurm::let to help build REST-enabled services. It provides a library of light-weight handler wrappers that attempt to conform to the REST protocol specification.

METHODS

Please see the documentation for Wurm::let for a description of super-class methods. Below is a list of overriden methods:

new($accept_map)

Creates a new Wurm::let object with a gate handler installed that will inspect the HTTP_ACCEPT request variable for possible response content types. The $accept_map parameter can be used to specify a custom mapping between a mime-type and a key name that will be set in $meal->{vent}. If no map is given, a default map is installed with the following values:

  text/html        => html
  application/json => json
  text/xml         => xml
  application/xml  => xml

The purpose of this is to make response encoding decisions easier. When the HTTP Accept header is set properly, this should allow you to do something like:

  sub handler {
    my $meal = shift;

    ...

    if   ($meal->{vent}{json}) { return to_json($meal); }
    elsif($meal->{vent}{xml} ) { return to_xml ($meal); }
    elsif($meal->{vent}{html}) { return to_html($meal); }

    # no type handler installed; someone else's problem
    return;
  }

The gate handler will also modify $meal->{tube} by calling Wurm::bend() assuming that any atom returned is an application record id. This is stored in $meal->{grit}{id} for further down-stream dispatching and access to the application. From the client point-of-view, this simply looks like:

  'http://.../path/to/rest'

and

  'http://.../path/to/rest/$id'
get($one, $all)

Adds an HTTP GET body handler to implement record and collection indexing and retrieval.

If a record id is present, the $one handler is called. Otherwise the $all handler is called. Each are expected to return a response upon success. If no response is returned, an HTTP 404 is returned instead.

post($post)

Adds an HTTP POST body handler to implement record creation.

If a record id is present, an HTTP 400 is generated. Otherwise the request is dispatched to the handler in $post which may return its own response. If no response is returned an HTTP 400 will be generated if $meal->{grit}{id} is not defined. Otherwise an HTTP <201> will be generated with the Location header set to the URL of the new resource.

put($old, $new)

Adds an HTTP PUT body handler to implement record modification and creation.

If a record id is present, the $old hander is called. Otherwise the $new handler is called. Each are expected to return a response upon success. If no record id is present in $meal->{grit}{id} after calling the handler, an HTTP 400 response is returned. Otherwise a redirect to the resource URL will be generated with the code set to 201 for new records and 302 for old records.

patch($one, $all)

Adds an HTTP PATCH body handler to implement record and collection patching.

If a record id is present, the $one handler is called and is expected to generate a response upon success. An HTTP 400 will be generated if the record id is removed from $meal->{grit}{id}. An HTTP 404 is generated if the handler does not generate a response.

If no record id is present, an HTTP 404 will be returned if the handler in $all is not defined. If a handler is defined, it will be called expecting to return a response. If no response is returned an HTTP 400 is generated.

delete($delete)

Adds an HTTP DELETE body handler to implement record and collection deletion.

If a record id is present, the $one handler is called and is expected to generate a response upon success. An HTTP 400 will be generated if the record id is removed from $meal->{grit}{id}. An HTTP 404 is generated if the handler does not generate a response.

If no record id is present, an HTTP 404 will be returned if the handler in $all is not defined. If a handler is defined, it will be called expecting to return a response. If no response is returned an HTTP 400 is generated.

SEE ALSO

Wurm
Wurm::let

AUTHOR

jason hord <pravus@cpan.org>

LICENSE

This software is information. It is subject only to local laws of physics.