[Catalyst-commits] r14381 - in
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk: .
lib/CatalystX/CRUD/Controller t t/lib/MyApp/Controller
t/lib/MyApp/Controller/REST t/lib/MyApp/root
karpet at dev.catalyst.perl.org
karpet at dev.catalyst.perl.org
Sun Nov 4 03:43:38 GMT 2012
Author: karpet
Date: 2012-11-04 03:43:38 +0000 (Sun, 04 Nov 2012)
New Revision: 14381
Removed:
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/root/favicon.ico
Modified:
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/Changes
CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/Makefile.PL
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:
complete re-think based on refactoring of CatalystX::CRUD 0.54
Modified: CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/Changes
===================================================================
--- CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/Changes 2012-11-04 03:11:29 UTC (rev 14380)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/Changes 2012-11-04 03:43:38 UTC (rev 14381)
@@ -1,5 +1,5 @@
Revision history for CatalystX-CRUD-Controller-REST
-0.01 Date/time
- First version, released on an unsuspecting world.
+0.001 xxx
+ First version, released on an unsuspecting world.
Modified: CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/Makefile.PL
===================================================================
--- CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/Makefile.PL 2012-11-04 03:11:29 UTC (rev 14380)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/Makefile.PL 2012-11-04 03:43:38 UTC (rev 14381)
@@ -10,7 +10,7 @@
all_from 'lib/CatalystX/CRUD/Controller/REST.pm';
requires 'Test::More' => 0;
-requires 'CatalystX::CRUD' => 0.53;
+requires 'CatalystX::CRUD' => 0.54;
requires 'Catalyst::Action::REST' => 0;
requires 'Scalar::Util' => 0;
requires 'JSON::XS' => 2.23;
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:11:29 UTC (rev 14380)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/lib/CatalystX/CRUD/Controller/REST.pm 2012-11-04 03:43:38 UTC (rev 14381)
@@ -5,11 +5,24 @@
use Data::Dump qw( dump );
BEGIN {
- extends 'Catalyst::Controller::REST', 'CatalystX::CRUD::Controller',;
+ extends qw( Catalyst::Controller::REST CatalystX::CRUD );
}
our $VERSION = '0.001';
+__PACKAGE__->mk_accessors(
+ qw(
+ model_adapter
+ model_name
+ model_meta
+ primary_key
+ naked_results
+ page_size
+ )
+);
+
+with 'CatalystX::CRUD::ControllerRole';
+
=head1 NAME
CatalystX::CRUD::Controller::REST - Catalyst::Controller::REST with CRUD
@@ -21,52 +34,73 @@
use namespace::autoclean;
BEGIN { extends 'CatalystX::CRUD::Controller::REST' }
+
+ __PACKAGE__->config(
+ model_name => 'Foo',
+ primary_key => 'id',
+ page_size => 50,
+ );
+
+ 1;
+
+ # now you can manage Foo objects with URIs like:
+ # POST /foo -> create new record
+ # GET /foo -> list all records
+ # PUT /foo/<pk> -> create or update record (idempotent)
+ # DELETE /foo/<pk> -> delete record
+ # GET /foo/<pk> -> view record
+ # GET /foo/<pk>/bar -> view 'bar' object(s) related to 'foo'
+ # GET /foo/<pk>/bar/<pk2> -> view 'bar' with id <pk2> related to 'foo' with <pk>
+ # 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'
=head1 DESCRIPTION
+Subclass of Catalyst::Controller::REST for use with CatalystX::CRUD.
+
+=head1 DISCLAIMERS
+
This module is B<not> to be confused with CatalystX::CRUD::REST.
This is not a drop-in replacement for existing CatalystX::CRUD::Controllers.
This module extends Catalyst::Controller::REST to work with the
-CatalystX::CRUD::Controller API.
+CatalystX::CRUD::Controller API. It is designed for web services,
+not managing CRUD actions via HTML forms.
+This is B<not> a subclass of CatalystX::CRUD::Controller.
+
=cut
=head1 METHODS
=cut
-# override all the CRUD methods to undo their attributes
-# and create *_HTTP methods instead.
+##############################################################
+# Local actions
-sub create { }
-sub read { }
-sub update { }
-sub delete { }
-sub add { }
-sub edit { }
-sub save { }
-sub view { }
-sub remove { }
-sub rm { }
-sub list_related { }
-sub view_related { }
+sub search_objects : Path('search') : Args(0) : ActionClass('REST') { }
-sub list : Path('') : Args(0) : ActionClass('REST') { }
-
-sub list_GET {
+sub search_objects_GET {
my ( $self, $c ) = @_;
- $c->log->debug('list_GET');
- $self->SUPER::list($c);
- $self->status_ok( $c, entity => $c->stash->{results}->serialize );
+ $c->log->debug('search_GET');
+ $self->search($c);
+ if ( !blessed( $c->stash->{results} ) ) {
+ $self->status_bad_request( $c,
+ message => 'Must provide search parameters' );
+ }
+ else {
+ $self->status_ok( $c, entity => $c->stash->{results}->serialize );
+ }
}
-sub search : Local : Args(0) : ActionClass('REST') { }
+sub count_objects : Path('count') : Args(0) : ActionClass('REST') { }
-sub search_GET {
+sub count_objects_GET {
my ( $self, $c ) = @_;
- $c->log->debug('search_GET');
- $self->SUPER::search($c);
+ $c->log->debug('count_GET');
+ $self->count($c);
if ( !blessed( $c->stash->{results} ) ) {
$self->status_bad_request( $c,
message => 'Must provide search parameters' );
@@ -76,6 +110,465 @@
}
}
+##############################################################
+# REST actions
+
+# base method for /
+sub zero_args : Path('') : Args(0) : ActionClass('REST') { }
+
+# list objects
+sub zero_args_GET {
+ my ( $self, $c ) = @_;
+ $self->list($c);
+ $self->status_ok( $c, entity => $c->stash->{results}->serialize );
+}
+
+# create object
+sub zero_args_POST {
+ my ( $self, $c ) = @_;
+
+ unless ( $self->can_write($c) ) {
+ $self->status_forbidden( $c, message => 'Permission denied' );
+ return;
+ }
+
+ $c->stash( object => $self->do_model( $c, 'fetch' ) );
+ if ( my $obj = $self->save_object($c) ) {
+ my $pk = $self->make_primary_key_string($obj);
+ $self->status_created(
+ $c,
+ location => $c->uri_for($pk),
+ entity => $c->stash->{object}->serialize
+ );
+ }
+ else {
+ # TODO msg
+ $self->status_bad_request( $c, message => 'Failed to create' );
+ }
+}
+
+# base method for /<pk>
+sub one_arg : Path('') : Args(1) : ActionClass('REST') {
+ my ( $self, $c, $id ) = @_;
+ $self->fetch( $c, $id );
+}
+
+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;
+ }
+
+ $self->status_ok( $c, entity => $c->stash->{object}->serialize );
+}
+
+# create or update object (idempotent)
+sub one_arg_PUT {
+ my ( $self, $c, $id ) = @_;
+
+ # remember if we're creating or updating
+ my $obj_is_new = $c->stash->{object}->is_new;
+
+ unless ( $self->can_write($c) ) {
+ $self->status_forbidden( $c, message => 'Permission denied' );
+ return;
+ }
+
+ if ( my $obj = $self->save_object($c) ) {
+ if ( !$obj_is_new ) {
+ $self->status_ok( $c, entity => $obj->serialize );
+ }
+ else {
+ my $loc = $c->uri_for($id);
+ $c->log->debug("PUT location=$loc") if $c->debug;
+ $self->status_created(
+ $c,
+ location => $loc,
+ entity => $obj->serialize,
+ );
+ }
+ }
+ else {
+ # TODO msg
+ $self->status_bad_request( $c, message => 'Failed to update' );
+ }
+}
+
+# delete object
+sub one_arg_DELETE {
+ 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;
+ }
+
+ unless ( $self->can_write($c) ) {
+ $self->status_forbidden( $c, message => 'Permission denied' );
+ return;
+ }
+
+ if ( $self->delete_object($c) ) {
+ $self->status_no_content($c);
+ }
+ else {
+ # TODO msg
+ $self->status_bad_request( $c, message => 'Failed to delete' );
+ }
+}
+
+# related to /<pk>
+sub two_args : Path('') : Args(2) : ActionClass('REST') {
+ my ( $self, $c, $id, $rel ) = @_;
+ $self->fetch( $c, $id );
+ $c->stash( rel_name => $rel );
+}
+
+# list /<pk>/<rel>
+sub two_args_GET {
+ 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;
+ }
+
+ my $results
+ = $self->do_model( $c, 'iterator_related', $c->stash->{object},
+ $rel, );
+ $self->status_ok( $c, entity => $results->serialize );
+}
+
+# create /<pk>/<rel>
+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;
+ }
+
+ unless ( $self->can_write($c) ) {
+ $self->status_forbidden( $c, message => 'Permission denied' );
+ return;
+ }
+
+ my $rel_obj
+ = $self->do_model( $c, 'create_related', $c->stash->{object}, $rel, );
+ if ($rel_obj) {
+ my $rel_id = $self->make_primary_key_string($rel_obj);
+ $self->status_created(
+ $c,
+ location =>
+ $c->uri_for( sprintf( "%s/%s/%s", $id, $rel, $rel_id ) ),
+ entity => $rel_obj->serialize
+ );
+ }
+ else {
+ # TODO msg
+ $self->status_bad_request( $c, message => 'Failed to delete' );
+ }
+
+}
+
+# actions on <rel> related to /<pk>
+sub three_args : Path('') : Args(3) : ActionClass('REST') {
+ my ( $self, $c, $id, $rel, $rel_id ) = @_;
+ $self->fetch( $c, $id );
+ $c->stash( rel_name => $rel );
+ $c->stash( foreign_pk_value => $rel_id );
+}
+
+# /<pk>/<re>/<pk2>
+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;
+ }
+
+ my $result = $self->do_model( $c, 'find_related', $c->stash->{object},
+ $rel, $rel_id, );
+ if ( !$result ) {
+ my $err_msg = sprintf( "No such %s with id '%s'", $rel, $rel_id );
+ $self->status_not_found( $c, message => $err_msg );
+ }
+ else {
+ $self->status_ok( $c, entity => $result->serialize );
+ }
+}
+
+# DELETE /foo/<pk>/bar/<pk2> -> sever 'bar' object relationship to 'foo'
+
+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;
+ }
+
+ 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}
+ );
+ if ($rt) {
+ $self->status_no_content($c);
+ }
+ else {
+ # TODO msg
+ $self->status_bad_request( $c,
+ message => 'Failed to remove relationship' );
+ }
+}
+
+# TODO
+# POST /foo/<pk>/bar/<pk2> -> create relationship between 'foo' and 'bar'
+# PUT /foo/<pk>/bar/<pk2> -> update 'bar' object related to 'foo'
+
+##########################################################
+# CRUD methods
+
+# override base method
+sub save_object {
+ my ( $self, $c ) = @_;
+
+ # get a valid object
+ my $obj = $self->inflate_object($c);
+ if ( !$obj ) {
+ $c->log->debug("inflate_object() returned false") if $c->debug;
+ return 0;
+ }
+
+ # write our changes
+ unless ( $self->precommit( $c, $obj ) ) {
+ $c->stash->{template} ||= $self->default_template;
+ return 0;
+ }
+ $self->create_or_update_object( $c, $obj );
+ $self->postcommit( $c, $obj );
+ return $obj;
+}
+
+=head2 create_or_update_object( I<context>, I<object> )
+
+Calls the update() or create() method on the I<object> (or model_adapter()),
+picking the method based on whether C<object_id> in stash()
+evaluates true (update) or false (create).
+
+=cut
+
+sub create_or_update_object {
+ my ( $self, $c, $obj ) = @_;
+ my $method = $obj->is_new ? 'create' : 'update';
+ $c->log->debug("object->$method") if $c->debug;
+ if ( $self->model_adapter ) {
+ $self->model_adapter->$method( $c, $obj );
+ }
+ else {
+ $obj->$method;
+ }
+}
+
+sub delete_object {
+ my ( $self, $c ) = @_;
+ unless ( $self->can_write($c) ) {
+ $self->status_forbidden( $c, message => 'Permission denied' );
+ return;
+ }
+
+ my $o = $c->stash->{object};
+
+ unless ( $self->precommit( $c, $o ) ) {
+ return 0;
+ }
+ if ( $self->model_adapter ) {
+ $self->model_adapter->delete( $c, $o );
+ }
+ else {
+ $o->delete;
+ }
+ $self->postcommit( $c, $o );
+ return 1;
+}
+
+=head2 inflate_object( I<ctx> )
+
+Returns the object from stash() initialized with the request data.
+
+=cut
+
+sub inflate_object {
+ my ( $self, $c ) = @_;
+ my $object = $c->stash->{object};
+ if ( !$object ) {
+ $self->throw_error("object not set in stash");
+ }
+ my $req_data = $c->req->data;
+ if ( !$req_data ) {
+ $c->status_bad_request( $c, message => 'Missing request data' );
+ return;
+ }
+
+ # TODO other sanity checks?
+
+ for my $f ( keys %$req_data ) {
+ if ( $object->can($f) ) {
+ $object->$f( $req_data->{$f} );
+ }
+ }
+ return $object;
+}
+
+sub can_read {1}
+sub can_write {1}
+
+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.
+Override this method to post-process a successful save_object() action.
+
+=cut
+
+sub postcommit {1}
+
+sub fetch {
+ my ( $self, $c, $id ) = @_;
+
+ unless ( $self->can_read($c) ) {
+ $self->status_forbidden( $c, message => 'Permission denied' );
+ return;
+ }
+
+ $c->stash->{object_id} = $id;
+ my @pk = $self->get_primary_key( $c, $id );
+
+ # make sure all elements of the @pk pairs are not-null
+ if ( scalar(@pk) % 2 ) {
+ $self->throw_error(
+ "Odd number of elements returned from get_primary_key()");
+ }
+ my %pk_pairs = @pk;
+ my $pk_is_null;
+ for my $key ( keys %pk_pairs ) {
+ my $val = $pk_pairs{$key};
+ if ( !defined($val) or !length($val) ) {
+ $pk_is_null = $key;
+ last;
+ }
+ }
+ if ( $c->debug and defined $pk_is_null ) {
+ $c->log->debug("Null PK value for '$pk_is_null'");
+ }
+ my @arg = ( defined $pk_is_null || !$id ) ? () : (@pk);
+ $c->log->debug( "fetch: " . dump \@arg ) if $c->debug;
+ $c->stash->{object} = $self->do_model( $c, 'fetch', @arg );
+ if ( $self->has_errors($c) or !$c->stash->{object} ) {
+ my $err_msg
+ = sprintf( "No such %s with id '%s'", $self->model_name, $id );
+ $self->status_not_found( $c, message => $err_msg );
+ $c->log->error($err_msg);
+ }
+}
+
+=head2 do_search( I<context>, I<arg> )
+
+Prepare and execute a search. Called internally by list()
+and search().
+
+Results are saved in stash() under the C<results> key.
+
+If B<naked_results> is true, then results are set just as they are
+returned from search() or list() (directly from the Model).
+
+If B<naked_results> is false (default), then results is a
+CatalystX::CRUD::Results object.
+
+=cut
+
+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 );
+ }
+ elsif ( $self->model_can( $c, 'make_query' ) ) {
+ $query = $self->do_model( $c, 'make_query', @arg );
+ }
+ else {
+ $self->throw_error(
+ "neither controller nor model implement a make_query() method");
+ }
+ my $count = $self->do_model( $c, 'count', $query ) || 0;
+ my $results;
+ unless ( $c->stash->{fetch_no_results} ) {
+ $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);
+ }
+ 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,
+ }
+ );
+ }
+
+}
+
1;
__END__
@@ -119,10 +612,6 @@
=back
-
-=head1 ACKNOWLEDGEMENTS
-
-
=head1 COPYRIGHT & LICENSE
Copyright 2012 Peter Karman.
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:11:29 UTC (rev 14380)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/001-file.t 2012-11-04 03:43:38 UTC (rev 14381)
@@ -9,22 +9,26 @@
use Catalyst::Test 'MyApp';
use Data::Dump qw( dump );
use HTTP::Request::Common;
+use JSON;
-###########################################
+####################################################
# do CRUD stuff
my $res;
# create
ok( $res = request(
- POST( '/rest/file/testfile', [ content => 'hello world' ] )
+ PUT('/rest/file/testfile',
+ Content => encode_json( { content => 'hello world' } )
+ )
),
- "POST new file"
+ "PUT new file"
);
-
-is( $res->content,
- '{ content => "hello world", file => "testfile" }',
- "POST new file response"
+is( $res->code, 201, "PUT returns 201" );
+is_deeply(
+ decode_json( $res->content ),
+ { content => "hello world", file => "testfile" },
+ "PUT new file response"
);
####################################################
@@ -109,44 +113,6 @@
);
is( $res->code, 204, "related == three" );
-# turn rpc enable on and run again
-MyApp->controller('REST::File')->enable_rpc_compat(1);
-
-ok( $res = request('/rest/file/create'), "/rest/file/create" );
-is( $res->code, 302, "/rest/file/create" );
-ok( $res = request('/rest/file'), "zero with rpc" );
-is( $res->code, 200, "zero with rpc => list()" );
-ok( $res = request('/rest/file/testfile'), "one with rpc" );
-is( $res->code, 200, "oid == one with rpc" );
-ok( $res = request('/rest/file/testfile/view'), "view with rpc" );
-is( $res->code, 200, "rpc == two with rpc" );
-ok( $res = request(
- POST( '/rest/file/testfile/dir/otherdir%2ftestfile2/add', [] )
- ),
- "three with rpc"
-);
-is( $res->code, 204, "related == three with rpc" );
-ok( $res = request(
- POST( '/rest/file/testfile/dir/otherdir%2ftestfile2/rpc', [] )
- ),
- "four"
-);
-is( $res->code, 404, "404 == related rpc with enable_rpc_compat" );
-
-ok( $res = request('/rest/file/testfile/two/three/four/five'),
- "five with rpc" );
-is( $res->code, 404, "404 == five with rpc" );
-
-ok( $res = request(
- POST(
- '/rest/file/testfile/dir/otherdir%2ftestfile2/remove',
- [ 'x-tunneled-method' => 'DELETE' ]
- )
- ),
- "three with rpc"
-);
-is( $res->code, 204, "related == three with rpc" );
-
# delete the file
ok( $res = request(
@@ -183,5 +149,5 @@
ok( $res = request('/rest/file'), "/ request with no items" );
#dump $res;
-is( $res->code, 200, "/ request with no items == 200" );
-is( $res->content, "", "no content for no results" );
+is( $res->code, 200, "/ request with no items == 200" );
+is( $res->content, "", "no content for no results" );
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:11:29 UTC (rev 14380)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/REST/File.pm 2012-11-04 03:43:38 UTC (rev 14381)
@@ -2,7 +2,6 @@
use strict;
use base qw(
CatalystX::CRUD::Controller::REST
- CatalystX::CRUD::Test::Controller
);
use Carp;
use Data::Dump qw( dump );
@@ -13,14 +12,34 @@
__PACKAGE__->config(
primary_key => 'absolute',
- form_class => 'MyApp::Form',
- form_fields => [qw( file content )],
+ data_fields => [qw( file content )],
model_name => 'File',
primary_key => 'file',
- init_form => 'init_with_file',
- init_object => 'file_from_form',
+ default => 'application/json', # default response content type
);
+sub fetch {
+ my ( $self, $c, $id ) = @_;
+ eval { $self->next::method( $c, $id ); };
+ if ( $self->has_errors($c) or $c->res->status == 404 ) {
+
+ 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);
+ }
+ }
+
+ # clean up at end
+ MyApp::Controller::Root->push_temp_files( $c->stash->{object} );
+}
+
sub do_search {
my ( $self, $c, @arg ) = @_;
$self->next::method( $c, @arg );
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:11:29 UTC (rev 14380)
+++ CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/Controller/Root.pm 2012-11-04 03:43:38 UTC (rev 14381)
@@ -19,7 +19,7 @@
$c->response->status(404);
}
-my @temp_files;
+my @temp_files = ();
sub push_temp_files {
shift;
Deleted: CatalystX-CRUD/CatalystX-CRUD-Controller-REST/trunk/t/lib/MyApp/root/favicon.ico
===================================================================
(Binary files differ)
More information about the Catalyst-commits
mailing list