[Catalyst-commits] r14382 - in
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk:
lib/CatalystX/CRUD/Controller t t/lib/MyApp/Controller
t/lib/MyApp/Controller/REST
karpet at dev.catalyst.perl.org
karpet at dev.catalyst.perl.org
Mon Nov 5 03:05:32 GMT 2012
Author: karpet
Date: 2012-11-05 03:05:32 +0000 (Mon, 05 Nov 2012)
New Revision: 14382
Removed:
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/boilerplate.t
Modified:
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/lib/CatalystX/CRUD/Controller/REST.pm
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/001-file.t
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/REST/File.pm
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/Root.pm
Log:
add POD; all tests passing
Modified: CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/lib/CatalystX/CRUD/Controller/REST.pm
===================================================================
--- CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/lib/CatalystX/CRUD/Controller/REST.pm 2012-11-04 03:43:38 UTC (rev 14381)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/lib/CatalystX/CRUD/Controller/REST.pm 2012-11-05 03:05:32 UTC (rev 14382)
@@ -23,6 +23,8 @@
with 'CatalystX::CRUD::ControllerRole';
+use CatalystX::CRUD::Results;
+
=head1 NAME
CatalystX::CRUD::Controller::REST - Catalyst::Controller::REST with CRUD
@@ -54,7 +56,7 @@
# POST /foo/<pk>/bar -> create 'bar' object related to 'foo' (idempotent)
# PUT /foo/<pk>/bar/<pk2> -> create relationship between 'foo' and 'bar'
# DELETE /foo/<pk>/bar/<pk2> -> sever 'bar' object relationship to 'foo'
- # POST /foo/<pk>/bar/<pk2> -> update 'bar' object related to 'foo'
+ # PUT /foo/<pk>/bar/<pk2> -> create/update 'bar' object related to 'foo'
=head1 DESCRIPTION
@@ -80,12 +82,23 @@
##############################################################
# Local actions
+=head2 search_objects
+
+Registers URL space for B<search>.
+
+=cut
+
sub search_objects : Path('search') : Args(0) : ActionClass('REST') { }
+=head2 search_objects_GET
+
+Query the model and return results. See do_search().
+
+=cut
+
sub search_objects_GET {
my ( $self, $c ) = @_;
- $c->log->debug('search_GET');
- $self->search($c);
+ $self->do_search($c);
if ( !blessed( $c->stash->{results} ) ) {
$self->status_bad_request( $c,
message => 'Must provide search parameters' );
@@ -95,12 +108,26 @@
}
}
+=head2 count_objects
+
+Registers URl space for B<count>.
+
+=cut
+
sub count_objects : Path('count') : Args(0) : ActionClass('REST') { }
+=head2 count_objects_GET
+
+Like search_objects_GET() but does not set result values, only a total count.
+Useful for AJAX-y types of situations where you want to query for a total
+number of matches and create a pager but not actually retrieve any data.
+
+=cut
+
sub count_objects_GET {
my ( $self, $c ) = @_;
- $c->log->debug('count_GET');
- $self->count($c);
+ $c->stash( fetch_no_results => 1 ); # optimize a little
+ $self->do_search($c);
if ( !blessed( $c->stash->{results} ) ) {
$self->status_bad_request( $c,
message => 'Must provide search parameters' );
@@ -113,17 +140,34 @@
##############################################################
# REST actions
-# base method for /
+=head2 zero_args
+
+Registers URL space for 0 path arguments.
+
+=cut
+
sub zero_args : Path('') : Args(0) : ActionClass('REST') { }
-# list objects
+=head2 zero_args_GET( I<ctx> )
+
+GET /foo -> list objects of type foo.
+
+Calls do_search().
+
+=cut
+
sub zero_args_GET {
my ( $self, $c ) = @_;
- $self->list($c);
+ $self->do_search($c);
$self->status_ok( $c, entity => $c->stash->{results}->serialize );
}
-# create object
+=head2 zero_args_POST( I<ctx> )
+
+POST /foo -> create object of type foo.
+
+=cut
+
sub zero_args_POST {
my ( $self, $c ) = @_;
@@ -147,27 +191,41 @@
}
}
-# base method for /<pk>
-sub one_arg : Path('') : Args(1) : ActionClass('REST') {
+=head2 one_arg
+
+Registers URL space for 1 path argument.
+
+=cut
+
+sub one_arg : Path('') : Args(1) : ActionClass('REST::ForBrowsers') {
my ( $self, $c, $id ) = @_;
$self->fetch( $c, $id );
}
+=head2 one_arg_GET( I<ctx>, I<pk> )
+
+GET /foo/<pk> -> retrieve object for I<pk>.
+
+=cut
+
sub one_arg_GET {
my ( $self, $c, $id ) = @_;
-
- # rely on one_arg() to handle errors to this point.
- if ( $self->has_errors($c) ) {
- $c->clear_errors;
- return if $c->response->status;
- }
-
+ return if $c->stash->{fetch_failed};
+ return if $c->stash->{object}->is_new; # 404
$self->status_ok( $c, entity => $c->stash->{object}->serialize );
}
-# create or update object (idempotent)
+=head2 one_arg_PUT( I<ctx>, I<pk> )
+
+PUT /foo/<pk> -> create or update the object for I<pk>.
+
+This method must be idempotent. POST is not.
+
+=cut
+
sub one_arg_PUT {
my ( $self, $c, $id ) = @_;
+ return if $c->stash->{fetch_failed};
# remember if we're creating or updating
my $obj_is_new = $c->stash->{object}->is_new;
@@ -197,16 +255,16 @@
}
}
-# delete object
+=head2 one_arg_DELETE( I<ctx>, I<pk> )
+
+DELETE /foo/<pk> -> delete the object for I<pk>.
+
+=cut
+
sub one_arg_DELETE {
my ( $self, $c, $id ) = @_;
+ return if $c->stash->{fetch_failed};
- # rely on one_arg() to handle errors to this point.
- if ( $self->has_errors($c) ) {
- $c->clear_errors;
- return if $c->response->status;
- }
-
unless ( $self->can_write($c) ) {
$self->status_forbidden( $c, message => 'Permission denied' );
return;
@@ -221,44 +279,64 @@
}
}
-# related to /<pk>
-sub two_args : Path('') : Args(2) : ActionClass('REST') {
+=head2 two_args
+
+Registers URL space for 2 path arguments.
+
+=cut
+
+sub two_args : Path('') : Args(2) : ActionClass('REST::ForBrowsers') {
my ( $self, $c, $id, $rel ) = @_;
+ $c->stash( rel_name => $rel );
$self->fetch( $c, $id );
- $c->stash( rel_name => $rel );
}
-# list /<pk>/<rel>
+=head2 two_args_GET( I<ctx>, I<pk>, I<rel> )
+
+GET /foo/<pk>/bar -> a list of objects of type bar related to foo.
+
+=cut
+
sub two_args_GET {
my ( $self, $c, $id, $rel ) = @_;
-
- # rely on two_args() to handle errors to this point.
+ return if $c->stash->{fetch_failed};
+ my $results
+ = $self->do_model( $c, 'iterator_related', $c->stash->{object},
+ $rel, );
if ( $self->has_errors($c) ) {
+ my $err = $c->error->[0];
+ if ( $err =~ m/^(unsupported relationship name: (\S+))/i ) {
+ $self->status_not_found( $c, message => $1 );
+ }
+ else {
+ $self->status_bad_request( $c, message => $err );
+ }
$c->clear_errors;
- return if $c->response->status;
}
-
- my $results
- = $self->do_model( $c, 'iterator_related', $c->stash->{object},
- $rel, );
- $self->status_ok( $c, entity => $results->serialize );
+ else {
+ $self->status_ok( $c, entity => $results->serialize );
+ }
}
-# create /<pk>/<rel>
+=head2 two_args_POST( I<ctx>, I<pk>, I<rel> )
+
+POST /foo/<pk>/bar -> create relationship between foo and bar.
+
+B<TODO> This method calls a not-yet-implemented create_related()
+action in the CXC::Model.
+
+=cut
+
sub two_args_POST {
my ( $self, $c, $id, $rel ) = @_;
-
- # rely on two_args() to handle errors to this point.
- if ( $self->has_errors($c) ) {
- $c->clear_errors;
- return if $c->response->status;
- }
-
+ return if $c->stash->{fetch_failed};
unless ( $self->can_write($c) ) {
$self->status_forbidden( $c, message => 'Permission denied' );
return;
}
+ $self->throw_error('TODO');
+
my $rel_obj
= $self->do_model( $c, 'create_related', $c->stash->{object}, $rel, );
if ($rel_obj) {
@@ -277,24 +355,28 @@
}
-# actions on <rel> related to /<pk>
-sub three_args : Path('') : Args(3) : ActionClass('REST') {
+=head2 three_args
+
+Registers the URL space for 3 path arguments.
+
+=cut
+
+sub three_args : Path('') : Args(3) : ActionClass('REST::ForBrowsers') {
my ( $self, $c, $id, $rel, $rel_id ) = @_;
- $self->fetch( $c, $id );
$c->stash( rel_name => $rel );
$c->stash( foreign_pk_value => $rel_id );
+ $self->fetch( $c, $id );
}
-# /<pk>/<re>/<pk2>
+=head2 three_args_GET( I<ctx>, I<pk>, I<rel>, I<rel_id> )
+
+GET /foo/<pk>/<re>/<pk2>
+
+=cut
+
sub three_args_GET {
my ( $self, $c, $id, $rel, $rel_id ) = @_;
-
- # rely on three_args() to handle errors to this point.
- if ( $self->has_errors($c) ) {
- $c->clear_errors;
- return if $c->response->status;
- }
-
+ return if $c->stash->{fetch_failed};
my $result = $self->do_model( $c, 'find_related', $c->stash->{object},
$rel, $rel_id, );
if ( !$result ) {
@@ -306,27 +388,22 @@
}
}
-# DELETE /foo/<pk>/bar/<pk2> -> sever 'bar' object relationship to 'foo'
+=head2 three_args_DELETE( I<ctx>, I<pk>, I<rel>, I<rel_pk> )
+DELETE /foo/<pk>/bar/<pk2> -> sever 'bar' object relationship to 'foo'
+
+=cut
+
sub three_args_DELETE {
my ( $self, $c, $id, $rel, $rel_id ) = @_;
-
- # rely on three_args() to handle errors to this point.
- if ( $self->has_errors($c) ) {
- $c->clear_errors;
- return if $c->response->status;
- }
-
+ return if $c->stash->{fetch_failed};
unless ( $self->can_write($c) ) {
$self->status_forbidden( $c, message => 'Permission denied' );
return;
}
- my $rt = $self->do_model(
- $c, 'rm_related', $c->stash->{object},
- $c->stash->{rel_name},
- $c->stash->{foreign_pk_value}
- );
+ my $rt = $self->do_model( $c, 'rm_related', $c->stash->{object},
+ $rel, $rel_id, );
if ($rt) {
$self->status_no_content($c);
}
@@ -337,16 +414,65 @@
}
}
-# TODO
-# POST /foo/<pk>/bar/<pk2> -> create relationship between 'foo' and 'bar'
-# PUT /foo/<pk>/bar/<pk2> -> update 'bar' object related to 'foo'
+=head2 three_args_POST( I<ctx>, I<pk>, I<rel>, I<rel_pk> )
+POST /foo/<pk>/bar/<pk2> -> create relationship between 'foo' and 'bar'
+
+=cut
+
+sub three_args_POST {
+ my ( $self, $c, $id, $rel, $rel_id ) = @_;
+ return if $c->stash->{fetch_failed};
+ my $rt = $self->do_model( $c, 'add_related', $c->stash->{object},
+ $rel, $rel_id, );
+ if ($rt) {
+ $self->status_no_content($c);
+ }
+ else {
+ # TODO msg
+ $self->status_bad_request( $c,
+ message => 'Failed to remove relationship' );
+ }
+}
+
+=head2 three_args_PUT( I<ctx>, I<pk>, I<rel>, I<rel_pk> )
+
+PUT /foo/<pk>/bar/<pk2> -> create/update 'bar' object related to 'foo'
+
+=cut
+
+sub three_args_PUT {
+ my ( $self, $c, $id, $rel, $rel_id ) = @_;
+ return if $c->stash->{fetch_failed};
+ my $rt = $self->do_model( $c, 'put_related', $c->stash->{object},
+ $rel, $rel_id, );
+
+ if ($rt) {
+ $self->status_no_content($c);
+ }
+ else {
+ # TODO msg
+ $self->status_bad_request( $c,
+ message => 'Failed to PUT relationship' );
+ }
+}
+
##########################################################
# CRUD methods
-# override base method
+=head2 save_object( I<ctx> )
+
+Calls can_write(), inflate_object(), precommit(), create_or_update_object()
+and postcommit().
+
+=cut
+
sub save_object {
my ( $self, $c ) = @_;
+ unless ( $self->can_write($c) ) {
+ $self->status_forbidden( $c, message => 'Permission denied' );
+ return;
+ }
# get a valid object
my $obj = $self->inflate_object($c);
@@ -357,7 +483,6 @@
# write our changes
unless ( $self->precommit( $c, $obj ) ) {
- $c->stash->{template} ||= $self->default_template;
return 0;
}
$self->create_or_update_object( $c, $obj );
@@ -385,6 +510,13 @@
}
}
+=head2 delete_object( I<ctx> )
+
+Checks can_write(), precommit(), and if both true,
+calls the delete() method on the B<object> in the stash().
+
+=cut
+
sub delete_object {
my ( $self, $c ) = @_;
unless ( $self->can_write($c) ) {
@@ -435,21 +567,58 @@
return $object;
}
-sub can_read {1}
+=head2 can_read( I<context> )
+
+Returns true if the current request is authorized to read() the C<object> in
+stash().
+
+Default is true.
+
+=cut
+
+sub can_read {1}
+
+=head2 can_write( I<context> )
+
+Returns true if the current request is authorized to create() or update()
+the C<object> in stash().
+
+=cut
+
sub can_write {1}
+=head2 precommit( I<context>, I<object> )
+
+Called by save_object(). If precommit() returns a false value, save_object() is aborted.
+If precommit() returns a true value, create_or_update_object() gets called.
+
+The default return is true.
+
+=cut
+
sub precommit {1}
=head2 postcommit( I<cxt>, I<obj> )
-Called internally inside save_object(). Overrides parent class
-which issues redirect on successful save_object(). Our default just returns true.
+Called internally inside save_object(). Our default just returns true.
Override this method to post-process a successful save_object() action.
=cut
sub postcommit {1}
+=head2 fetch( I<ctx>, I<pk> )
+
+Determines the correct value and field name for I<pk>
+and calls the do_model() method for C<fetch>.
+
+On success, the B<object> key will be set in stash().
+
+On failure, calls status_not_found() and sets the
+B<fetch_failed> stash() key.
+
+=cut
+
sub fetch {
my ( $self, $c, $id ) = @_;
@@ -486,7 +655,10 @@
= sprintf( "No such %s with id '%s'", $self->model_name, $id );
$self->status_not_found( $c, message => $err_msg );
$c->log->error($err_msg);
+ $c->stash( fetch_failed => 1 );
+ return 0;
}
+ return $c->stash->{object};
}
=head2 do_search( I<context>, I<arg> )
@@ -507,21 +679,11 @@
sub do_search {
my ( $self, $c, @arg ) = @_;
- $self->throw_error("TODO");
-
- # stash the form so it can be re-displayed
- # subclasses must stick-ify it in their own way.
- $c->stash->{form} ||= $self->form($c);
-
# if we have no input, just return for initial search
if ( !@arg && !$c->req->param && $c->action->name eq 'search' ) {
return;
}
- # turn flag on unless explicitly turned off
- $c->stash->{view_on_single_result} = 1
- unless exists $c->stash->{view_on_single_result};
-
my $query;
if ( $self->can('make_query') ) {
$query = $self->make_query( $c, @arg );
@@ -539,36 +701,43 @@
$results = $self->do_model( $c, 'search', $query );
}
- if ( $results
- && $count == 1
- && $c->stash->{view_on_single_result}
- && ( my $uri = $self->uri_for_view_on_single_result( $c, $results ) )
- )
- {
- $c->log->debug("redirect for single_result") if $c->debug;
- $c->response->redirect($uri);
+ my $pager;
+ if ( $count && $self->model_can( $c, 'make_pager' ) ) {
+ $pager = $self->do_model( $c, 'make_pager', $count, $results );
}
- else {
- my $pager;
- if ( $count && $self->model_can( $c, 'make_pager' ) ) {
- $pager = $self->do_model( $c, 'make_pager', $count, $results );
+ $c->stash->{results}
+ = $self->naked_results
+ ? $results
+ : CatalystX::CRUD::Results->new(
+ { count => $count,
+ pager => $pager,
+ results => $results,
+ query => $query,
}
+ );
- $c->stash->{results}
- = $self->naked_results
- ? $results
- : CatalystX::CRUD::Results->new(
- { count => $count,
- pager => $pager,
- results => $results,
- query => $query,
- }
- );
+}
+
+=head2 do_model( I<ctx>, I<args> )
+
+Wrapper around the ControllerRole method of the same name.
+The wrapper does an eval and sets the I<ctx> error param
+with $@.
+
+=cut
+
+around 'do_model' => sub {
+ my ( $orig, $self, $c, @args ) = @_;
+ my $results;
+ eval { $results = $self->$orig( $c, @args ); };
+ if ($@) {
+ $c->error($@); # re-throw
+ return $results;
}
+ return $results;
+};
-}
-
1;
__END__
Modified: CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/001-file.t
===================================================================
--- CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/001-file.t 2012-11-04 03:43:38 UTC (rev 14381)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/001-file.t 2012-11-05 03:05:32 UTC (rev 14382)
@@ -1,6 +1,6 @@
#!/usr/bin/env perl
-use Test::More tests => 54;
+use Test::More tests => 43;
use strict;
use lib qw( lib t/lib );
use_ok('CatalystX::CRUD::Model::File');
@@ -12,11 +12,15 @@
use JSON;
####################################################
-# do CRUD stuff
+# basic CRUD
my $res;
-# create
+# confirm testfile is not there
+ok( $res = request( GET('/rest/file/testfile') ), "GET testfile" );
+is( $res->code, 404, "no testfile at start" );
+
+# create testfile
ok( $res = request(
PUT('/rest/file/testfile',
Content => encode_json( { content => 'hello world' } )
@@ -31,42 +35,50 @@
"PUT new file response"
);
-####################################################
# read the file we just created
ok( $res = request( HTTP::Request->new( GET => '/rest/file/testfile' ) ),
"GET new file" );
#diag( $res->content );
-like( $res->content, qr/content => "hello world"/, "read file" );
+is_deeply(
+ decode_json( $res->content ),
+ { content => "hello world", file => "testfile" },
+ "GET file response"
+);
-####################################################
# update the file
ok( $res = request(
- POST( '/rest/file/testfile', [ content => 'foo bar baz' ] )
+ PUT('/rest/file/testfile',
+ Content => encode_json( { content => 'foo bar baz' } )
+ )
),
"update file"
);
-like( $res->content, qr/content => "foo bar baz"/, "update file" );
+is_deeply(
+ decode_json( $res->content ),
+ { content => "foo bar baz", file => "testfile" },
+ "PUT file update response"
+);
####################################################
-# create related file
+# create another new file
ok( $res = request(
- POST(
- '/rest/file/otherdir%2ftestfile2',
- [ content => 'hello world 2' ]
+ PUT('/rest/file/otherdir%2ftestfile2',
+ Content => encode_json( { content => 'hello world 2' } )
)
),
- "POST new file2"
+ "PUT new file2"
);
-is( $res->content,
- '{ content => "hello world 2", file => "otherdir/testfile2" }',
- "POST new file2 response"
+is_deeply(
+ decode_json( $res->content ),
+ { content => "hello world 2", file => "otherdir/testfile2" },
+ "PUT new file2 response"
);
-is( $res->code, 302, "new file 302 redirect status" );
+is( $res->code, 201, "new file 201 status" );
###################################################
# test with no args
@@ -75,34 +87,80 @@
ok( $res = request('/rest/file'), "/ request with multiple items" );
is( $res->code, 200, "/ request with multiple items lists" );
-ok( $res->content =~ qr/foo bar baz/ && $res->content =~ qr/hello world/,
- "content has 2 files" );
+#diag( dump( decode_json( $res->content ) ) );
+is_deeply(
+ decode_json( $res->content ),
+ { count => 2,
+ query => 1,
+ results => [
+ { content => "foo bar baz", file => "./testfile" },
+ { content => "hello world 2", file => "otherdir/testfile2" },
+ ],
+ },
+ "content has 2 files"
+);
+
###################################################
-# test the Arg matching with no rpc
+# test dispatching
-ok( $res = request('/rest/file/create'), "/rest/file/create" );
-is( $res->code, 302, "/rest/file/create" );
ok( $res = request('/rest/file'), "zero" );
is( $res->code, 200, "zero => list()" );
ok( $res = request('/rest/file/testfile'), "one" );
is( $res->code, 200, "oid == one" );
ok( $res = request('/rest/file/testfile/view'), "view" );
is( $res->code, 404, "rpc == two" );
-ok( $res
- = request(
- POST( '/rest/file/testfile/dir/otherdir%2ftestfile2', [] ) ),
- "three"
-);
+
+######################################################
+# relate 2 files together
+
+# create relationship between testfile and testfile2
+ok( $res = request( PUT('/rest/file/testfile/dir/otherdir%2ftestfile2') ),
+ "three" );
is( $res->code, 204, "related == three" );
-ok( $res = request(
- POST( '/rest/file/testfile/dir/otherdir%2ftestfile2/rpc', [] )
- ),
- "four"
+
+# more test routing
+ok( $res = request( PUT('/rest/file/testfile/dir/otherdir%2ftestfile2/rpc') ),
+ "four" );
+is( $res->code, 404, "404 4 is too many args" );
+ok( $res = request('/rest/file/testfile/two/three/four/five'), "five" );
+is( $res->code, 404, "404 5 is too many args" );
+
+########################################################
+# non-CRUD actions: search and count
+ok( $res = request( GET('/rest/file/search?file=testfile') ),
+ "/search?file=testfile" );
+
+#diag( dump decode_json( $res->content ) );
+is_deeply(
+ decode_json( $res->content ),
+ { count => 3,
+ query => 1,
+ results => [
+ { content => "foo bar baz", file => "./testfile" },
+ { content => "foo bar baz", file => "./testfile2" },
+ { content => "hello world 2", file => "otherdir/testfile2" },
+ ],
+ },
+ "search gets 3 results"
);
-is( $res->code, 404, "404 == related rpc with no enable_rpc_compat" );
-ok( $res = request('/rest/file/testfile/two/three/four/five'), "five" );
-is( $res->code, 404, "404 == five" );
+
+ok( $res = request( GET('/rest/file/count') ), "GET /count" );
+
+#diag( dump decode_json( $res->content ) );
+is_deeply(
+ decode_json( $res->content ),
+ { count => 3,
+ query => 1,
+ results => [],
+ },
+ "count gets 3 results"
+);
+
+########################################################
+# test "browser-like" behavior tunneling through POST
+
+# delete relationship between testfile and testfile2
ok( $res = request(
POST(
'/rest/file/testfile/dir/otherdir%2ftestfile2',
@@ -111,43 +169,40 @@
),
"three"
);
-is( $res->code, 204, "related == three" );
+is( $res->code, 204, "tunneled DELETE related == three" );
-# delete the file
-
+# delete testfile
ok( $res = request(
- POST( '/rest/file/testfile', [ _http_method => 'DELETE' ] )
+ POST( '/rest/file/testfile', [ 'x-tunneled-method' => 'DELETE' ] )
),
"rm file"
);
ok( $res = request(
- POST( '/rest/file/testfile2/delete', [ _http_method => 'DELETE' ] )
- ),
- "rm file2"
-);
-
-ok( $res = request(
POST(
- '/rest/file/otherdir%2ftestfile2/delete',
- [ _http_method => 'DELETE' ]
+ '/rest/file/otherdir%2ftestfile2',
+ [ 'x-tunneled-method' => 'DELETE' ]
)
),
"rm otherdir/testfile2"
);
+is( $res->code, 204, "tunneled DELETE otherdir/testfile2" );
#diag( $res->content );
-# confirm it is gone
-ok( $res = request( HTTP::Request->new( GET => '/rest/file/testfile' ) ),
+# confirm testfile is gone
+ok( $res = request( GET('/rest/file/testfile') ),
"confirm we nuked the file" );
-#diag( $res->content );
+#diag( dump $res->content );
+is( $res->code, 404, "testfile is gone" );
-like( $res->content, qr/content => undef/, "file nuked" );
-
ok( $res = request('/rest/file'), "/ request with no items" );
+is( $res->code, 200, "/ request with no items == 200" );
-#dump $res;
-is( $res->code, 200, "/ request with no items == 200" );
-is( $res->content, "", "no content for no results" );
+#diag( dump decode_json( $res->content ) );
+is_deeply(
+ decode_json( $res->content ),
+ { count => 0, query => 1, results => [], },
+ "no content for no results"
+);
Deleted: CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/boilerplate.t
===================================================================
--- CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/boilerplate.t 2012-11-04 03:43:38 UTC (rev 14381)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/boilerplate.t 2012-11-05 03:05:32 UTC (rev 14382)
@@ -1,55 +0,0 @@
-#!perl -T
-
-use strict;
-use warnings;
-use Test::More tests => 3;
-
-sub not_in_file_ok {
- my ($filename, %regex) = @_;
- open( my $fh, '<', $filename )
- or die "couldn't open $filename for reading: $!";
-
- my %violated;
-
- while (my $line = <$fh>) {
- while (my ($desc, $regex) = each %regex) {
- if ($line =~ $regex) {
- push @{$violated{$desc}||=[]}, $.;
- }
- }
- }
-
- if (%violated) {
- fail("$filename contains boilerplate text");
- diag "$_ appears on lines @{$violated{$_}}" for keys %violated;
- } else {
- pass("$filename contains no boilerplate text");
- }
-}
-
-sub module_boilerplate_ok {
- my ($module) = @_;
- not_in_file_ok($module =>
- 'the great new $MODULENAME' => qr/ - The great new /,
- 'boilerplate description' => qr/Quick summary of what the module/,
- 'stub function definition' => qr/function[12]/,
- );
-}
-
-TODO: {
- local $TODO = "Need to replace the boilerplate text";
-
- not_in_file_ok(README =>
- "The README is used..." => qr/The README is used/,
- "'version information here'" => qr/to provide version information/,
- );
-
- not_in_file_ok(Changes =>
- "placeholder date/time" => qr(Date/time)
- );
-
- module_boilerplate_ok('lib/CatalystX/CRUD/Controller/REST.pm');
-
-
-}
-
Modified: CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/REST/File.pm
===================================================================
--- CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/REST/File.pm 2012-11-04 03:43:38 UTC (rev 14381)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/REST/File.pm 2012-11-05 03:05:32 UTC (rev 14382)
@@ -20,24 +20,21 @@
sub fetch {
my ( $self, $c, $id ) = @_;
- eval { $self->next::method( $c, $id ); };
- if ( $self->has_errors($c) or $c->res->status == 404 ) {
+ my $rt = $self->next::method( $c, $id );
- my $err = $c->error->[0] || 'No such File';
- if ( $err =~ m/^No such File/ ) {
- my $file = $self->do_model( $c, 'new_object', file => $id );
- $file = $self->do_model( $c, 'prep_new_object', $file );
- $c->log->debug("empty file object:$file") if $c->debug;
- $c->stash( object => $file );
- }
- else {
- # re-throw
- $self->throw_error($err);
- }
+ # File model requires an object to work on
+ # regardless of whether we fetched one.
+ if ( !$rt and $rt == 0 ) {
+ my $file = $self->do_model( $c, 'new_object', file => $id );
+ $file = $self->do_model( $c, 'prep_new_object', $file );
+ $c->log->debug("empty file object:$file") if $c->debug;
+ $c->stash( object => $file );
+ delete $c->stash->{fetch_failed};
}
# clean up at end
MyApp::Controller::Root->push_temp_files( $c->stash->{object} );
+ return $rt;
}
sub do_search {
Modified: CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/Root.pm
===================================================================
--- CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/Root.pm 2012-11-04 03:43:38 UTC (rev 14381)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/Root.pm 2012-11-05 03:05:32 UTC (rev 14382)
@@ -28,6 +28,9 @@
END {
for my $f (@temp_files) {
+ if ( defined -s $f ) {
+ warn "exists > $f\n";
+ }
warn "unlinking $f\n" if $ENV{CATALYST_DEBUG};
$f->remove;
}
More information about the Catalyst-commits
mailing list