perltidy all classes
[catagits/Catalyst-Controller-DBIC-API.git] / lib / Catalyst / Controller / DBIC / API.pm
index 837fbea..bbfb536 100644 (file)
@@ -2,21 +2,34 @@ package Catalyst::Controller::DBIC::API;
 
 #ABSTRACT: Provides a DBIx::Class web service automagically
 use Moose;
-BEGIN { extends 'Catalyst::Controller::ActionRole'; }
+BEGIN { extends 'Catalyst::Controller'; }
 
 use CGI::Expand ();
 use DBIx::Class::ResultClass::HashRefInflator;
-use JSON::Any;
+use JSON ();
 use Test::Deep::NoTest('eq_deeply');
 use MooseX::Types::Moose(':all');
 use Moose::Util;
-use Scalar::Util('blessed', 'reftype');
+use Scalar::Util( 'blessed', 'reftype' );
 use Try::Tiny;
 use Catalyst::Controller::DBIC::API::Request;
 use namespace::autoclean;
 
-with 'Catalyst::Controller::DBIC::API::StoredResultSource';
-with 'Catalyst::Controller::DBIC::API::StaticArguments';
+has '_json' => (
+    is         => 'ro',
+    isa        => 'JSON',
+    lazy_build => 1,
+);
+
+sub _build__json {
+
+    # no ->utf8 here because the request params get decoded by Catalyst
+    return JSON->new;
+}
+
+with 'Catalyst::Controller::DBIC::API::StoredResultSource',
+    'Catalyst::Controller::DBIC::API::StaticArguments';
+
 with 'Catalyst::Controller::DBIC::API::RequestArguments' => { static => 1 };
 
 __PACKAGE__->config();
@@ -28,25 +41,31 @@ __PACKAGE__->config();
   BEGIN { extends 'Catalyst::Controller::DBIC::API::RPC' }
 
   __PACKAGE__->config
