Dancer-Plugin-REST-0.07/0000755000175000017500000000000011654066762014236 5ustar sukriasukriaDancer-Plugin-REST-0.07/MANIFEST0000644000175000017500000000052311654066762015367 0ustar sukriasukriaCHANGES lib/Dancer/Plugin/REST.pm Makefile.PL MANIFEST This list of files README t/01_base.t t/02_prepare_serializer_for_format.t t/03_resource.t t/04_plugin_settings.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) Dancer-Plugin-REST-0.07/Makefile.PL0000644000175000017500000000311111654065722016177 0ustar sukriasukriause ExtUtils::MakeMaker; WriteMakefile1( NAME => 'Dancer::Plugin::REST', ABSTRACT => "REST plugin for Dancer", LICENSE => 'perl', VERSION_FROM => 'lib/Dancer/Plugin/REST.pm', META_MERGE => { resources => { repository => 'http://github.com/sukria/Dancer-Plugin-REST', }, }, BUILD_REQUIRES => { 'Test::More' => '0.87', }, PREREQ_PM => { 'Dancer' => '1.3014', 'JSON' => 0, }, test => {TESTS => join( ' ', (glob( 't/*.t'), glob('t/*/*.t')))}, ); sub WriteMakefile1 { #Written by Alexandr Ciornii, version 0.21. Added by eumm-upgrade. my %params=@_; my $eumm_version=$ExtUtils::MakeMaker::VERSION; $eumm_version=eval $eumm_version; die "EXTRA_META is deprecated" if exists $params{EXTRA_META}; die "License not specified" if not exists $params{LICENSE}; if ($params{BUILD_REQUIRES} and $eumm_version < 6.5503) { #EUMM 6.5502 has problems with BUILD_REQUIRES $params{PREREQ_PM}={ %{$params{PREREQ_PM} || {}} , %{$params{BUILD_REQUIRES}} }; delete $params{BUILD_REQUIRES}; } delete $params{CONFIGURE_REQUIRES} if $eumm_version < 6.52; delete $params{MIN_PERL_VERSION} if $eumm_version < 6.48; delete $params{META_MERGE} if $eumm_version < 6.46; delete $params{META_ADD} if $eumm_version < 6.46; delete $params{LICENSE} if $eumm_version < 6.31; delete $params{AUTHOR} if $] < 5.005; delete $params{ABSTRACT_FROM} if $] < 5.005; delete $params{BINARY_LOCATION} if $] < 5.005; WriteMakefile(%params); } Dancer-Plugin-REST-0.07/t/0000755000175000017500000000000011654066762014501 5ustar sukriasukriaDancer-Plugin-REST-0.07/t/02_prepare_serializer_for_format.t0000644000175000017500000000267611654065722023302 0ustar sukriasukriause strict; use warnings; use Dancer::ModuleLoader; use Test::More import => ['!pass']; plan skip_all => "JSON is needed for this test" unless Dancer::ModuleLoader->load('JSON'); plan skip_all => "YAML is needed for this test" unless Dancer::ModuleLoader->load('YAML'); my $data = { foo => 42 }; my $json = JSON::encode_json($data); my $yaml = YAML::Dump($data); { package Webservice; use Dancer; use Dancer::Plugin::REST; setting environment => 'testing'; prepare_serializer_for_format; get '/' => sub { "root" }; get '/:something.:format' => sub { $data; }; } use Dancer::Test; my @tests = ( { request => [GET => '/'], content_type => 'text/html', response => 'root', }, { request => [GET => '/foo.json'], content_type => 'application/json', response => $json }, { request => [GET => '/foo.yml'], content_type => 'text/x-yaml', response => $yaml, }, { request => [GET => '/'], content_type => 'text/html', response => 'root', }, ); plan tests => scalar(@tests) * 2; for my $test ( @tests ) { my $response = dancer_response(@{ $test->{request} }); is($response->header('Content-Type'), $test->{content_type}, "headers have content_type set to ".$test->{content_type}); is( $response->{content}, $test->{response}, "\$data has been encoded" ); } Dancer-Plugin-REST-0.07/t/04_plugin_settings.t0000644000175000017500000000272011654066104020376 0ustar sukriasukriause strict; use warnings; use Dancer::ModuleLoader; use Test::More import => ['!pass']; plan skip_all => "JSON is needed for this test" unless Dancer::ModuleLoader->load('JSON'); plan skip_all => "YAML is needed for this test" unless Dancer::ModuleLoader->load('YAML'); my $data = { foo => 42 }; my $json = JSON::encode_json($data); my $yaml = YAML::Dump($data); { package Webservice; use Dancer; use Dancer::Plugin::REST; set environment => 'test'; prepare_serializer_for_format; get '/' => sub { "root" }; get '/:something.:format' => sub { $data; }; } use Dancer::Test; my @tests = ( { request => [GET => '/'], response => 'root', }, { request => [GET => '/foo.json'], response => $json, }, { request => [GET => '/foo.yml'], response => $yaml, }, { request => [GET => '/foo.foobar'], response => qr/unsupported format requested: foobar/ms, }, { request => [GET => '/'], response => 'root', }, ); plan tests => scalar(@tests); for my $test ( @tests ) { my $response = dancer_response(@{$test->{request}}); if (ref($test->{response})) { like( $response->{content}, $test->{response}, "response looks good for '@{$test->{request}}'" ); } else { is( $response->{content}, $test->{response}, "response looks good for '@{$test->{request}}'" ); } } Dancer-Plugin-REST-0.07/t/03_resource.t0000644000175000017500000000524711654066104017015 0ustar sukriasukriause strict; use warnings; use Dancer::ModuleLoader; use Test::More import => ['!pass']; # Dancer::Test had a bug in version previous 1.3059_01 that prevent this test # from running correctly. my $dancer_version = eval "\$Dancer::VERSION"; $dancer_version =~ s/_//g; plan skip_all => "Dancer 1.3059_01 is needed for this test (you have $dancer_version)" if $dancer_version < 1.305901; plan tests => 8; { package Webservice; use Dancer; use Dancer::Plugin::REST; use Test::More import => ['!pass']; resource user => 'get' => \&on_get_user, 'create' => \&on_create_user, 'delete' => \&on_delete_user, 'update' => \&on_update_user; my $users = {}; my $last_id = 0; sub on_get_user { my $id = params->{'id'}; { user => $users->{$id} }; } sub on_create_user { my $id = ++$last_id; my $user = params('body'); $user->{id} = $id; $users->{$id} = $user; { user => $users->{$id} }; } sub on_delete_user { my $id = params->{'id'}; my $deleted = $users->{$id}; delete $users->{$id}; { user => $deleted }; } sub on_update_user { my $id = params->{'id'}; my $user = $users->{$id}; return { user => undef } unless defined $user; $users->{$id} = { %$user, %{params('body')} }; { user => $users->{$id} }; } eval { resource failure => get => sub { 'GET' } }; like $@, qr{resource should be given with triggers}, "resource must have 4 hooks"; } use Dancer::Test; my $r = dancer_response(GET => '/user/1'); is_deeply $r->{content}, {user => undef}, "user 1 is not defined"; $r = dancer_response(POST => '/user', { body => {name => 'Alexis' }}); is_deeply $r->{content}, { user => { id => 1, name => "Alexis" } }, "create user works"; $r = dancer_response(GET => '/user/1'); is_deeply $r->{content}, {user => { id => 1, name => 'Alexis'}}, "user 1 is defined"; $r = dancer_response(PUT => '/user/1', { body => { nick => 'sukria', name => 'Alexis Sukrieh' } }); is_deeply $r->{content}, {user => { id => 1, name => 'Alexis Sukrieh', nick => 'sukria'}}, "user 1 is updated"; $r = dancer_response(DELETE => '/user/1'); is_deeply $r->{content}, {user => { id => 1, name => 'Alexis Sukrieh', nick => 'sukria'}}, "user 1 is deleted"; $r = dancer_response(GET => '/user/1'); is_deeply $r->{content}, {user => undef}, "user 1 is not defined"; $r = dancer_response(POST => '/user', { body => { name => 'Franck Cuny' } }); is_deeply $r->{content}, { user => { id => 2, name => "Franck Cuny" } }, "id is correctly increased"; Dancer-Plugin-REST-0.07/t/01_base.t0000644000175000017500000000007311654065722016073 0ustar sukriasukriause Test::More tests => 1; use_ok 'Dancer::Plugin::REST'; Dancer-Plugin-REST-0.07/README0000644000175000017500000000013611654065722015111 0ustar sukriasukriaDancer::Plugin::REST A Dancer plugin to transform your Dancer app in a RESTful webservice. Dancer-Plugin-REST-0.07/META.json0000644000175000017500000000173311654066762015663 0ustar sukriasukria{ "abstract" : "REST plugin for Dancer", "author" : [ "unknown" ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 6.59, CPAN::Meta::Converter version 2.112150", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Dancer-Plugin-REST", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "Test::More" : "0.87" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : 0 } }, "runtime" : { "requires" : { "Dancer" : "1.3014", "JSON" : 0 } } }, "release_status" : "stable", "resources" : { "repository" : { "url" : "http://github.com/sukria/Dancer-Plugin-REST" } }, "version" : "0.07" } Dancer-Plugin-REST-0.07/CHANGES0000644000175000017500000000163711654066732015235 0ustar sukriasukria0.07 2011-11-01 * Minor updates to fix warnings with Dancer 1.3080 * Fix POD (Alexis Sukrieh) 0.06 2011-05-28 [ TEST SUITE ] * Skip test t/03_resource.t if Dancer is not >= 1.3059_01 A bug in Dancer::Test in previous versions prevent the test from passing. 0.05 2011-03-04 [ BUG FIXES ] * Fix the test suite with dancer_response * Fix dependencies: Dancer 1.3014 Version 0.04 [ Alexis Sukrieh ] * Add all HTTP response status Version 0.03 [ Franck Cuny ] * New keywords for ReST response status (status_ok, status_created, ...) Version 0.02 * 100% coverage * fix a bug in 'resource' (order of route hander definition) Version 0.0001_01 * Support for prepare_serializer_for_format keyword autoamtically set a serializer according to params->{format} * Support for resource definition When a resource is defined a set of routes are defined in a RESTful manner. Dancer-Plugin-REST-0.07/META.yml0000644000175000017500000000102711654066762015507 0ustar sukriasukria--- abstract: 'REST plugin for Dancer' author: - unknown build_requires: Test::More: 0.87 configure_requires: ExtUtils::MakeMaker: 0 dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 6.59, CPAN::Meta::Converter version 2.112150' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Dancer-Plugin-REST no_index: directory: - t - inc requires: Dancer: 1.3014 JSON: 0 resources: repository: http://github.com/sukria/Dancer-Plugin-REST version: 0.07 Dancer-Plugin-REST-0.07/lib/0000755000175000017500000000000011654066762015004 5ustar sukriasukriaDancer-Plugin-REST-0.07/lib/Dancer/0000755000175000017500000000000011654066762016200 5ustar sukriasukriaDancer-Plugin-REST-0.07/lib/Dancer/Plugin/0000755000175000017500000000000011654066762017436 5ustar sukriasukriaDancer-Plugin-REST-0.07/lib/Dancer/Plugin/REST.pm0000644000175000017500000001527211654066732020555 0ustar sukriasukriapackage Dancer::Plugin::REST; use strict; use warnings; use Carp 'croak'; use Dancer ':syntax'; use Dancer::Plugin; our $AUTHORITY = 'SUKRIA'; our $VERSION = '0.07'; my $content_types = { json => 'application/json', yml => 'text/x-yaml', xml => 'application/xml', }; register prepare_serializer_for_format => sub { my $conf = plugin_setting; my $serializers = ( ($conf && exists $conf->{serializers}) ? $conf->{serializers} : { 'json' => 'JSON', 'yml' => 'YAML', 'xml' => 'XML', 'dump' => 'Dumper', } ); hook 'before' => sub { my $format = params->{'format'}; return unless defined $format; my $serializer = $serializers->{$format}; unless (defined $serializer) { return halt( Dancer::Error->new( code => 404, message => "unsupported format requested: " . $format ) ); } set serializer => $serializer; my $ct = $content_types->{$format} || setting('content_type'); content_type $ct; }; }; register resource => sub { my ($resource, %triggers) = @_; croak "resource should be given with triggers" unless defined $resource and defined $triggers{get} and defined $triggers{update} and defined $triggers{delete} and defined $triggers{create}; get "/${resource}/:id.:format" => $triggers{get}; get "/${resource}/:id" => $triggers{get}; put "/${resource}/:id.:format" => $triggers{update}; put "/${resource}/:id" => $triggers{update}; post "/${resource}.:format" => $triggers{create}; post "/${resource}" => $triggers{create}; del "/${resource}/:id.:format" => $triggers{delete}; del "/${resource}/:id" => $triggers{delete}; }; register send_entity => sub { my ($entity, $http_code) = @_; $http_code ||= 200; status($http_code); $entity; }; my %http_codes = ( # 1xx 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', # 2xx 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', 210 => 'Content Different', # 3xx 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', 310 => 'Too many Redirect', # 4xx 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested range unsatisfiable', 417 => 'Expectation failed', 418 => 'Teapot', 422 => 'Unprocessable entity', 423 => 'Locked', 424 => 'Method failure', 425 => 'Unordered Collection', 426 => 'Upgrade Required', 449 => 'Retry With', 450 => 'Parental Controls', # 5xx 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported', 507 => 'Insufficient storage', 509 => 'Bandwidth Limit Exceeded', ); for my $code (keys %http_codes) { my $helper_name = lc($http_codes{$code}); $helper_name =~ s/[^\w]+/_/gms; $helper_name = "status_${helper_name}"; register $helper_name => sub { if ($code >= 400) { send_entity({error => $_[0]}, $code); } else { send_entity($_[0], $code); } }; } register_plugin; 1; __END__ =pod =head1 NAME Dancer::Plugin::REST - A plugin for writing RESTful apps with Dancer =head1 SYNOPSYS package MyWebService; use Dancer; use Dancer::Plugin::REST; prepare_serializer_for_format; get '/user/:id.:format' => sub { User->find(params->{id}); }; # curl http://mywebservice/user/42.json { "id": 42, "name": "John Foo", email: "john.foo@example.com"} # curl http://mywebservice/user/42.yml -- id: 42 name: "John Foo" email: "john.foo@example.com" =head1 DESCRIPTION This plugin helps you write a RESTful webservice with Dancer. =head1 KEYWORDS =head2 prepare_serializer_for_format When this pragma is used, a before filter is set by the plugin to automatically change the serializer when a format is detected in the URI. That means that each route you define with a B<:format> token will trigger a serializer definition, if the format is known. This lets you define all the REST actions you like as regular Dancer route handlers, without explicitly handling the outgoing data format. =head2 resource This keyword lets you declare a resource your application will handle. resource user => get => sub { # return user where id = params->{id} }, create => sub { # create a new user with params->{user} }, delete => sub { # delete user where id = params->{id} }, update => sub { # update user with params->{user} }; # this defines the following routes: # GET /user/:id # GET /user/:id.:format # POST /user # POST /user.:format # DELETE /user/:id # DELETE /user/:id.:format # PUT /user/:id # PUT /user/:id.:format =head2 helpers Some helpers are available. This helper will set an appropriate HTTP status for you. =head3 status_ok status_ok({users => {...}}); Set the HTTP status to 200 =head3 status_created status_created({users => {...}}); Set the HTTP status to 201 =head3 status_accepted status_accepted({users => {...}}); Set the HTTP status to 202 =head3 status_bad_request status_bad_request("user foo can't be found"); Set the HTTP status to 400. This function as for argument a scalar that will be used under the key B. =head3 status_not_found status_not_found("users doesn't exists"); Set the HTTP status to 404. This function as for argument a scalar that will be used under the key B. =head1 LICENCE This module is released under the same terms as Perl itself. =head1 AUTHORS This module has been written by Alexis Sukrieh C<< >> and Franck Cuny. =head1 SEE ALSO L L =cut