The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

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.