-    ( action => { setup => { PathPart => 'artist', Chained => '/api/rpc/rpc_base' } }, # define parent chain action and partpath
-      class => 'MyAppDB::Artist', # DBIC schema class
-      create_requires => ['name', 'age'], # columns required to create
-      create_allows => ['nickname'], # additional non-required columns that create allows
-      update_allows => ['name', 'age', 'nickname'], # columns that update allows
-      update_allows => ['name', 'age', 'nickname'], # columns that update allows
-      select => [qw/name age/], # columns that data returns
-      prefetch => ['cds'], # relationships that are prefetched when no prefetch param is passed
-      prefetch_allows => [ # every possible prefetch param allowed
+    ( # define parent chain action and PathPart
+      action => {
+          setup => {
+              Chained  => '/api/rpc/rpc_base',
+              PathPart => 'artist',
+          }
+      },
+      class            => 'MyAppDB::Artist',
+      resultset_class  => 'MyAppDB::ResultSet::Artist',
+      create_requires  => ['name', 'age'],
+      create_allows    => ['nickname'],
+      update_allows    => ['name', 'age', 'nickname'],
+      update_allows    => ['name', 'age', 'nickname'],
+      select           => ['name', 'age'],
+      prefetch         => ['cds'],
+      prefetch_allows  => [
           'cds',
-          qw/ cds /,
           { cds => 'tracks' },
-          { cds => [qw/ tracks /] }
+          { cds => ['tracks'] },
       ],
-      ordered_by => [qw/age/], # order of generated list
-      search_exposes => [qw/age nickname/, { cds => [qw/title year/] }], # columns that can be searched on via list
-      data_root => 'data' # defaults to "list" for backwards compatibility
-      use_json_boolean => 1, # use JSON::Any::true|false in the response instead of strings
-      return_object => 1, # makes create and update actions return the object
+      ordered_by       => ['age'],
+      search_exposes   => ['age', 'nickname', { cds => ['title', 'year'] }],
+      data_root        => 'data',
+      use_json_boolean => 1,
+      return_object    => 1,
       );
 
   # Provides the following functional endpoints:
@@ -64,19 +83,20 @@ begin is provided in the base class to setup the Catalyst Request object, by app
 
 =cut
 
-sub begin :Private
-{
-    my ($self, $c) = @_;
+sub begin : Private {
+    my ( $self, $c ) = @_;
 
-    Catalyst::Controller::DBIC::API::Request->meta->apply($c->req)
-        unless Moose::Util::does_role($c->req, 'Catalyst::Controller::DBIC::API::Request');
+    Moose::Util::ensure_all_roles( $c->req,
+        'Catalyst::Controller::DBIC::API::Request' );
 }
 
 =method_protected setup
 
  :Chained('specify.in.subclass.config') :CaptureArgs(0) :PathPart('specify.in.subclass.config')
 
-This action is the chain root of the controller. It must either be overridden or configured to provide a base pathpart to the action and also a parent action. For example, for class MyAppDB::Track you might have
+This action is the chain root of the controller. It must either be overridden or
+configured to provide a base PathPart to the action and also a parent action.
+For example, for class MyAppDB::Track you might have
 
   package MyApp::Controller::API::RPC::Track;
   use Moose;
@@ -89,7 +109,7 @@ This action is the chain root of the controller. It must either be overridden or
 
   # or
 
-  sub setup :Chained('/api/rpc_base') :CaptureArgs(0) :PathPart('track') {
+  sub setup :Chained('/api/rpc/rpc_base') :CaptureArgs(0) :PathPart('track') {
     my ($self, $c) = @_;
 
     $self->next::method($c);
@@ -99,13 +119,14 @@ This action does nothing by default.
 
 =cut
 
-sub setup :Chained('specify.in.subclass.config') :CaptureArgs(0) :PathPart('specify.in.subclass.config') {}
+sub setup : Chained('specify.in.subclass.config') : CaptureArgs(0) :
+    PathPart('specify.in.subclass.config') { }
 
 =method_protected deserialize
 
  :Chained('setup') :CaptureArgs(0) :PathPart('') :ActionClass('Deserialize')
 
-deserialize absorbs the request data and transforms it into useful bits by using CGI::Expand->expand_hash and a smattering of JSON::Any->from_json for a handful of arguments. Current only the following arguments are capable of being expressed as JSON:
+deserialize absorbs the request data and transforms it into useful bits by using CGI::Expand->expand_hash and a smattering of JSON->decode for a handful of arguments. Current only the following arguments are capable of being expressed as JSON:
 
     search_arg
     count_arg
@@ -118,70 +139,80 @@ It should be noted that arguments can used mixed modes in with some caveats. Eac
 
 =cut
 
-sub deserialize :Chained('setup') :CaptureArgs(0) :PathPart('') :ActionClass('Deserialize')
-{
-    my ($self, $c) = @_;
+sub deserialize : Chained('setup') : CaptureArgs(0) : PathPart('') :
+    ActionClass('Deserialize') {
+    my ( $self, $c ) = @_;
     my $req_params;
 
-    if ($c->req->data && scalar(keys %{$c->req->data}))
-    {
+    if ( $c->req->data && scalar( keys %{ $c->req->data } ) ) {
         $req_params = $c->req->data;
     }
-    else
-    {
-        $req_params = CGI::Expand->expand_hash($c->req->params);
-
-        foreach my $param (@{[$self->search_arg, $self->count_arg, $self->page_arg, $self->offset_arg, $self->ordered_by_arg, $self->grouped_by_arg, $self->prefetch_arg]})
+    else {
+        $req_params = CGI::Expand->expand_hash( $c->req->params );
+
+        foreach my $param (
+            @{  [   $self->search_arg,     $self->count_arg,
+                    $self->page_arg,       $self->offset_arg,
+                    $self->ordered_by_arg, $self->grouped_by_arg,
+                    $self->prefetch_arg
+                ]
+            }
+            )
         {
             # these params can also be composed of JSON
             # but skip if the parameter is not provided
             next if not exists $req_params->{$param};
+
             # find out if CGI::Expand was involved
-            if (ref $req_params->{$param} eq 'HASH')
-            {
-                for my $key ( keys %{$req_params->{$param}} )
-                {
-                    try
-                    {
-                        my $deserialized = JSON::Any->from_json($req_params->{$param}->{$key});
+            if ( ref $req_params->{$param} eq 'HASH' ) {
+                for my $key ( keys %{ $req_params->{$param} } ) {
+
+                    # copy the value because JSON::XS will alter it
+                    # even if decoding failed
+                    my $value = $req_params->{$param}->{$key};
+                    try {
+                        my $deserialized = $self->_json->decode($value);
                         $req_params->{$param}->{$key} = $deserialized;
                     }
-                    catch
-                    {
-                        $c->log->debug("Param '$param.$key' did not deserialize appropriately: $_")
-                        if $c->debug;
+                    catch {
+                        $c->log->debug(
+                            "Param '$param.$key' did not deserialize appropriately: $_"
+                        ) if $c->debug;
                     }
                 }
             }
-            else
-            {
-                try
-                {
-                    my $deserialized = JSON::Any->from_json($req_params->{$param});
+            else {
+                try {
+                    my $value        = $req_params->{$param};
+                    my $deserialized = $self->_json->decode($value);
                     $req_params->{$param} = $deserialized;
                 }
-                catch
-                {
-                    $c->log->debug("Param '$param' did not deserialize appropriately: $_")
-                    if $c->debug;
+                catch {
+                    $c->log->debug(
+                        "Param '$param' did not deserialize appropriately: $_"
+                    ) if $c->debug;
                 }
             }
         }
     }
 
-    $self->inflate_request($c, $req_params);
+    $self->inflate_request( $c, $req_params );
 }
 
 =method_protected generate_rs
 
-generate_rs is used by inflate_request to generate the resultset stored in the current request. It receives $c as its only argument. And by default it merely returns the resultset from the stored_result_source on the controller. Override this method if you need to manipulate the default implementation of getting the resultset from the controller.
+generate_rs is used by inflate_request to get a resultset for the current
+request. It receives $c as its only argument.
+By default it returns a resultset of the controller's class.
+Override this method if you need to manipulate the default implementation of
+getting a resultset.
 
 =cut
 
-sub generate_rs
-{
-    my ($self, $c) = @_;
-    return $self->stored_result_source->resultset;
+sub generate_rs {
+    my ( $self, $c ) = @_;
+
+    return $c->model( $c->stash->{class} || $self->class );
 }
 
 =method_protected inflate_request
@@ -190,12 +221,10 @@ inflate_request is called at the end of deserialize to populate key portions of
 
 =cut
 
-sub inflate_request
-{
-    my ($self, $c, $params) = @_;
+sub inflate_request {
+    my ( $self, $c, $params ) = @_;
 
-    try
-    {
+    try {
         # set static arguments
         $c->req->_set_controller($self);
 
@@ -203,13 +232,12 @@ sub inflate_request
         $c->req->_set_request_data($params);
 
         # set the current resultset
-        $c->req->_set_current_result_set($self->generate_rs($c));
+        $c->req->_set_current_result_set( $self->generate_rs($c) );
 
     }
-    catch
-    {
+    catch {
         $c->log->error($_);
-        $self->push_error($c, { message => $_ });
+        $self->push_error( $c, { message => $_ } );
         $c->detach();
     }
 }
@@ -222,26 +250,23 @@ This action is the chain root for all object level actions (such as delete and u
 
 =cut
 
-sub object_with_id :Chained('deserialize') :CaptureArgs(1) :PathPart('')
-{
-       my ($self, $c, $id) = @_;
+sub object_with_id : Chained('deserialize') : CaptureArgs(1) : PathPart('') {
+    my ( $self, $c, $id ) = @_;
+
+    my $vals = $c->req->request_data->{ $self->data_root };
+    unless ( defined($vals) ) {
 
-    my $vals = $c->req->request_data->{$self->data_root};
-    unless(defined($vals))
-    {
         # no data root, assume the request_data itself is the payload
         $vals = $c->req->request_data;
     }
 
-    try
-    {
+    try {
         # there can be only one set of data
-        $c->req->add_object([$self->object_lookup($c, $id), $vals]);
+        $c->req->add_object( [ $self->object_lookup( $c, $id ), $vals ] );
     }
-    catch
-    {
+    catch {
         $c->log->error($_);
-        $self->push_error($c, { message => $_ });
+        $self->push_error( $c, { message => $_ } );
         $c->detach();
     }
 }
@@ -254,55 +279,49 @@ This action is the chain root for object level actions (such as create, update,
 
 =cut
 
-sub objects_no_id :Chained('deserialize') :CaptureArgs(0) :PathPart('')
-{
-    my ($self, $c) = @_;
+sub objects_no_id : Chained('deserialize') : CaptureArgs(0) : PathPart('') {
+    my ( $self, $c ) = @_;
 
-    if($c->req->has_request_data)
-    {
+    if ( $c->req->has_request_data ) {
         my $data = $c->req->request_data;
         my $vals;
 
-        if(exists($data->{$self->data_root}) && defined($data->{$self->data_root}))
+        if ( exists( $data->{ $self->data_root } )
+            && defined( $data->{ $self->data_root } ) )
         {
-            my $root = $data->{$self->data_root};
-            if(reftype($root) eq 'ARRAY')
-            {
+            my $root = $data->{ $self->data_root };
+            if ( reftype($root) eq 'ARRAY' ) {
                 $vals = $root;
             }
-            elsif(reftype($root) eq 'HASH')
-            {
+            elsif ( reftype($root) eq 'HASH' ) {
                 $vals = [$root];
             }
-            else
-            {
+            else {
                 $c->log->error('Invalid request data');
-                $self->push_error($c, { message => 'Invalid request data' });
+                $self->push_error( $c,
+                    { message => 'Invalid request data' } );
                 $c->detach();
             }
         }
-        else
-        {
+        else {
             # no data root, assume the request_data itself is the payload
-            $vals = [$c->req->request_data];
+            $vals = [ $c->req->request_data ];
         }
 
-        foreach my $val (@$vals)
-        {
-            unless(exists($val->{id}))
-            {
-                $c->req->add_object([$c->req->current_result_set->new_result({}), $val]);
+        foreach my $val (@$vals) {
+            unless ( exists( $val->{id} ) ) {
+                $c->req->add_object(
+                    [ $c->req->current_result_set->new_result( {} ), $val ] );
                 next;
             }
 
-            try
-            {
-                $c->req->add_object([$self->object_lookup($c, $val->{id}), $val]);
+            try {
+                $c->req->add_object(
+                    [ $self->object_lookup( $c, $val->{id} ), $val ] );
             }
-            catch
-            {
+            catch {
                 $c->log->error($_);
-                $self->push_error($c, { message => $_ });
+                $self->push_error( $c, { message => $_ } );
                 $c->detach();
             }
         }
@@ -315,9 +334,8 @@ This method provides the look up functionality for an object based on 'id'. It i
 
 =cut
 
-sub object_lookup
-{
-    my ($self, $c, $id) = @_;
+sub object_lookup {
+    my ( $self, $c, $id ) = @_;
 
     die 'No valid ID provided for look up' unless defined $id and length $id;
     my $object = $c->req->current_result_set->find($id);
@@ -329,7 +347,7 @@ sub object_lookup
 
 list's steps are broken up into three distinct methods: L</list_munge_parameters>, L</list_perform_search>, and L</list_format_output>.
 
-The goal of this method is to call ->search() on the current_result_set, HashRefInflator the result, and return it in $c->stash->{response}->{$self->data_root}. Please see the individual methods for more details on what actual processing takes place.
+The goal of this method is to call ->search() on the current_result_set, change resultset class of the result (if needed), and return it in $c->stash->{$self->stash_key}->{$self->data_root}. Please see the individual methods for more details on what actual processing takes place.
 
 If the L</select> config param is defined then the hashes will contain only those columns, otherwise all columns in the object will be returned. L</select> of course supports the function/procedure calling semantics that L<DBIx::Class::ResultSet/select>. In order to have proper column names in the result, provide arguments in L</as> (which also follows L<DBIx::Class::ResultSet/as> semantics. Similarly L</count>, L</page>, L</grouped_by> and L</ordered_by> affect the maximum number of rows returned as well as the ordering and grouping. Note that if select, count, ordered_by or grouped_by request parameters are present then these will override the values set on the class with select becoming bound by the select_exposes attribute.
 
@@ -359,9 +377,8 @@ Would result in this search:
 
 =cut
 
-sub list
-{
-    my ($self, $c) = @_;
+sub list {
+    my ( $self, $c ) = @_;
 
     $self->list_munge_parameters($c);
     $self->list_perform_search($c);
@@ -374,10 +391,11 @@ sub list
 =method_protected list_munge_parameters
 
 list_munge_parameters is a noop by default. All arguments will be passed through without any manipulation. In order to successfully manipulate the parameters before the search is performed, simply access $c->req->search_parameters|search_attributes (ArrayRef and HashRef respectively), which correspond directly to ->search($parameters, $attributes). Parameter keys will be in already-aliased form.
+To store the munged parameters call $c->req->_set_search_parameters($newparams) and $c->req->_set_search_attributes($newattrs).
 
 =cut
 
-sub list_munge_parameters { } # noop by default
+sub list_munge_parameters { }    # noop by default
 
 =method_protected list_perform_search
 
@@ -385,29 +403,29 @@ list_perform_search executes the actual search. current_result_set is updated to
 
 =cut
 
-sub list_perform_search
-{
-    my ($self, $c) = @_;
+sub list_perform_search {
+    my ( $self, $c ) = @_;
 
-    try
-    {
+    try {
         my $req = $c->req;
 
-        my $rs = $req->current_result_set->search
-        (
-            $req->search_parameters,
-            $req->search_attributes
-        );
+        my $rs =
+            $req->current_result_set->search( $req->search_parameters,
+            $req->search_attributes );
 
         $req->_set_current_result_set($rs);
 
-        $req->_set_search_total_entries($req->current_result_set->pager->total_entries)
-            if $req->has_search_attributes && (exists($req->search_attributes->{page}) && defined($req->search_attributes->{page}) && length($req->search_attributes->{page}));
+        $req->_set_search_total_entries(
+            $req->current_result_set->pager->total_entries )
+            if $req->has_search_attributes
+            && ( exists( $req->search_attributes->{page} )
+            && defined( $req->search_attributes->{page} )
+            && length( $req->search_attributes->{page} ) );
     }
-    catch
-    {
+    catch {
         $c->log->error($_);
-        $self->push_error($c, { message => 'a database error has occured.' });
+        $self->push_error( $c,
+            { message => 'a database error has occured.' } );
         $c->detach();
     }
 }
@@ -418,36 +436,33 @@ list_format_output prepares the response for transmission across the wire. A cop
 
 =cut
 
-sub list_format_output
-{
-    my ($self, $c) = @_;
+sub list_format_output {
+    my ( $self, $c ) = @_;
 
     my $rs = $c->req->current_result_set->search;
-    $rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
+    $rs->result_class( $self->result_class ) if $self->result_class;
 
-    try
-    {
-        my $output = {};
+    try {
+        my $output    = {};
         my $formatted = [];
 
-        foreach my $row ($rs->all)
-        {
-            push(@$formatted, $self->row_format_output($c, $row));
+        foreach my $row ( $rs->all ) {
+            push( @$formatted, $self->row_format_output( $c, $row ) );
         }
 
-        $output->{$self->data_root} = $formatted;
+        $output->{ $self->data_root } = $formatted;
 
-        if ($c->req->has_search_total_entries)
-        {
-            $output->{$self->total_entries_arg} = $c->req->search_total_entries + 0;
+        if ( $c->req->has_search_total_entries ) {
+            $output->{ $self->total_entries_arg } =
+                $c->req->search_total_entries + 0;
         }
 
-        $c->stash->{response} = $output;
+        $c->stash->{ $self->stash_key } = $output;
     }
-    catch
-    {
+    catch {
         $c->log->error($_);
-        $self->push_error($c, { message => 'a database error has occured.' });
+        $self->push_error( $c,
+            { message => 'a database error has occured.' } );
         $c->detach();
     }
 }
@@ -458,10 +473,11 @@ row_format_output is called each row of the inflated output generated from the s
 
 =cut
 
-sub row_format_output
-{
-    my ($self, $c, $row) = @_;
-    return $row; # passthrough by default
+sub row_format_output {
+
+    #my ($self, $c, $row) = @_;
+    my ( $self, undef, $row ) = @_;
+    return $row;    # passthrough by default
 }
 
 =method_protected item
@@ -470,19 +486,18 @@ item will return a single object called by identifier in the uri. It will be inf
 
 =cut
 
-sub item
-{
-    my ($self, $c) = @_;
+sub item {
+    my ( $self, $c ) = @_;
 
-    if($c->req->count_objects != 1)
-    {
+    if ( $c->req->count_objects != 1 ) {
         $c->log->error($_);
-        $self->push_error($c, { message => 'No objects on which to operate' });
+        $self->push_error( $c,
+            { message => 'No objects on which to operate' } );
         $c->detach();
     }
-    else
-    {
-        $c->stash->{response}->{$self->item_root} = $self->each_object_inflate($c, $c->req->get_object(0)->[0]);
+    else {
+        $c->stash->{ $self->stash_key }->{ $self->item_root } =
+            $self->each_object_inflate( $c, $c->req->get_object(0)->[0] );
     }
 }
 
@@ -492,19 +507,17 @@ update_or_create is responsible for iterating any stored objects and performing
 
 =cut
 
-sub update_or_create
-{
-    my ($self, $c) = @_;
+sub update_or_create {
+    my ( $self, $c ) = @_;
 
-    if($c->req->has_objects)
-    {
+    if ( $c->req->has_objects ) {
         $self->validate_objects($c);
-        $self->transact_objects($c, sub { $self->save_objects($c, @_) } );
+        $self->transact_objects( $c, sub { $self->save_objects( $c, @_ ) } );
     }
-    else
-    {
+    else {
         $c->log->error($_);
-        $self->push_error($c, { message => 'No objects on which to operate' });
+        $self->push_error( $c,
+            { message => 'No objects on which to operate' } );
         $c->detach();
     }
 }
@@ -515,22 +528,17 @@ transact_objects performs the actual commit to the database via $schema->txn_do.
 
 =cut
 
-sub transact_objects
-{
-    my ($self, $c, $coderef) = @_;
-
-    try
-    {
-        $self->stored_result_source->schema->txn_do
-        (
-            $coderef,
-            $c->req->objects
-        );
+sub transact_objects {
+    my ( $self, $c, $coderef ) = @_;
+
+    try {
+        $self->stored_result_source->schema->txn_do( $coderef,
+            $c->req->objects );
     }
-    catch
-    {
+    catch {
         $c->log->error($_);
-        $self->push_error($c, { message => 'a database error has occured.' });
+        $self->push_error( $c,
+            { message => 'a database error has occured.' } );
         $c->detach();
     }
 }
@@ -541,23 +549,19 @@ This is a shortcut method for performing validation on all of the stored objects
 
 =cut
 
-sub validate_objects
-{
-    my ($self, $c) = @_;
+sub validate_objects {
+    my ( $self, $c ) = @_;
 
-    try
-    {
-        foreach my $obj ($c->req->all_objects)
-        {
-            $obj->[1] = $self->validate_object($c, $obj);
+    try {
+        foreach my $obj ( $c->req->all_objects ) {
+            $obj->[1] = $self->validate_object( $c, $obj );
         }
     }
-    catch
-    {
+    catch {
         my $err = $_;
         $c->log->error($err);
         $err =~ s/\s+at\s+.+\n$//g;
-        $self->push_error($c, { message => $err });
+        $self->push_error( $c, { message => $err } );
         $c->detach();
     }
 }
@@ -568,86 +572,81 @@ validate_object takes the context and the object as an argument. It then filters
 
 =cut
 
-sub validate_object
-{
-    my ($self, $c, $obj) = @_;
-    my ($object, $params) = @$obj;
+sub validate_object {
+    my ( $self, $c, $obj ) = @_;
+    my ( $object, $params ) = @$obj;
 
     my %values;
-    my %requires_map = map
-    {
-        $_ => 1
-    }
-    @{
-        ($object->in_storage)
+    my %requires_map = map { $_ => 1 } @{
+          ( $object->in_storage )
         ? []
         : $c->stash->{create_requires} || $self->create_requires
     };
 
-    my %allows_map = map
-    {
-        (ref $_) ? %{$_} : ($_ => 1)
-    }
-    (
+    my %allows_map = map { ( ref $_ ) ? %{$_} : ( $_ => 1 ) } (
         keys %requires_map,
-        @{
-            ($object->in_storage)
-            ? ($c->stash->{update_allows} || $self->update_allows)
-            : ($c->stash->{create_allows} || $self->create_allows)
+        @{    ( $object->in_storage )
+            ? ( $c->stash->{update_allows} || $self->update_allows )
+            : ( $c->stash->{create_allows} || $self->create_allows )
         }
     );
 
-    foreach my $key (keys %allows_map)
-    {
+    foreach my $key ( keys %allows_map ) {
+
         # check value defined if key required
         my $allowed_fields = $allows_map{$key};
 
-        if (ref $allowed_fields)
-        {
+        if ( ref $allowed_fields ) {
             my $related_source = $object->result_source->related_source($key);
             my $related_params = $params->{$key};
             my %allowed_related_map = map { $_ => 1 } @$allowed_fields;
-            my $allowed_related_cols = ($allowed_related_map{'*'}) ? [$related_source->columns] : $allowed_fields;
-
-            foreach my $related_col (@{$allowed_related_cols})
-            {
-                if (defined(my $related_col_value = $related_params->{$related_col})) {
+            my $allowed_related_cols =
+                  ( $allowed_related_map{'*'} )
+                ? [ $related_source->columns ]
+                : $allowed_fields;
+
+            foreach my $related_col ( @{$allowed_related_cols} ) {
+                if (defined(
+                        my $related_col_value =
+                            $related_params->{$related_col}
+                    )
+                    )
+                {
                     $values{$key}{$related_col} = $related_col_value;
                 }
             }
         }
-        else
-        {
+        else {
             my $value = $params->{$key};
 
-            if ($requires_map{$key})
-            {
-                unless (defined($value))
-                {
+            if ( $requires_map{$key} ) {
+                unless ( defined($value) ) {
+
                     # if not defined look for default
-                    $value = $object->result_source->column_info($key)->{default_value};
-                    unless (defined $value)
-                    {
+                    $value = $object->result_source->column_info($key)
+                        ->{default_value};
+                    unless ( defined $value ) {
                         die "No value supplied for ${key} and no default";
                     }
                 }
             }
 
             # check for multiple values
-            if (ref($value) && !($value == JSON::Any::true || $value == JSON::Any::false))
+            if ( ref($value) && !( reftype($value) eq reftype(JSON::true) ) )
             {
                 require Data::Dumper;
-                die "Multiple values for '${key}': ${\Data::Dumper::Dumper($value)}";
+                die
+                    "Multiple values for '${key}': ${\Data::Dumper::Dumper($value)}";
             }
 
             # check exists so we don't just end up with hash of undefs
             # check defined to account for default values being used
-            $values{$key} = $value if exists $params->{$key} || defined $value;
+            $values{$key} = $value
+                if exists $params->{$key} || defined $value;
         }
     }
 
-    unless (keys %values || !$object->in_storage)
-    {
+    unless ( keys %values || !$object->in_storage ) {
         die 'No valid keys passed';
     }
 
@@ -660,19 +659,18 @@ delete operates on the stored objects in the request. It first transacts the obj
 
 =cut
 
-sub delete
-{
-    my ($self, $c) = @_;
+sub delete {
+    my ( $self, $c ) = @_;
 
-    if($c->req->has_objects)
-    {
-        $self->transact_objects($c, sub { $self->delete_objects($c, @_) });
+    if ( $c->req->has_objects ) {
+        $self->transact_objects( $c,
+            sub { $self->delete_objects( $c, @_ ) } );
         $c->req->clear_objects;
     }
-    else
-    {
+    else {
         $c->log->error($_);
-        $self->push_error($c, { message => 'No objects on which to operate' });
+        $self->push_error( $c,
+            { message => 'No objects on which to operate' } );
         $c->detach();
     }
 }
@@ -683,13 +681,11 @@ This method is used by update_or_create to perform the actual database manipulat
 
 =cut
 
-sub save_objects
-{
-    my ($self, $c, $objects) = @_;
+sub save_objects {
+    my ( $self, $c, $objects ) = @_;
 
-    foreach my $obj (@$objects)
-    {
-        $self->save_object($c, $obj);
+    foreach my $obj (@$objects) {
+        $self->save_object( $c, $obj );
     }
 }
 
@@ -699,43 +695,50 @@ save_object first checks to see if the object is already in storage. If so, it c
 
 =cut
 
-sub save_object
-{
-    my ($self, $c, $obj) = @_;
+sub save_object {
+    my ( $self, $c, $obj ) = @_;
 
-    my ($object, $params) = @$obj;
+    my ( $object, $params ) = @$obj;
 
-    if ($object->in_storage)
-    {
-        $self->update_object_from_params($c, $object, $params);
+    if ( $object->in_storage ) {
+        $self->update_object_from_params( $c, $object, $params );
     }
-    else
-    {
-        $self->insert_object_from_params($c, $object, $params);
+    else {
+        $self->insert_object_from_params( $c, $object, $params );
     }
 
 }
 
 =method_protected update_object_from_params
 
-update_object_from_params iterates through the params to see if any of them are pertinent to relations. If so it calls L</update_object_relation> with the object, and the relation parameters. Then it calls ->upbdate on the object.
+update_object_from_params iterates through the params to see if any of them are pertinent to relations. If so it calls L</update_object_relation> with the object, and the relation parameters. Then it calls ->update on the object.
 
 =cut
 
-sub update_object_from_params
-{
-    my ($self, $c, $object, $params) = @_;
+sub update_object_from_params {
+    my ( $self, $c, $object, $params ) = @_;
 
-    foreach my $key (keys %$params)
-    {
+    foreach my $key ( keys %$params ) {
         my $value = $params->{$key};
-        if (ref($value) && !($value == JSON::Any::true || $value == JSON::Any::false))
-        {
-            $self->update_object_relation($c, $object, delete $params->{$key}, $key);
+        if ( ref($value) && !( reftype($value) eq reftype(JSON::true) ) ) {
+            $self->update_object_relation( $c, $object,
+                delete $params->{$key}, $key );
+        }
+
+        # accessor = colname
+        elsif ( $object->can($key) ) {
+            $object->$key($value);
+        }
+
+        # accessor != colname
+        else {
+            my $accessor =
+                $object->result_source->column_info($key)->{accessor};
+            $object->$accessor($value);
         }
     }
 
-    $object->update($params);
+    $object->update();
 }
 
 =method_protected update_object_relation
@@ -744,11 +747,36 @@ update_object_relation finds the relation to the object, then calls ->update wit
 
 =cut
 
-sub update_object_relation
-{
-    my ($self, $c, $object, $related_params, $relation) = @_;
-    my $row = $object->find_related($relation, {} , {});
-    $row->update($related_params);
+sub update_object_relation {
+    my ( $self, $c, $object, $related_params, $relation ) = @_;
+    my $row = $object->find_related( $relation, {}, {} );
+
+    if ($row) {
+        foreach my $key ( keys %$related_params ) {
+            my $value = $related_params->{$key};
+            if ( ref($value) && !( reftype($value) eq reftype(JSON::true) ) )
+            {
+                $self->update_object_relation( $c, $row,
+                    delete $related_params->{$key}, $key );
+            }
+
+            # accessor = colname
+            elsif ( $row->can($key) ) {
+                $row->$key($value);
+            }
+
+            # accessor != colname
+            else {
+                my $accessor =
+                    $row->result_source->column_info($key)->{accessor};
+                $row->$accessor($value);
+            }
+        }
+        $row->update();
+    }
+    else {
+        $object->create_related( $relation, $related_params );
+    }
 }
 
 =method_protected insert_object_from_params
@@ -757,11 +785,35 @@ insert_object_from_params sets the columns for the object, then calls ->insert
 
 =cut
 
-sub insert_object_from_params
-{
-    my ($self, $c, $object, $params) = @_;
-    $object->set_columns($params);
+sub insert_object_from_params {
+
+    #my ($self, $c, $object, $params) = @_;
+    my ( $self, undef, $object, $params ) = @_;
+
+    my %rels;
+    while ( my ( $key, $value ) = each %{$params} ) {
+        if ( ref($value) && !( reftype($value) eq reftype(JSON::true) ) ) {
+            $rels{$key} = $value;
+        }
+
+        # accessor = colname
+        elsif ( $object->can($key) ) {
+            $object->$key($value);
+        }
+
+        # accessor != colname
+        else {
+            my $accessor =
+                $object->result_source->column_info($key)->{accessor};
+            $object->$accessor($value);
+        }
+    }
+
     $object->insert;
+
+    while ( my ( $k, $v ) = each %rels ) {
+        $object->create_related( $k, $v );
+    }
 }
 
 =method_protected delete_objects
@@ -770,11 +822,10 @@ delete_objects iterates through each object calling L</delete_object>
 
 =cut
 
-sub delete_objects
-{
-    my ($self, $c, $objects) = @_;
+sub delete_objects {
+    my ( $self, $c, $objects ) = @_;
 
-    map { $self->delete_object($c, $_->[0]) } @$objects;
+    map { $self->delete_object( $c, $_->[0] ) } @$objects;
 }
 
 =method_protected delete_object
@@ -783,9 +834,10 @@ Performs the actual ->delete on the object
 
 =cut
 
-sub delete_object
-{
-    my ($self, $c, $object) = @_;
+sub delete_object {
+
+    #my ($self, $c, $object) = @_;
+    my ( $self, undef, $object ) = @_;
 
     $object->delete;
 }
@@ -796,38 +848,42 @@ end performs the final manipulation of the response before it is serialized. Thi
 
 =cut
 
-sub end :Private
-{
-    my ($self, $c) = @_;
+sub end : Private {
+    my ( $self, $c ) = @_;
 
-    # check for errors
-    my $default_status;
-
-    # Check for errors caught elsewhere
-    if ( $c->res->status and $c->res->status != 200 ) {
-        $default_status = $c->res->status;
-        $c->stash->{response}->{success} = $self->use_json_boolean ? JSON::Any::false : 'false';
-    } elsif ($self->get_errors($c)) {
-        $c->stash->{response}->{messages} = $self->get_errors($c);
-        $c->stash->{response}->{success} = $self->use_json_boolean ? JSON::Any::false : 'false';
-        $default_status = 400;
-    } else {
-        $c->stash->{response}->{success} = $self->use_json_boolean ? JSON::Any::true : 'true';
-        $default_status = 200;
+    # don't change the http status code if already set elsewhere
+    unless ( $c->res->status && $c->res->status != 200 ) {
+        if ( $self->has_errors($c) ) {
+            $c->res->status(400);
+        }
+        else {
+            $c->res->status(200);
+        }
     }
 
-    unless ($default_status == 200)
-    {
-        delete $c->stash->{response}->{$self->data_root};
+    if ( $c->res->status == 200 ) {
+        $c->stash->{ $self->stash_key }->{success} =
+            $self->use_json_boolean ? JSON::true : 'true';
+        if ( $self->return_object && $c->req->has_objects ) {
+            my $returned_objects = [];
+            push( @$returned_objects, $self->each_object_inflate( $c, $_ ) )
+                for map { $_->[0] } $c->req->all_objects;
+            $c->stash->{ $self->stash_key }->{ $self->data_root } =
+                scalar(@$returned_objects) > 1
+                ? $returned_objects
+                : $returned_objects->[0];
+        }
     }
-    elsif($self->return_object && $c->req->has_objects)
-    {
-        my $returned_objects = [];
-        push(@$returned_objects, $self->each_object_inflate($c, $_)) for map { $_->[0] } $c->req->all_objects;
-        $c->stash->{response}->{$self->data_root} = scalar(@$returned_objects) > 1 ? $returned_objects : $returned_objects->[0];
+    else {
+        $c->stash->{ $self->stash_key }->{success} =
+            $self->use_json_boolean ? JSON::false : 'false';
+        $c->stash->{ $self->stash_key }->{messages} = $self->get_errors($c)
+            if $self->has_errors($c);
+
+        # don't return data for error responses
+        delete $c->stash->{ $self->stash_key }->{ $self->data_root };
     }
 
-    $c->res->status( $default_status || 200 );
     $c->forward('serialize');
 }
 
@@ -839,15 +895,22 @@ This only executes if L</return_object> if set and if there are any objects to a
 
 =cut
 
-sub each_object_inflate
-{
-    my ($self, $c, $object) = @_;
+sub each_object_inflate {
+
+    #my ($self, $c, $object) = @_;
+    my ( $self, undef, $object ) = @_;
 
-    return { $object->get_inflated_columns };
+    return { $object->get_columns };
 }
 
+=method_protected serialize
+
+multiple actions forward to serialize which uses Catalyst::Action::Serialize.
+
+=cut
+
 # from Catalyst::Action::Serialize
-sub serialize :ActionClass('Serialize') { }
+sub serialize : ActionClass('Serialize') { }
 
 =method_protected push_error
 
@@ -855,10 +918,19 @@ push_error stores an error message into the stash to be later retrieved by L</en
 
 =cut
 
-sub push_error
-{
+sub push_error {
     my ( $self, $c, $params ) = @_;
-    push( @{$c->stash->{_dbic_crud_errors}}, $params->{message} || 'unknown error' );
+    die 'Catalyst app object missing'
+        unless defined $c;
+    my $error = 'unknown error';
+    if ( exists $params->{message} ) {
+        $error = $params->{message};
+
+        # remove newline from die "error message\n" which is required to not
+        # have the filename and line number in the error text
+        $error =~ s/\n$//;
+    }
+    push( @{ $c->stash->{_dbic_crud_errors} }, $error );
 }
 
 =method_protected get_errors
@@ -867,12 +939,26 @@ get_errors returns all of the errors stored in the stash
 
 =cut
 
-sub get_errors
-{
+sub get_errors {
     my ( $self, $c ) = @_;
+    die 'Catalyst app object missing'
+        unless defined $c;
     return $c->stash->{_dbic_crud_errors};
 }
 
+=method_protected has_errors
+
+returns returns true if errors are stored in the stash
+
+=cut
+
+sub has_errors {
+    my ( $self, $c ) = @_;
+    die 'Catalyst app object missing'
+        unless defined $c;
+    return exists $c->stash->{_dbic_crud_errors};
+}
+
 =head1 DESCRIPTION
 
 Easily provide common API endpoints based on your L<DBIx::Class> schema classes. Module provides both RPC and REST interfaces to base functionality. Uses L<Catalyst::Action::Serialize> and L<Catalyst::Action::Deserialize> to serialise response and/or deserialise request.
@@ -916,13 +1002,21 @@ Below are explanations for various configuration parameters. Please see L<Cataly
 
 Whatever you would pass to $c->model to get a resultset for this class. MyAppDB::Track for example.
 
+=head3 resultset_class
+
+Desired resultset class after accessing your model. MyAppDB::ResultSet::Track for example. By default, it's DBIx::Class::ResultClass::HashRefInflator. Set to empty string to leave resultset class without change.
+
+=head3 stash_key
+
+Controls where in stash request_data should be stored, and defaults to 'response'.
+
 =head3 data_root
 
-By default, the response data is serialized into $c->stash->{response}->{$self->data_root} and data_root defaults to 'list' to preserve backwards compatibility. This is now configuable to meet the needs of the consuming client.
+By default, the response data is serialized into $c->stash->{$self->stash_key}->{$self->data_root} and data_root defaults to 'list' to preserve backwards compatibility. This is now configuable to meet the needs of the consuming client.
 
 =head3 use_json_boolean
 
-By default, the response success status is set to a string value of "true" or "false". If this attribute is true, JSON::Any's true() and false() will be used instead. Note, this does not effect other internal processing of boolean values.
+By default, the response success status is set to a string value of "true" or "false". If this attribute is true, JSON's true() and false() will be used instead. Note, this does not effect other internal processing of boolean values.
 
 =head3 count_arg, page_arg, select_arg, search_arg, grouped_by_arg, ordered_by_arg, prefetch_arg, as_arg, total_entries_arg
 
@@ -1011,7 +1105,7 @@ Arguments to pass to L<DBIx::Class::ResultSet/rows> when performing search for L
 
 =head3 page
 
-Arguments to pass to L<DBIx::Class::ResultSet/rows> when performing search for L</list>.
+Arguments to pass to L<DBIx::Class::ResultSet/page> when performing search for L</list>.
 
 =head1 EXTENDING
 
@@ -1031,8 +1125,8 @@ For example if you wanted create to return the JSON for the newly created object
     $self->next::method($c);
 
     if ($c->req->has_objects) {
-      # $c->stash->{response} will be serialized in the end action
-      $c->stash->{response}->{$self->data_root} = [ map { { $_->get_inflated_columns } } ($c->req->all_objects) ] ;
+      # $c->stash->{$self->stash_key} will be serialized in the end action
+      $c->stash->{$self->stash_key}->{$self->data_root} = [ map { { $_->get_inflated_columns } } ($c->req->all_objects) ] ;
     }
   }
 
@@ -1052,7 +1146,7 @@ If more extensive customization is required, it is recommened to peer into the r
 
 It should be noted that version 1.004 and above makes a rapid depature from the status quo. The internals were revamped to use more modern tools such as Moose and its role system to refactor functionality out into self-contained roles.
 
-To this end, internally, this module now understands JSON boolean values (as represented by JSON::Any) and will Do The Right Thing in handling those values. This means you can have ColumnInflators installed that can covert between JSON::Any booleans and whatever your database wants for boolean values.
+To this end, internally, this module now understands JSON boolean values (as represented by the JSON module) and will Do The Right Thing in handling those values. This means you can have ColumnInflators installed that can covert between JSON booleans and whatever your database wants for boolean values.
 
 Validation for various *_allows or *_exposes is now accomplished via Data::DPath::Validator with a lightly simplified, via subclass, Data::DPath::Validator::Visitor. The rough jist of the process goes as follows: Arguments provided to those attributes are fed into the Validator and Data::DPaths are generated. Then, incoming requests are validated against these paths generated. The validator is set in "loose" mode meaning only one path is required to match. For more information, please see L<Data::DPath::Validator> and more specifically L<Catalyst::Controller::DBIC::API::Validator>.