[Catalyst-commits] r12448 - in Catalyst-Controller-DBIC-API/1.002: . branches branches/moosify-validation-configurability branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/REST branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC branches/moosify-validation-configurability/t/lib/RestTest/Schema/Result branches/moosify-validation-configurability/t/lib/RestTest/Schema/ResultSet branches/moosify-validation-configurability/t/rest branches/moosify-validation-configurability/t/rpc branches/moosify-validation-configurability/t/var trunk trunk/lib/Catalyst/Controller/DBIC trunk/lib/Catalyst/Controller/DBIC/API trunk/t/lib/RestTest/Controller/API/REST trunk/t/lib/RestTest/Controller/API/RPC trunk/t/lib/RestTest/Schema/Result trunk/t/lib/RestTest/Schema/ResultSet trunk/t/rest trunk/t/rpc trunk/t/var

lukes at dev.catalyst.perl.org lukes at dev.catalyst.perl.org
Mon Dec 21 17:28:50 GMT 2009


Author: lukes
Date: 2009-12-21 17:28:49 +0000 (Mon, 21 Dec 2009)
New Revision: 12448

Added:
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Request.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/StoredResultSource.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Types.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Validator.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_prefetch.t
   Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_prefetch.t
Modified:
   Catalyst-Controller-DBIC-API/1.002/
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/Changes
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/Makefile.PL
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Base.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/REST.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/RPC.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/REST/Artist.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/REST/Producer.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/Artist.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/CD.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/Producer.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/TrackExposed.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Schema/Result/Track.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Schema/ResultSet/Track.pm
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rest/create.t
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rest/list.t
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/create.t
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list.t
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_json_search.t
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_search_allows.t
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/setup_dbic_args.t
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/update.t
   Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/var/DBIxClass.db
   Catalyst-Controller-DBIC-API/1.002/trunk/Changes
   Catalyst-Controller-DBIC-API/1.002/trunk/Makefile.PL
   Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/Base.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/REST.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/RPC.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Artist.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Producer.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Artist.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/CD.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Producer.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/Result/Track.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/ResultSet/Track.pm
   Catalyst-Controller-DBIC-API/1.002/trunk/t/rest/create.t
   Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/create.t
   Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list.t
   Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_json_search.t
   Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_search_allows.t
   Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/setup_dbic_args.t
   Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/update.t
   Catalyst-Controller-DBIC-API/1.002/trunk/t/var/DBIxClass.db
Log:
 r722 at luke-mbp (orig r10173):  lukes | 2009-05-16 13:51:55 +0200
 manually merged prefetch branch
 r723 at luke-mbp (orig r10174):  lukes | 2009-05-16 14:55:42 +0200
 list_prefetch test added with JSON, CGI::Expand and normal params support
 r724 at luke-mbp (orig r10175):  lukes | 2009-05-16 15:00:26 +0200
 removed all NEXT calls from tests and docs
 r725 at luke-mbp (orig r10176):  lukes | 2009-05-16 15:05:24 +0200
 removed a couple more necessary NEXT calls
 r726 at luke-mbp (orig r10177):  lukes | 2009-05-16 15:15:08 +0200
 altered docs to make action config clearer
 r727 at luke-mbp (orig r10178):  lukes | 2009-05-16 16:01:40 +0200
 refactored to channel all request param parsing through common code
 r728 at luke-mbp (orig r10179):  lukes | 2009-05-16 16:07:50 +0200
 added entries to changes
 r1383 at luke-mbp (orig r10249):  t0m | 2009-05-23 13:09:06 +0200
 Add repository resource to all the MI Makefile.PLs
 r1384 at luke-mbp (orig r10915):  lukes | 2009-07-17 10:54:05 +0200
 fixed failing test
 r1385 at luke-mbp (orig r10916):  lukes | 2009-07-17 11:17:23 +0200
 added tests for controller class which can handle any object type
 r1386 at luke-mbp (orig r10917):  lukes | 2009-07-17 11:17:53 +0200
 updated Changes
 r1399 at luke-mbp (orig r10937):  abraxxa | 2009-07-21 19:02:01 +0200
 added totalcount to paged list responses
 fixed some tests weren't run in t/rpc/list.t
 fixed wrong setup_dbic_args_method error message
 
 r1708 at luke-mbp (orig r10939):  lukes | 2009-07-22 12:05:42 +0200
 upped DBIC dep
 r1764 at luke-mbp (orig r11038):  abraxxa | 2009-08-06 14:17:43 +0200
 Database errors are properly handled + test
 
 r1765 at luke-mbp (orig r11818):  lukes | 2009-11-13 16:31:14 +0100
 fixes to make compatible with future version of C::C::Rest
 r1766 at luke-mbp (orig r11848):  lukes | 2009-11-16 10:13:11 +0100
 fixed 'isa redefined warnings'
 r1767 at luke-mbp (orig r11849):  lukes | 2009-11-16 10:15:00 +0100
 upped version
 r1768 at luke-mbp (orig r12230):  abraxxa | 2009-12-07 12:08:00 +0100
 Database errors are also handled for searches + tests
 totalcount isn't included in the response if a db error occurs while fetching data
 converted no_plan tests to done_testing (required Test::More 0.88)
 
 r1769 at luke-mbp (orig r12272):  nperez | 2009-12-09 20:02:52 +0100
  r4250 at nicklaptop:  nicholas | 2009-12-09 13:00:02 -0600
  branch for moosification, more validation and configuration
 
 r1770 at luke-mbp (orig r12273):  nperez | 2009-12-09 20:03:08 +0100
  r4251 at nicklaptop:  nicholas | 2009-12-09 13:01:05 -0600
  Moosify, validation, more configurability for data
 
 r1771 at luke-mbp (orig r12288):  nperez | 2009-12-10 10:25:01 +0100
  r4254 at nicklaptop:  nicholas | 2009-12-10 03:23:49 -0600
  Update test count on list_prefetch
 
 r1772 at luke-mbp (orig r12289):  nperez | 2009-12-10 10:30:12 +0100
  r4256 at nicklaptop:  nicholas | 2009-12-10 03:29:01 -0600
  Update response structure to actually use $self->data_root
 
 r1773 at luke-mbp (orig r12291):  nperez | 2009-12-10 11:04:21 +0100
  r4258 at nicklaptop:  nicholas | 2009-12-10 03:54:44 -0600
  Support JSON boolean in validate
 
 r1774 at luke-mbp (orig r12292):  nperez | 2009-12-10 11:04:30 +0100
  r4259 at nicklaptop:  nicholas | 2009-12-10 04:03:10 -0600
  Support JSON boolean in validate for real this time
 
 r1775 at luke-mbp (orig r12293):  nperez | 2009-12-10 13:24:27 +0100
  r4262 at nicklaptop:  nicholas | 2009-12-10 06:23:17 -0600
  Make returning the object from create/update configurable
 
 r1816 at luke-mbp (orig r12294):  nperez | 2009-12-10 15:30:15 +0100
  r4264 at nicklaptop:  nicholas | 2009-12-10 08:29:03 -0600
  Properly expand objects for convenience return_object option and support json bool in searching
 
 r1817 at luke-mbp (orig r12351):  nperez | 2009-12-13 23:32:23 +0100
 Add dep for MooseX::Aliases
 r1818 at luke-mbp (orig r12442):  nperez | 2009-12-21 14:32:44 +0100
  r5162 at nicklaptop:  nicholas | 2009-12-21 07:30:55 -0600
  Convert DBIC::API::Request into a role and apply it to ->req, move types out into their own package, update the docs and explain new features, bump version to 1.004, correct small problem with setup_list_method, only one begin method in Base instead
 



Property changes on: Catalyst-Controller-DBIC-API/1.002
___________________________________________________________________
Name: svk:merge
   - 4ad37cd2-5fec-0310-835f-b3785c72a374:/Catalyst-Controller-DBIC-API/1.001:9528
4ad37cd2-5fec-0310-835f-b3785c72a374:/Catalyst-Controller-DBIC-API/1.003:10040
   + 4ad37cd2-5fec-0310-835f-b3785c72a374:/Catalyst-Controller-DBIC-API/1.001:9528
4ad37cd2-5fec-0310-835f-b3785c72a374:/Catalyst-Controller-DBIC-API/1.003:12447
992f488a-d630-404b-95f9-f7d0fdf28443:/local/ccda:5162

Copied: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability (from rev 10040, Catalyst-Controller-DBIC-API/1.002/trunk)

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/Changes
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/Changes	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/Changes	2009-12-21 17:28:49 UTC (rev 12448)
@@ -1,8 +1,36 @@
 Revision history for Catalyst-Controller-DBIC-API
 
-1.002001
-- Minor change to make this module work with Catalyst::Runtime 5.80
+1.004000
+- Moosify
+- Move validation for *_exposes/*_allows to Data::DPath::Validator
+- Reorganize internals to use Moose and roles
+- Allow maximum configuration for what request parameters are named
+- Properly handle JSON boolean values
+- Earlier and more consistent validation of configuration and request parameters
 
+1.003004
+- Database errors are also handled for searches + tests
+- totalcount isn't included in the response if a db error occurs while fetching data
+- converted no_plan tests to done_testing (required Test::More 0.88)
+
+1.003003
+- Database errors are properly handled + test
+- Fixed isa redefined warnings
+- Fixed bug preventing compat with future Catalyst::Action::Deserialize versions
+
+1.003002
+- Added totalcount to paged list responses
+- Fixed some tests weren't run in t/rpc/list.t
+- Fixed wrong setup_dbic_args_method error message
+
+1.003001
+- Minor fix to prevent failing test
+
+1.003000
+- Added prefetch support
+- Refactored to ensure all request params accept JSON, CGI::Expand or standard params
+- Doc improvements
+
 1.002000
 - Better error handing when unable to parse search arg
 - Added setup_dbic_args_method config option

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/Makefile.PL
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/Makefile.PL	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/Makefile.PL	2009-12-21 17:28:49 UTC (rev 12448)
@@ -4,13 +4,16 @@
 perl_version '5.006001';
 all_from 'lib/Catalyst/Controller/DBIC/API.pm';
 
-requires 'DBIx::Class' => 0.08;
+requires 'DBIx::Class' => 0.08103;
 requires 'Catalyst::Runtime' => 5.7010;
 requires 'Catalyst::Action::REST' => 0.60;
 requires 'CGI::Expand' => 2.02;
 requires 'JSON::Any' => 1.19;
+requires 'Test::Deep' => 0.104;
+requires 'Data::DPath::Validator' => 0.093411;
+requires 'MooseX::Aliases' => 0.07;
 
-build_requires 'Test::More'       => 0.7;
+build_requires 'Test::More'       => 0.88;
 build_requires 'Catalyst::Model::DBIC::Schema' => 0.20;
 build_requires 'Test::WWW::Mechanize'       => 0.20;
 build_requires 'Test::WWW::Mechanize::Catalyst'       => 0.37;
@@ -21,4 +24,6 @@
 
 auto_install;
 
+resources repository => 'http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Controller-DBIC-API/1.002/trunk/';
+
 WriteAll;

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Base.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/Base.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Base.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -1,123 +1,185 @@
-package													# hide from PAUSE
+package		# hide from PAUSE
 	Catalyst::Controller::DBIC::API::Base;
+our $VERSION = '1.004000';
 
-use strict;
-use warnings;
-use base qw/Catalyst::Controller CGI::Expand/;
+use Moose;
 
+
+BEGIN { extends 'Catalyst::Controller'; }
+use CGI::Expand ();
 use DBIx::Class::ResultClass::HashRefInflator;
 use JSON::Any;
+use Test::Deep::NoTest('eq_deeply');
+use MooseX::Types::Moose(':all');
+use MooseX::Aliases;
+use Moose::Util;
+use Try::Tiny;
+use Catalyst::Controller::DBIC::API::Request;
+use namespace::autoclean;
 
-__PACKAGE__->mk_accessors(qw(
-															class create_requires update_requires update_allows $self->rs_stash_key create_allows list_count list_returns list_grouped_by list_search_exposes list_ordered_by rs_stash_key object_stash_key setup_list_method setup_dbic_args_method
-													));
+with 'Catalyst::Controller::DBIC::API::StoredResultSource';
+with 'Catalyst::Controller::DBIC::API::StaticArguments';
+with 'Catalyst::Controller::DBIC::API::RequestArguments' => { static => 1 };
 
-__PACKAGE__->config(
-	class => undef,
-	create_requires => [],
-	create_allows => [],
-	update_requires => [],
-	update_allows => [],
-	list_returns => [],
-	list_grouped_by => [],
-	list_search_exposes => [],
-	list_ordered_by => [],
-	list_count => undef,
-	object_stash_key => 'object',
-	rs_stash_key => 'class_rs'
-);
+has 'rs_stash_key' => ( is => 'ro', isa => Str, default => 'class_rs' );
+has 'object_stash_key' => ( is => 'ro', isa => Str, default => 'object' );
+has 'setup_list_method' => ( is => 'ro', isa => Str, predicate => 'has_setup_list_method');
+has 'setup_dbic_args_method' => ( is => 'ro', isa => Str, predicate => 'has_setup_dbic_args_method');
 
+__PACKAGE__->config();
+
 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');
+    $c->forward('deserialize');
+}
+
+sub setup :Chained('specify.in.subclass.config') :CaptureArgs(0) :PathPart('specify.in.subclass.config') {
 	my ($self, $c) = @_;
 
-	$c->forward('deserialize');
-	if ($c->req->data) {
-		$c->req->params($c->req->data);
-	}
-	$self->NEXT::begin($c);	
+	$c->stash->{$self->rs_stash_key} = $self->stored_model;
 }
 
-sub setup :Chained('specify.in.subclass.config') :CaptureArgs(0) :PathPart('specify.in.subclass.config') {
+# from Catalyst::Action::Serialize
+sub deserialize :ActionClass('Deserialize') {
 	my ($self, $c) = @_;
 
-	$c->stash->{$self->rs_stash_key} = $c->model($self->class);
+	my $req_params;
+	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->ordered_by_arg, $self->grouped_by_arg, $self->prefetch_arg]}) {
+			# these params can also be composed of JSON
+			try 
+            {
+				my $deserialized = JSON::Any->from_json($req_params->{$param});
+				$req_params->{$param} = $deserialized;
+			}
+            catch { $c->log->debug("Param '$param' did not deserialize appropriately: $_") if $c->debug }
+		}
+	}
+    
+    if(exists($req_params->{$self->data_root}))
+    {
+        my $val = delete $req_params->{$self->data_root};
+        $req_params->{data} = $val;
+    }
+    else
+    {
+        $req_params->{data} = \%$req_params;
+    }
+
+    try
+    {
+        # set static arguments
+        $c->req->_set_application($self); 
+        $c->req->_set_prefetch_allows($self->prefetch_allows);
+        $c->req->_set_search_exposes($self->search_exposes);
+        $c->req->_set_select_exposes($self->select_exposes);
+        $c->req->_set_request_data($req_params->{data});
+
+        # set request arguments
+        $c->req->_set_prefetch($req_params->{$self->prefetch_arg}) if exists $req_params->{$self->prefetch_arg};
+        $c->req->_set_select($req_params->{$self->select_arg}) if exists $req_params->{$self->select_arg};
+        $c->req->_set_grouped_by($req_params->{$self->grouped_by_arg}) if exists $req_params->{$self->grouped_by_arg};
+        $c->req->_set_ordered_by($req_params->{$self->ordered_by_arg}) if exists $req_params->{$self->ordered_by_arg};
+        $c->req->_set_search($req_params->{$self->search_arg}) if exists $req_params->{$self->search_arg};
+        $c->req->_set_count($req_params->{$self->count_arg}) if exists $req_params->{$self->count_arg};
+        $c->req->_set_page($req_params->{$self->page_arg}) if exists $req_params->{$self->page_arg};
+    }
+    catch
+    {
+        $self->push_error($c, { message => $_ });
+    }
 }
 
 sub list :Private {
 	my ($self, $c) = @_;
 
+	return if $self->get_errors($c);
 	my $ret = $c->forward('generate_dbic_search_args');
 	return unless ($ret && ref $ret);
 	my ($params, $args) = @{$ret};
 	return if $self->get_errors($c);
-
+    
 	$c->stash->{$self->rs_stash_key} = $c->stash->{$self->rs_stash_key}->search($params, $args);
-	$c->forward('format_list');
+    # add the total count of all rows in case of a paged resultset
+    try 
+    {
+        $c->stash->{_dbic_api}->{totalcount} = $c->stash->{$self->rs_stash_key}->pager->total_entries
+            if $args->{page};
+        $c->forward('format_list');
+    }
+    catch
+    {
+        $c->log->error($_);
+        # send a generic error to the client to not give out infos about
+        # the database schema
+        $self->push_error($c, { message => 'a database error has occured.' });
+    }
 }
 
 sub generate_dbic_search_args :Private {
 	my ($self, $c) = @_;
+  
+	my $args = {};
+    my $req = $c->req;
+    my $pre_format_params;
 
-	my $req_params = (grep { ref $_ } values %{$c->req->params}) ? $c->req->params : $self->expand_hash($c->req->params);
-	# if expand_hash didn't do anything, try json
-	unless (exists $req_params->{search} && ref $req_params->{search} eq 'HASH') {
-		eval {
-			$req_params->{search} = exists $c->req->params->{search} ? JSON::Any->from_json($c->req->params->{search}) : undef;
-		};
-		if ($@) {
-			# json didn't work either. looks like it's screwed.
-			$self->push_error($c, { message => "can not parse search arg" });
-			return;
-		}
-	}
-
-	if ( my $a = $self->setup_list_method ) {
-		my $setup_action = $self->action_for($a);
+	if ( my $action_name = $self->setup_list_method ) {
+		my $setup_action = $self->action_for($action_name);
 		if ( defined $setup_action ) {
-			$c->forward("/$setup_action", [ $req_params ]);
+			$c->forward("/$setup_action", [ $req->request_data, $req ]);
+            if(exists($req->request_data->{$self->search_arg}))
+            {
+                if(!$req->has_search)
+                {
+                    $req->_set_search($req->request_data->{$self->search_arg});
+                }
+                elsif(!eq_deeply($req->has_search, $req->request_data->{$self->search_arg}))
+                {
+                    $req->_set_search($req->request_data->{$self->search_arg});
+                }
+            }
 		} else {
-			$c->log->error("setup_list_method was configured, but action $a not found");
+			$c->log->error("setup_list_method was configured, but action $action_name not found");
 		}
 	}
-	my $source = $c->stash->{$self->rs_stash_key}->result_source;
+	my $source = $self->stored_result_source;
 
 	my ($params, $join);
-	my $args = {};
-	
-	($params, $join) = $self->_format_search($c, { params => $req_params->{search}, source => $source }) if ($req_params->{search});
 
-	$args->{group_by} = $req_params->{list_grouped_by} || ((scalar(@{$self->list_grouped_by})) ? $self->list_grouped_by : undef);
-	$args->{order_by} = $req_params->{list_ordered_by} || ((scalar(@{$self->list_ordered_by})) ? $self->list_ordered_by : undef);
-	$args->{rows} = $req_params->{list_count} || $self->list_count;
-	$args->{page} = $req_params->{list_page};
-	if ($args->{page}) {
-		unless ($args->{page} =~ /^\d+$/xms) {
-			$self->push_error($c, { message => "list_page must be numeric" });
-		}
-	}
-	if ($args->{rows}) {
-		unless ($args->{rows} =~ /^\d+$/xms) {
-			$self->push_error($c, { message => "list_count must be numeric" });
-		}
-	}
+	($params, $join) = $self->_format_search($c, { params => $req->search, source => $source }) if $req->has_search;
+    
+    $args->{prefetch} = $req->prefetch || $self->prefetch || undef;
+	$args->{group_by} = $req->grouped_by || ((scalar(@{$self->grouped_by})) ? $self->grouped_by : undef);
+	$args->{order_by} = $req->ordered_by || ((scalar(@{$self->ordered_by})) ? $self->ordered_by : undef);
+	$args->{rows} = $req->count || $self->count;
+    $args->{page} = $req->page;
+
 	if ($args->{page} && !$args->{rows}) {
 		$self->push_error($c, { message => "list_page can only be used with list_count" });
 	}
-	$args->{select} = $req_params->{list_returns} || ((scalar(@{$self->list_returns})) ? $self->list_returns : undef);
+	
+    $args->{select} = $req->select || ((scalar(@{$self->select})) ? $self->select : undef);
 	if ($args->{select}) {
 		# make sure all columns have an alias to avoid ambiguous issues
 		$args->{select} = [map { ($_ =~ m/\./) ? $_ : "me.$_" } (ref $args->{select}) ? @{$args->{select}} : $args->{select}];
 	}
+
 	$args->{join} = $join;
-	if ( my $a = $self->setup_dbic_args_method ) {
-		my $format_action = $self->action_for($a);
+	if ( my $action_name = $self->setup_dbic_args_method ) {
+		my $format_action = $self->action_for($action_name);
 		if ( defined $format_action ) {
 			($params, $args) = @{$c->forward("/$format_action", [ $params, $args ])};
 		} else {
-			$c->log->error("setup_dbic_args_method was configured, but action $a not found");
+			$c->log->error("setup_dbic_args_method was configured, but action $action_name not found");
 		}
 	}
-
+	
 	return [$params, $args];
 }
 
@@ -129,42 +191,29 @@
 
 	my $join = {};
 	my %search_params;
-
+    
+    my $search_exposes = $self->search_exposes;
 	# munge list_search_exposes into format that's easy to do with
-	my %valid = map { (ref $_) ? %{$_} : ($_ => 1) } @{$p->{_list_search_exposes} || $self->list_search_exposes};
+	my %valid = map { (ref $_) ? %{$_} : ($_ => 1) } @{$p->{_list_search_exposes} || $search_exposes};
 	if ($valid{'*'}) {
 		# if the wildcard is passed they can access any column or relationship
 		$valid{$_} = 1 for $source->columns;
 		$valid{$_} = ['*'] for $source->relationships;
 	}
 	# figure out the valid cols, defaulting to all cols if not specified
-	my @valid_cols = @{$self->list_search_exposes} ? (grep { $valid{$_} eq 1 } keys %valid) : $source->columns;
+	my @valid_cols = @$search_exposes ? (grep { $valid{$_} eq 1 } keys %valid) : $source->columns;
 
 	# figure out the valid rels, defaulting to all rels if not specified
-	my @valid_rels = @{$self->list_search_exposes} ? (grep { ref $valid{$_} } keys %valid) : $source->relationships;
+	my @valid_rels = @$search_exposes ? (grep { ref $valid{$_} } keys %valid) : $source->relationships;
 
 	my %_col_map = map { $_ => 1 } @valid_cols;
 	my %_rel_map = map { $_ => 1 } @valid_rels;
 	my %_source_col_map = map { $_ => 1 } $source->columns;
 
-	# validate search params
-	foreach my $key (keys %{$params}) {
-		# if req args is a ref, assume it refers to a rel
-		# XXX this is broken when attempting complex search 
-		# XXX clauses on a col like { col => { LIKE => '%dfdsfs%' } }
-		# XXX when rel and col have the same name
-		next if $valid{'*'};
-		if (ref $params->{$key} && $_rel_map{$key}) {
-			$self->push_error($c, { message => "${key} is not a valid relation" }) unless (exists $_rel_map{$key});
-		} else {			
-			$self->push_error($c, { message => "${key} is not a valid column" }) unless exists $_col_map{$key};
-		}
-	}
-
 	# build up condition on root source
 	foreach my $column (@valid_cols) {
 		next unless (exists $params->{$column});
-		next if ($_rel_map{$column} && ref $params->{$column});
+		next if ($_rel_map{$column} && (ref $params->{$column} && !($params->{$column} == JSON::Any::true() || $params->{$column} == JSON::Any::false())));
 
 		if ($_source_col_map{$column}) {
 			$search_params{join('.', $base, $column)} = $params->{$column};
@@ -192,7 +241,23 @@
 	# it still is what they expect (and not inflating to a hash ref)
 	my $rs = $c->stash->{$self->rs_stash_key}->search;
 	$rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
-	$c->stash->{response}->{list} = [ $rs->all ];
+    try
+    {
+	    $c->stash->{response}->{$self->data_root} = [ $rs->all ];
+        # only add the totalcount to the response if also data is returned
+        if (my $totalcount = $c->stash->{_dbic_api}->{totalcount}) {
+            # numify which is important for JSON
+            $totalcount += 0;
+            $c->stash->{response}->{totalcount} = $totalcount;
+        }
+    }
+    catch
+    {
+        $c->log->error($_);
+        # send a generic error to the client to not give out infos about
+        # the database schema
+        $self->push_error($c, { message => 'a database error has occured.' });
+    }
 }
 
 sub create :Private {
@@ -207,15 +272,13 @@
 
 	my $empty_object = $c->stash->{$self->rs_stash_key}->new_result({});
 	$c->stash->{created_object} = $self->validate_and_save_object($c, $empty_object);
+    %{$c->stash->{response}->{$self->data_root}} = $c->stash->{created_object}->get_inflated_columns
+        if $self->return_object;
 }
 
 sub update :Private {
 	my ($self, $c) = @_;
 
-	# expand params unless they have already been expanded
-	my $req_params = (grep { ref $_ } values %{$c->req->params}) ? $c->req->params : $self->expand_hash($c->req->params);
-
-	$c->req->params($req_params);
 	die "no object to update (looking at " . $self->object_stash_key . ")"
 		unless ( defined $c->stash->{$self->object_stash_key} );
 
@@ -226,9 +289,11 @@
 		die "class resultset not set";
 	}
 
-	#	use Data::Dumper; $c->log->debug(Dumper(\%create_args));
 	my $object = $c->stash->{$self->object_stash_key};
-	$self->validate_and_save_object($c, $object);
+	$object = $self->validate_and_save_object($c, $object);
+    %{$c->stash->{response}->{$self->data_root}} = $object->get_inflated_columns
+        if $self->return_object;
+
 }
 
 sub delete :Private {
@@ -246,7 +311,6 @@
 			if $c->debug;
 		return;
 	}
-
 	if ( $c->debug ) {
 		$c->log->debug("Saving object: $object");
 		$c->log->_dump( $params );
@@ -256,32 +320,18 @@
 
 sub validate {
 	my ($self, $c, $object) = @_;
-	my $params = $c->req->params;
+	my $params = $c->req->request_data();
 
 	my %values;
 	my %requires_map = map { $_ => 1 } @{($object->in_storage) ? [] : $c->stash->{create_requires} || $self->create_requires};
 	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)});
-	
+
 	foreach my $key (keys %allows_map) {
 		# check value defined if key required
 		my $allowed_fields = $allows_map{$key};
 		if (ref $allowed_fields) {
 			my $related_source = $object->result_source->related_source($key);
-			unless ($related_source) {
-				$self->push_error($c, { message => "${key} is not a valid relation" });
-				next;
-			}
-		   
 			my $related_params = $params->{$key};
-
-			# it's an error for $c->req->params->{$key} to be defined but not be an array
-			unless (ref $related_params) {
-				unless (!defined $related_params) {
-					$self->push_error($c, { message => "Value of ${key} must be a hash" });
-				}
-				next;
-			}
-			
 			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}) {
@@ -306,8 +356,9 @@
 			# TODO: do automatic col type checking here
 			
 			# check for multiple values
-			if (ref($value)) {
-				$self->push_error($c, { message => "Multiple values for '${key}'" });
+			if (ref($value) && !($value == JSON::Any::true || $value == JSON::Any::false)) {
+                require Data::Dumper;
+				$self->push_error($c, { message => "Multiple values for '${key}': ${\Data::Dumper::Dumper($value)}" });
 			}
 
 			# check exists so we don't just end up with hash of undefs
@@ -326,21 +377,33 @@
 
 sub save_object {
 	my ($self, $c, $object, $params) = @_;
-	
-	if ($object->in_storage) {
-		foreach my $key (keys %{$params}) {
-			if (ref $params->{$key}) {
-				my $related_params = delete $params->{$key};
-				my $row = $object->find_related($key, {} , {});
-				$row->update($related_params);
-			}
-		}
-		$object->update($params);
-	} else {
-		$object->set_columns($params);
-		$object->insert;
-	}
-	return $object;
+
+    try
+    {
+    	if ($object->in_storage) {
+    		foreach my $key (keys %{$params}) {
+                my $value = $params->{$key};
+    			if (ref($value) && !($value == JSON::Any::true || $value == JSON::Any::false)) {
+    				my $related_params = delete $params->{$key};
+    				my $row = $object->find_related($key, {} , {});
+    				$row->update($related_params);
+    			}
+    		}
+    		$object->update($params);
+    	} else {
+    		$object->set_columns($params);
+    		$object->insert;
+    	}
+    }
+    catch
+    {
+        $c->log->error($@);
+        # send a generic error to the client to not give out infos about
+        # the database schema
+        $self->push_error($c, { message => 'a database error has occured.' });
+    };
+    
+    return $object;
 }
 
 sub end :Private {
@@ -352,17 +415,17 @@
 	# Check for errors caught elsewhere
 	if ( $c->res->status and $c->res->status != 200 ) {
 		$default_status = $c->res->status;
-		$c->stash->{response}->{success} = 'false';
+		$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} = 'false';
+		$c->stash->{response}->{success} = $self->use_json_boolean ? JSON::Any::false : 'false';
 		$default_status = 400;
 	} else {
-		$c->stash->{response}->{success} = 'true';
+		$c->stash->{response}->{success} = $self->use_json_boolean ? JSON::Any::true : 'true';
 		$default_status = 200;
 	}
 	
-	delete $c->stash->{response}->{list} unless ($default_status == 200);
+	delete $c->stash->{response}->{$self->data_root} unless ($default_status == 200);
 	$c->res->status( $default_status || 200 );
 	$c->forward('serialize');
 }
@@ -390,6 +453,10 @@
 
 Luke Saunders <luke.saunders at gmail.com>
 
+=head1 LICENSE
+
+You may distribute this code under the same terms as Perl itself.
+
 =cut
 
 1;

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/REST.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/REST.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/REST.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -1,9 +1,8 @@
 package Catalyst::Controller::DBIC::API::REST;
+our $VERSION = '1.004000';
+use Moose;
+BEGIN { extends 'Catalyst::Controller::DBIC::API::Base'; }
 
-use strict;
-use warnings;
-use base qw/Catalyst::Controller::DBIC::API::Base/;
-
 __PACKAGE__->config(
 						'default'   => 'application/json',
 						'stash_key' => 'response',
@@ -74,16 +73,10 @@
 
 =cut 
 
-# from Catalyst::Action::Serialize
-sub deserialize :ActionClass('Deserialize') {
-	my ($self, $c) = @_;
-
-}
-
 sub object :Chained('setup') :Args(1) :PathPart('') :ActionClass('REST') {
 	my ($self, $c, $id) = @_;
 
-	my $object = $c->stash->{$self->rs_stash_key}->find( $id );
+	my $object = $self->stored_model->find( $id );
 	unless ($object) {
 		$self->push_error($c, { message => "Invalid id" });
 		$c->detach; # no point continuing

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/RPC.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/RPC.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/RPC.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -1,10 +1,8 @@
 package Catalyst::Controller::DBIC::API::RPC;
+our $VERSION = '1.004000';
+use Moose;
+BEGIN { extends 'Catalyst::Controller::DBIC::API::Base'; }
 
-use strict;
-use warnings;
-use base qw/Catalyst::Controller::DBIC::API::Base/;
-use JSON::Syck;
-
 __PACKAGE__->config(
 						'default'   => 'application/json',
 						'stash_key' => 'response',
@@ -86,12 +84,6 @@
 
 =cut 
 
-# from Catalyst::Action::Serialize
-sub deserialize :ActionClass('Deserialize') {
-	my ($self, $c) = @_;
-
-}
-
 sub object :Chained('setup') :CaptureArgs(1) :PathPart('id') {
 	my ($self, $c, $id) = @_;
 

Added: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Request.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Request.pm	                        (rev 0)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Request.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -0,0 +1,22 @@
+package Catalyst::Controller::DBIC::API::Request;
+our $VERSION = '1.004000';
+use Moose::Role;
+use MooseX::Aliases;
+use MooseX::Types::Moose('Object');
+use namespace::autoclean;
+
+### XXX Stupid hack to make role attribute handles work
+sub check_has_relation { }
+sub check_column_relation { }
+
+has 'application' =>
+(
+    is => 'ro',
+    writer => '_set_application',
+    isa => Object,
+    handles => 'Catalyst::Controller::DBIC::API::StoredResultSource',
+);
+
+with 'Catalyst::Controller::DBIC::API::RequestArguments';
+
+1;

Added: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm	                        (rev 0)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -0,0 +1,247 @@
+package Catalyst::Controller::DBIC::API::RequestArguments;
+our $VERSION = '1.004000';
+use MooseX::Role::Parameterized;
+use Catalyst::Controller::DBIC::API::Types(':all');
+use MooseX::Types::Moose(':all');
+use Data::Dumper;
+use namespace::autoclean;
+
+requires qw/check_has_relation check_column_relation/;
+
+with 'MooseX::Role::BuildInstanceOf' =>
+{
+    'target' => 'Catalyst::Controller::DBIC::API::Validator',
+    'prefix' => 'search_validator',
+};
+
+with 'MooseX::Role::BuildInstanceOf' =>
+{
+    'target' => 'Catalyst::Controller::DBIC::API::Validator',
+    'prefix' => 'select_validator',
+};
+
+with 'MooseX::Role::BuildInstanceOf' =>
+{
+    'target' => 'Catalyst::Controller::DBIC::API::Validator',
+    'prefix' => 'prefetch_validator',
+};
+
+parameter static => ( isa => Bool, default => 0 );
+
+role {
+    
+    my $p = shift;
+
+    has 'count' =>
+    (
+        is => 'ro',
+        writer => '_set_count',
+        isa => Int,
+        predicate => 'has_count',
+        traits => ['Aliased'],
+        alias => 'list_count'
+    );
+
+    has 'page' =>
+    (
+        is => 'ro',
+        writer => '_set_page',
+        isa => Int,
+        predicate => 'has_page',
+        traits => ['Aliased'],
+        alias => 'list_page'
+    );
+
+    has 'ordered_by' =>
+    (
+        is => 'ro',
+        writer => '_set_ordered_by',
+        isa => OrderedBy,
+        predicate => 'has_ordered_by',
+        traits => ['Aliased'],
+        coerce => 1,
+        default => sub { $p->static ? [] : undef },
+        alias => 'list_ordered_by',
+    );
+
+    has 'grouped_by' =>
+    (
+        is => 'ro',
+        writer => '_set_grouped_by',
+        isa => GroupedBy,
+        predicate => 'has_grouped_by',
+        traits => ['Aliased'],
+        coerce => 1,
+        default => sub { $p->static ? [] : undef },
+        alias => 'list_grouped_by',
+    );
+
+    has prefetch =>
+    (
+        is => 'ro',
+        writer => '_set_prefetch',
+        isa => Prefetch, 
+        default => sub { $p->static ? [] : undef },
+        coerce => 1,
+        trigger => sub
+        {
+            my ($self, $new) = @_;
+            if($self->has_prefetch_allows and @{$self->prefetch_allows})
+            {
+                foreach my $pf (@$new)
+                {
+                    if(HashRef->check($pf))
+                    {
+                        die qq|'${\Dumper($pf)}' is not an allowd prefetch in: ${\join("\n", @{$self->prefetch_validator->templates})}|
+                            unless $self->prefetch_validator->validate($pf)->[0];
+                    }
+                    else
+                    {
+                        die qq|'$pf' is not an allowed prefetch in: ${\join("\n", @{$self->prefetch_validator->templates})}|
+                            unless $self->prefetch_validator->validate({$pf => 1})->[0];
+                    }
+                }
+            }
+            else
+            {
+                return if not defined($new);
+                die 'Prefetching is not allowed' if @$new;
+            }
+        },
+        traits => ['Aliased'],
+        alias => 'list_prefetch',
+    );
+
+    has prefetch_allows =>
+    (
+        is => 'ro',
+        writer => '_set_prefetch_allows',
+        isa => ArrayRef[ArrayRef|Str|HashRef], 
+        default => sub { [ ] },
+        predicate => 'has_prefetch_allows',
+        trigger => sub
+        {
+            my ($self, $new) = @_;
+            foreach my $rel (@$new)
+            {
+                if(ArrayRef->check($rel))
+                {
+                    foreach my $rel_sub (@$rel)
+                    {
+                        $self->check_has_relation($rel_sub, undef, undef, $p->static);
+                        $self->prefetch_validator->load($rel_sub);
+                    }
+                }
+                elsif(HashRef->check($rel))
+                {
+                    $self->check_has_relation(%$rel, undef, $p->static);
+                    $self->prefetch_validator->load($rel);
+                }
+                else
+                {
+                    $self->check_has_relation($rel, undef, undef, $p->static);
+                    $self->prefetch_validator->load($rel);
+                }
+            }
+        },
+        traits => ['Aliased'],
+        alias => 'list_prefetch_allows',
+    );
+
+    has 'search_exposes' =>
+    (
+        is => 'ro',
+        writer => '_set_search_exposes',
+        isa => ArrayRef[Str|HashRef],
+        predicate => 'has_search_exposes',
+        traits => ['Aliased'],
+        default => sub { [ ] },
+        alias => 'list_search_exposes',
+        trigger => sub
+        {
+            my ($self, $new) = @_;
+            $self->search_validator->load($_) for @$new;
+        },
+    );
+
+    has 'search' =>
+    (
+        is => 'ro',
+        writer => '_set_search',
+        isa => HashRef,
+        predicate => 'has_search',
+        trigger => sub
+        {
+            my ($self, $new) = @_;
+            
+            if($self->has_search_exposes and @{$self->search_exposes})
+            {
+                while( my ($k, $v) = each %$new)
+                {
+                    local $Data::Dumper::Terse = 1;
+                    die qq|{ $k => ${\Dumper($v)} } is not an allowed search term in: ${\join("\n", @{$self->search_validator->templates})}|
+                        unless $self->search_validator->validate({$k=>$v})->[0];
+                }
+            }
+            else
+            {
+                while( my ($k, $v) = each %$new)
+                {
+                    $self->check_column_relation({$k => $v});
+                }
+            }
+        },
+    );
+
+    has 'select_exposes' =>
+    (
+        is => 'ro',
+        writer => '_set_select_exposes',
+        isa => ArrayRef[Str|HashRef],
+        predicate => 'has_select_exposes',
+        default => sub { [ ] },
+        traits => ['Aliased'],
+        alias => 'list_returns_exposes',
+        trigger => sub
+        {
+            my ($self, $new) = @_;
+            $self->select_validator->load($_) for @$new;
+        },
+    );
+
+    has select =>
+    (
+        is => 'ro',
+        writer => '_set_select',
+        isa => SelectColumns,
+        default => sub { $p->static ? [] : undef },
+        traits => ['Aliased'],
+        alias => 'list_returns',
+        coerce => 1,
+        trigger => sub
+        {   
+            my ($self, $new) = @_;
+            if($self->has_select_exposes)
+            {
+                foreach my $val (@$new)
+                {
+                    die "'$val' is not allowed in a select"
+                        unless $self->select_validator->validate($val);
+                }
+            }
+            else
+            {
+                $self->check_column_relation($_, $p->static) for @$new;
+            }
+        },
+    );
+
+    has 'request_data' =>
+    (
+        is => 'ro',
+        writer => '_set_request_data',
+        isa => HashRef,
+    );
+};
+
+1;

Added: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm	                        (rev 0)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/StaticArguments.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -0,0 +1,48 @@
+package Catalyst::Controller::DBIC::API::StaticArguments;
+our $VERSION = '1.004000';
+use Moose::Role;
+use MooseX::Types::Moose(':all');
+use namespace::autoclean;
+
+requires 'check_column_relation';
+
+foreach my $var (qw/create_requires create_allows update_requires update_allows/)
+{
+    has $var =>
+    (
+        is => 'ro',
+        isa => ArrayRef[Str|HashRef],
+        traits => ['Array'],
+        default => sub { [] },
+        trigger => sub
+        {   
+            my ($self, $new) = @_;
+            $self->check_column_relation($_, 1) for @$new;
+        },
+        handles =>
+        {
+            "get_${var}_column" => 'get',
+            "set_${var}_column" => 'set',
+            "delete_${var}_column" => 'delete',
+            "insert_${var}_column" => 'insert',
+            "count_${var}_column" => 'count',
+            "all_${var}_columns" => 'elements',
+        }
+    );
+
+    before "set_${var}_column" => sub { $_[0]->check_column_relation($_[2], 1) }; #"
+    before "insert_${var}_column" => sub { $_[0]->check_column_relation($_[2], 1) }; #"
+}
+
+has 'count_arg' => ( is => 'ro', isa => Str, default => 'list_count' );
+has 'page_arg' => ( is => 'ro', isa => Str, default => 'list_page' );
+has 'select_arg' => ( is => 'ro', isa => Str, default => 'list_returns' );
+has 'search_arg' => ( is => 'ro', isa => Str, default => 'search' );
+has 'grouped_by_arg' => ( is => 'ro', isa => Str, default => 'list_grouped_by' );
+has 'ordered_by_arg' => ( is => 'ro', isa => Str, default => 'list_ordered_by' );
+has 'prefetch_arg' => ( is => 'ro', isa => Str, default => 'list_prefetch' );
+has 'data_root' => ( is => 'ro', isa => Str, default => 'list');
+has 'use_json_boolean' => ( is => 'ro', isa => Bool, default => 0 );
+has 'return_object' => ( is => 'ro', isa => Bool, default => 0 );
+
+1;

Added: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/StoredResultSource.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/StoredResultSource.pm	                        (rev 0)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/StoredResultSource.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -0,0 +1,87 @@
+package Catalyst::Controller::DBIC::API::StoredResultSource;
+our $VERSION = '1.004000';
+use Moose::Role;
+use Moose::Util::TypeConstraints;
+use MooseX::Types::Moose(':all');
+use Try::Tiny;
+use namespace::autoclean;
+
+requires '_application';
+
+has 'class' => ( is => 'ro', isa => Str );
+
+has 'stored_result_source' =>
+(
+    is => 'ro',
+    isa => class_type('DBIx::Class::ResultSource'),
+    lazy_build => 1,
+);
+
+has 'stored_model' =>
+(
+    is => 'ro',
+    isa => class_type('DBIx::Class'),
+    lazy_build => 1,
+);
+
+sub _build_stored_model
+{   
+    return $_[0]->_application->model($_[0]->class);
+}
+
+sub _build_stored_result_source
+{
+    return shift->stored_model->result_source();
+}
+
+sub check_has_column
+{
+    my ($self, $col) = @_;
+    confess "Column '$col' does not exist in ResultSet '${\$self->class}'"
+        unless $self->stored_result_source->has_column($col);
+}
+
+sub check_has_relation
+{
+    my ($self, $rel, $other, $nest, $static) = @_;
+    
+    $nest ||= $self->stored_result_source;
+
+    if(HashRef->check($other))
+    {
+        my $rel_src = $nest->related_source($rel);
+        die "Relation '$rel_src' does not exist" if not defined($rel_src);
+        return $self->check_has_relation(%$other, $rel_src, $static);
+    }
+    else
+    {
+        return 1 if $static && ArrayRef->check($other) && $other->[0] eq '*';
+        die "Relation '$rel' does not exist in ${\ref($nest)}"
+            unless $nest->has_relationship($rel) || $nest->has_column($rel);
+        return 1;
+    }
+}
+
+sub check_column_relation
+{
+    my ($self, $col_rel, $static) = @_;
+    
+    if(HashRef->check($col_rel))
+    {
+        try
+        {
+            $self->check_has_relation(%$col_rel, undef, $static);
+        }
+        catch
+        {
+            # not a relation but a column with a predicate
+            $self->check_has_column(keys %$col_rel);
+        }
+    }
+    else
+    {
+        $self->check_has_column($col_rel);
+    }
+}
+
+1;

Added: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Types.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Types.pm	                        (rev 0)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Types.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -0,0 +1,22 @@
+package Catalyst::Controller::DBIC::API::Types;
+our $VERSION = '1.004000';
+
+use warnings;
+use strict;
+
+use MooseX::Types -declare => [qw/OrderedBy GroupedBy Prefetch SelectColumns/];
+use MooseX::Types::Moose(':all');
+
+subtype Prefetch, as Maybe[ArrayRef[Str|HashRef]];
+coerce Prefetch, from Str, via { [$_] }, from HashRef, via { [$_] };
+
+subtype GroupedBy, as Maybe[ArrayRef[Str]];
+coerce GroupedBy, from Str, via { [$_] };
+
+subtype OrderedBy, as Maybe[ArrayRef[Str|HashRef|ScalarRef]];
+coerce OrderedBy, from Str, via { [$_] };
+
+subtype SelectColumns, as Maybe[ArrayRef[Str|HashRef]];
+coerce SelectColumns, from Str, via { [$_] };
+
+1;

Added: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Validator.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Validator.pm	                        (rev 0)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API/Validator.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -0,0 +1,110 @@
+package Catalyst::Controller::DBIC::API::Visitor;
+our $VERSION = '1.004000';
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Data::DPath::Validator::Visitor'; }
+
+use constant DEBUG => $ENV{DATA_DPATH_VALIDATOR_DEBUG} || 0;
+
+around visit_array => sub
+{
+    my ($orig, $self, $array) = @_;
+    $self->dive();
+    warn 'ARRAY: '. $self->current_template if DEBUG;
+    if(@$array == 1 && $array->[0] eq '*')
+    {
+        $self->append_text('[reftype eq "HASH" ]');
+        $self->add_template($self->current_template);
+    }
+    else
+    {
+        if($self->current_template =~ /\/$/)
+        {
+            my $temp = $self->current_template;
+            $self->reset_template();
+            $temp =~ s/\/$//;
+            $self->append_text($temp);
+        }
+        $self->$orig($array);
+    }
+    $self->rise();
+};
+
+sub visit_array_entry
+{
+    my ($self, $elem, $index, $array) = @_;
+    $self->dive();
+    warn 'ARRAYENTRY: '. $self->current_template if DEBUG;
+    if(!ref($elem))
+    {
+        $self->append_text($elem . '/*');
+        $self->add_template($self->current_template);
+    }
+    elsif(ref($elem) eq 'HASH')
+    {
+        $self->visit($elem);
+    }
+    $self->rise();
+    $self->value_type('NONE');
+};
+
+around visit_hash => sub
+{
+    my ($orig, $self, $hash) = @_;
+    $self->dive();
+    if($self->current_template =~ /\/$/)
+    {
+        my $temp = $self->current_template;
+        $self->reset_template();
+        $temp =~ s/\/$//;
+        $self->append_text($temp);
+    }
+    warn 'HASH: '. $self->current_template if DEBUG;
+    $self->$orig($hash);
+    $self->rise();
+};
+
+around visit_value => sub
+{
+    my ($orig, $self, $val) = @_;
+    
+    if($self->value_type eq 'NONE')
+    {
+        $self->dive();
+        $self->append_text($val . '/*');
+        $self->add_template($self->current_template);
+        warn 'VALUE: ' . $self->current_template if DEBUG;
+        $self->rise();
+    }
+    elsif($self->value_type eq 'HashKey')
+    {
+        $self->append_text($val);
+        warn 'VALUE: ' . $self->current_template if DEBUG;
+    }    
+    else
+    {
+        $self->$orig($val);
+    }
+
+};
+
+
+Catalyst::Controller::DBIC::API::Visitor->meta->make_immutable;
+
+package Catalyst::Controller::DBIC::API::Validator;
+our $VERSION = '1.004000';
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Data::DPath::Validator'; }
+
+has '+visitor' => ( 'builder' => '_build_custom_visitor' );
+
+sub _build_custom_visitor
+{
+    return Catalyst::Controller::DBIC::API::Visitor->new();
+}
+
+Catalyst::Controller::DBIC::API::Validator->meta->make_immutable;
+1;

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/lib/Catalyst/Controller/DBIC/API.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -5,11 +5,11 @@
 
 =head1 VERSION
 
-Version 1.002000
+Version 1.004
 
 =cut
 
-our $VERSION = '1.002001';
+our $VERSION = '1.004000';
 
 =head1 NAME
 
@@ -27,9 +27,19 @@
       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
-      list_returns => [qw/name age/], # columns that list returns
-      list_ordered_by => [qw/age/], # order of generated list
-      list_search_exposes => [qw/age nickname/, { cd => [qw/title year/] }], # columns that can be searched on via list
+      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
+          'cds',
+          qw/ cds /,
+          { cds => 'tracks' },
+          { cds => [qw/ 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
       );
 
   # Provides the following functional endpoints:
@@ -68,15 +78,29 @@
       return;
     }
 
-    $self->NEXT::setup($c);
+    $self->next::method($c);
   }
 
 Generally it's better to have one controller for each DBIC source with the config hardcoded, but in some cases this isn't possible.
 
+Note that the Chained, CaptureArgs and PathPart are just standard Catalyst configuration parameters and that then endpoint specified in Chained - in this case '/api/rpc/rpc_base' - must actually exist elsewhere in your application. See L<Catalyst::DispatchType::Chained> for more details.
+
 =head2 class
 
 Whatever you would pass to $c->model to get a resultset for this class. MyAppDB::Track for example.
 
+head2 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.
+
+head2 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.
+
+head2 count_arg, page_arg, select_arg, search_arg, grouped_by_arg, ordered_by_arg, prefetch_arg
+
+These attributes allow customization of the component to understand requests made by clients where these argument names are not flexible and cannot conform to this components defaults.
+
 =head2 create_requires
 
 Arrayref listing columns required to be passed to create in order for the request to be valid.
@@ -89,25 +113,42 @@
 
 Arrayref listing columns that update will allow. Columns passed to update that are not listed here will be ignored.
 
-=head2 list_returns
+=head2 select
 
 Arguments to pass to L<DBIx::Class::ResultSet/select> when performing search for L</list>.
 
-=head2 list_grouped_by
+=head2 select_exposes
 
+Columns and related columns that are okay to return in the resultset since clients can request more or less information specified than the above select argument.
+
+=head2 prefetch
+
+Arguments to pass to L<DBIx::Class::ResultSet/prefetch> when performing search for L</list>.
+
+=head2 prefetch_allows
+
+Arrayref listing relationships that are allowed to be prefetched.
+This is necessary to avoid denial of service attacks in form of
+queries which would return a large number of data
+and unwanted disclosure of data.
+Every element of the arrayref is one allowed parameter to prefetch.
+So for three searches, all requiring different prefetch parameters,
+three elements have to be passed to list_prefetch_allows in the controller.
+
+=head2 grouped_by
+
 Arguments to pass to L<DBIx::Class::ResultSet/group_by> when performing search for L</list>.
 
-=head2 list_ordered_by
+=head2 ordered_by
 
 Arguments to pass to L<DBIx::Class::ResultSet/order_by> when performing search for L</list>.
 
-=head2 list_search_exposes
+=head2 search_exposes
 
 Columns and related columns that are okay to search on. For example if only the position column and all cd columns were to be allowed
 
- list_search_exposes => [qw/position/, { cd => ['*'] }]
+ search_exposes => [qw/position/, { cd => ['*'] }]
 
-
 You can also use this to allow custom columns should you wish to allow them through in order to be caught by a custom resultset. For example:
 
   package RestTest::Controller::API::RPC::TrackExposed;
@@ -136,11 +177,11 @@
     my $rs = $self->SUPER::search(@_);
   }
 
-=head2 list_count
+=head2 count
 
 Arguments to pass to L<DBIx::Class::ResultSet/rows> when performing search for L</list>.
 
-=head2 list_page
+=head2 page
 
 Arguments to pass to L<DBIx::Class::ResultSet/rows> when performing search for L</list>.
 
@@ -193,6 +234,10 @@
 
 Note: see the individual interface classes - L<Catalyst::Controller::DBIC::API::RPC> and L<Catalyst::Controller::DBIC::API::REST> - for details of the endpoints to these abstract methods.
 
+=head2 begin
+
+A begin method is provided to apply the L<Catalyst::Controller::DBIC::API::Request> role to $c->request, and perform deserialization and validation of request parameters
+
 =head2 setup
 
 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
@@ -210,7 +255,7 @@
   sub setup :Chained('/api/rpc_base') :CaptureArgs(0) :PathPart('track') {
     my ($self, $c) = @_;
 
-    $self->NEXT::setup($c);
+    $self->next::method($c);
   }
 
 This action will populate $c->stash->{$self->rs_stash_key} with $c->model($self->class) for other actions in the chain to use.
@@ -223,11 +268,11 @@
 
 List level action chained from L</setup>. Checks $c->req->params for each column specified in the L</create_requires> and L</create_allows> parameters of the controller config. If all of the required columns are present then the object is created.
 
-Does not populate the response with any additional information.
+Does not populate the response with any additional information unless the return_object option is set to true, then the created object will be serialized within $c->stash->{response}->{$self->data_root}.
 
 =head2 list
 
-List level action chained from L</setup>. By default populates $c->stash->{response}->{list} with a list of hashrefs representing each object in the class resultset. If the L</list_returns> config param is defined then the hashes will contain only those columns, otherwise all columns in the object will be returned. Similarly L</list_count>, L</list_page>, L</list_grouped_by> and L</list_ordered_by> affect the maximum number of rows returned as well as the ordering and grouping. Note that if list_returns, list_count, list_ordered_by or list_grouped_by request parameters are present then these will override the values set on the class.
+List level action chained from L</setup>. By default populates $c->stash->{response}->{$self->data_root} with a list of hashrefs representing each object in the class resultset. 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. 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.
 
 If not all objects in the resultset are required then it's possible to pass conditions to the method as request parameters. You can use a JSON string as the 'search' parameter for maximum flexibility or use L</CGI::Expand> syntax. In the second case the request parameters are expanded into a structure and then $c->req->params->{search} is used as the search condition.
 
@@ -243,7 +288,7 @@
 
 Note that if pagination is needed, this can be achieved using a combination of the L</list_count> and L</list_page> parameters. For example:
 
-  ?list_page=2&list_count=20
+  ?page=2&count=20
 
 Would result in this search:
  
@@ -253,13 +298,13 @@
 
 =head2 format_list
 
-Used by L</list> to populate response based on class resultset. By default populates $c->stash->{response}->{list} with a list of hashrefs representing each object in the resultset. Can be overidden to format the list as required.
+Used by L</list> to populate response based on class resultset. By default populates $c->stash->{response}->{$self->data_root} with a list of hashrefs representing each object in the resultset. Can be overidden to format the list as required.
 
 =head2 update
 
 Object level action chained from L</object>. Checks $c->req->params for each column specified in the L</update_allows> parameter of the controller config. If any of these columns are found in $c->req->params then the object set by L</object> is updated with those columns.
 
-Does not populate the response with any additional information.
+Does not populate the response with any additional information uness the return_object option is set to true, then the updated object will be serialized within $c->stash->{response}->{$self->data_root}.
 
 =head2 delete
 
@@ -269,23 +314,26 @@
 
 =head2 end
 
-If the request was successful then $c->stash->{response}->{success} is set to 1, if not then it is set to 0 and $c->stash->{response}->{messages} set to an arrayref containing all error messages.
+$c->stash->{response}->{success} is set to 'true' or 'false' (or their respective JSON::Any values for true and false) regarding the success of the request. If the request failed, $c->stash->{response}->{messages} is set to an arrayref containing all error messages.
 
 Then the contents of $c->stash->{response} are serialized using L<Catalyst::Action::Serialize>.
 
 =head1 EXTENDING
 
-By default the create, delete and update actions will not return anything apart from the success parameter set in L</end>, often this is not ideal but the required behaviour varies from application to application. So normally it's sensible to write an intermediate class which your main controller classes subclass from. For example if you wanted create to return the JSON for the newly created object you might have something like:
+By default the create, delete and update actions will not return anything apart from the success parameter set in L</end>, often this is not ideal but the required behaviour varies from application to application. So normally it's sensible to write an intermediate class which your main controller classes subclass from.
 
+For example if you wanted create to return the JSON for the newly created object you might have something like:
+
   package MyApp::ControllerBase::DBIC::API::RPC;
   ...
-  use base qw/Catalyst::Controller::DBIC::API::RPC/;
+  use Moose;
+  BEGIN { extends 'Catalyst::Controller::DBIC::API::RPC' };
   ...
   sub create :Chained('setup') :Args(0) :PathPart('create') {
     my ($self, $c) = @_;
 
     # will set $c->stash->{created_object} if successful
-    $self->NEXT::create($c);
+    $self->next::method($c);
 
     if ($c->stash->{created_object}) {    
       # $c->stash->{response} will be serialized in the end action
@@ -296,24 +344,41 @@
 
   package MyApp::Controller::API::RPC::Track;
   ...
-  use base qw/MyApp::ControllerBase::DBIC::API::RPC/;
+  use Moose;
+  BEGIN { extends 'MyApp::ControllerBase::DBIC::API::RPC' };
   ...
 
-If you were using the RPC style. For REST the only difference besides the class names would be that create should be :Private rather than an endpoint.
+It should be noted that the L</return_object> attribute will produce the above result for you, free of charge.
 
+For REST the only difference besides the class names would be that create should be :Private rather than an endpoint.
+
 Similarly you might want create, update and delete to all forward to the list action once they are done so you can refresh your view. This should also be simple enough.
 
+If more extensive customization is required, it is recommened to peer into the roles that comprise the system and make use 
+
+=head1 NOTES
+
+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.
+
+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>.
+
+All in all, significant efforts have been made to preserve backwards compatibility with previous versions. This means arguments to config and even internal structures (ie, the stash) should Just Work. This is accomplished by using L<MooseX::Aliases> to provide a mapping from old names to new names. Even the validator behavior /should/ be the same if not a bit more consistent. Internal validation of ->config arguments also happens much, much sooner. And, request parameters are validated as upfront as possible before ->search.
+
 =head1 AUTHOR
 
   Luke Saunders <luke.saunders at gmail.com>
 
 =head1 CONTRIBUTORS
 
+  Nicholas Perez <nperez at cpan.org>
+
   J. Shirley <jshirley at gmail.com>
 
   Zbigniew Lukasiak <zzbbyy at gmail.com>
 
-  Alexander Hartmaier <alex_hartmaier at hotmail.com>
+  Alexander Hartmaier <abraxxa at cpan.org>
 
 =head1 SPECIAL THANKS
 

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/REST/Artist.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Artist.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/REST/Artist.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,8 @@
       class => 'RestTestDB::Artist',
       create_requires => ['name'],
       create_allows => ['name'],
-      update_allows => ['name']
+      update_allows => ['name'],
+      list_prefetch_allows => [[qw/ cds /],{ 'cds' => 'tracks'}],
       );
 
 1;

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/REST/Producer.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Producer.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/REST/Producer.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -15,7 +15,7 @@
 
 sub create :Private {
   my ($self, $c) = @_;
-  $self->NEXT::create($c);
+  $self->next::method($c);
 
   if ($c->stash->{created_object}) {
     %{$c->stash->{response}->{new_producer}} = $c->stash->{created_object}->get_columns;

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/Artist.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Artist.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/Artist.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,8 @@
       class => 'RestTestDB::Artist',
       create_requires => ['name'],
       create_allows => ['name'],
-      update_allows => ['name']
+      update_allows => ['name'],
+      list_prefetch_allows => [[qw/ cds /],{ 'cds' => 'tracks'}],
       );
 
 1;

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/CD.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/CD.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/CD.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -1,15 +1,15 @@
 package RestTest::Controller::API::RPC::CD;
 
-use strict;
-use warnings;
-use base qw/Catalyst::Controller::DBIC::API::RPC/;
+use Moose;
+BEGIN { extends 'Catalyst::Controller::DBIC::API::RPC' }
 use JSON::Syck;
 
 __PACKAGE__->config
     ( action => { setup => { PathPart => 'cd', Chained => '/api/rpc/rpc_base' } },
       class => 'RestTestDB::CD',
       create_requires => ['artist', 'title', 'year' ],
-      update_allows => ['title', 'year']
+      update_allows => ['title', 'year'],
+      list_prefetch_allows => [[qw/ tracks /]],
       );
 
 1;

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/Producer.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Producer.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/Producer.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -9,13 +9,14 @@
     ( action => { setup => { PathPart => 'producer', Chained => '/api/rpc/rpc_base' } },
       class => 'RestTestDB::Producer',
       create_requires => ['name'],
+      create_allows => ['producerid'],
       update_allows => ['name'],
       list_returns => ['name']
       );
 
 sub create :Chained('setup') :Args(0) :PathPart('create') {
   my ($self, $c) = @_;
-  $self->NEXT::create($c);
+  $self->next::method($c);
 
   if ($c->stash->{created_object}) {
     %{$c->stash->{response}->{new_producer}} = $c->stash->{created_object}->get_columns;

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/TrackExposed.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/TrackExposed.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Controller/API/RPC/TrackExposed.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
       class => 'RestTestDB::Track',
       list_returns => [qw/position title/],
       list_ordered_by => [qw/position/],
-      list_search_exposes => [qw/position/, { cd => [qw/title year pretend/, { artist => ['*'] }] }],
+      list_search_exposes => [qw/position/, { cd => [qw/title year pretend/, { 'artist' => ['*'] } ]}],
       );
 
 1;

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Schema/Result/Track.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/Result/Track.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Schema/Result/Track.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -14,7 +14,7 @@
   'position' => {
     data_type => 'integer',
     accessor => 'pos',
-	default_value => 0
+		default_value => 0
   },
   'title' => {
     data_type => 'varchar',

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Schema/ResultSet/Track.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/ResultSet/Track.pm	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/lib/RestTest/Schema/ResultSet/Track.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -7,10 +7,12 @@
 	my $self = shift;
 	my ($clause, $params) = @_;
 
-	# test custom attrs
-	if (my $pretend = delete $clause->{pretend}) {
-		$clause->{'cd.year'} = $pretend;
-	}
+  if (ref $clause eq 'HASH') {
+    # test custom attrs
+    if (my $pretend = delete $clause->{pretend}) {
+      $clause->{'cd.year'} = $pretend;
+    }
+  }
   my $rs = $self->SUPER::search(@_);	
 }
 

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rest/create.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rest/create.t	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rest/create.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -61,9 +61,8 @@
 						 );
 	$req->content( $test_data );
 	$mech->request($req);
-
 	cmp_ok( $mech->status, '==', 200, 'request with valid content okay' );
-        my $new_obj = $schema->resultset('Producer')->find({ name => 'king luke' });
+	my $new_obj = $schema->resultset('Producer')->find({ name => 'king luke' });
 	ok($new_obj, 'record created with specified name');
 
 	my $response = JSON::Syck::Load( $mech->content);

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rest/list.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rest/list.t	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rest/list.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -78,7 +78,6 @@
 
   my @expected_response = map { { $_->get_columns } } $schema->resultset('Artist')->search({ 'cds.title' => 'Forkful of bees' }, { join => 'cds' })->all;
   my $response = JSON::Syck::Load( $mech->content);
-#  use Data::Dumper; warn Dumper($response, \@expected_response);
   is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned for class with list_returns specified' );
 }
 

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/create.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/create.t	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/create.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 
 use RestTest;
 use DBICTest;
-use Test::More tests => 11;
+use Test::More tests => 12;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -73,3 +73,15 @@
   my $response = JSON::Syck::Load( $mech->content);
   is_deeply( $response, { success => 'true' }, 'json for new artist returned' );
 }
+
+# test create returns an error as expected when passing invalid value
+{
+  my $long_string = '-' x 1024;
+
+  my $req = POST( $producer_create_url, {
+	  producerid => $long_string,
+      name       => $long_string,
+  }, 'Accept' => 'text/json' );
+  $mech->request($req, $content_type);
+  cmp_ok( $mech->status, '==', 400, 'invalid param value produces error' );
+}

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list.t	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 use RestTest;
 use DBICTest;
 use URI;
-use Test::More qw(no_plan);
+use Test::More;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -88,7 +88,7 @@
 
 {
 	my $uri = URI->new( $track_list_url );
-	$uri->query_form({ 'order_by' => 'position' });	
+	$uri->query_form({ 'list_ordered_by' => 'position' });	
 	my $req = GET( $uri, 'Accept' => 'text/x-json' );
 	$mech->request($req);
 	cmp_ok( $mech->status, '==', 200, 'search related request okay' );
@@ -132,7 +132,9 @@
 	$mech->request($req);
 	cmp_ok( $mech->status, '==', 400, 'non numeric list_page request not okay' );
 	my $response = JSON::Syck::Load( $mech->content);
-	is_deeply({ success => 'false', messages => ["list_page must be numeric"]}, $response, 'correct data returned' );
+	#  use Data::Dumper; warn Dumper($response);
+	is($response->{success}, 'false', 'correct data returned');
+    like($response->{messages}->[0], qr/Attribute \(page\) does not pass the type constraint because: Validation failed for 'Int' failed with value fgdg/, 'correct data returned');
 }
 
 {
@@ -142,7 +144,9 @@
 	$mech->request($req);
 	cmp_ok( $mech->status, '==', 400, 'non numeric list_count request not okay' );
 	my $response = JSON::Syck::Load( $mech->content);
-	is_deeply({ success => 'false', messages => ["list_count must be numeric"]}, $response, 'correct data returned' );
+	is($response->{success}, 'false', 'correct data returned');
+    like($response->{messages}->[0], qr/Attribute \(count\) does not pass the type constraint because: Validation failed for 'Int' failed with value sdsdf/, 'correct data returned');
+    
 }
 
 {
@@ -155,7 +159,7 @@
 	my @expected_response = map { { $_->get_columns } } $schema->resultset('Track')->search({}, { group_by => 'position', order_by => 'position ASC', select => 'position', rows => 2, page => 2 })->all;
 	my $response = JSON::Syck::Load( $mech->content);
 	#  use Data::Dumper; warn Dumper($response, \@expected_response);
-	is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned' );
+    is_deeply( { list => \@expected_response, success => 'true', totalcount => 3 }, $response, 'correct data returned' );
 }
 
 {
@@ -197,26 +201,39 @@
 
 {
 	my $uri = URI->new( $cd_list_url );
-	$uri->query_form({ 'search.artist.artistid' => 1 });
+	$uri->query_form({ 'search.title' => 'Spoonful of bees', 'search.tracks.position' => 1 });
 	my $req = GET( $uri, 'Accept' => 'text/x-json' );
 	$mech->request($req);
-	cmp_ok( $mech->status, '==', 200, 'search on rel column with same name fk request okay' );
+	if (cmp_ok( $mech->status, '==', 200, 'search on col which exists for me and related table okay' )) {
+		my @expected_response = map { { $_->get_columns } } $schema->resultset('CD')->search({'me.title' => 'Spoonful of bees', 'tracks.position' => 1}, { join => 'tracks' })->all;
+		my $response = JSON::Syck::Load( $mech->content);
+		#use Data::Dumper; warn Dumper($response, \@expected_response);
+		is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned for search on col which exists for me and related table' );
+	}
+}
 
-	my @expected_response = map { { $_->get_columns } } $schema->resultset('CD')->search({'artist.artistid' => 1}, { join => 'artist' })->all;
-	my $response = JSON::Syck::Load( $mech->content);
-	#use Data::Dumper; warn Dumper($response, \@expected_response);
-	is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned for search on rel column with same name rel fk' );
+{
+	my $uri = URI->new( $cd_list_url );
+	$uri->query_form({ 'list_ordered_by' => 'invalid_column' });
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	if (cmp_ok( $mech->status, '==', 400, 'order_by on non-existing col returns error' )) {
+		my $response = JSON::Syck::Load( $mech->content);
+		is_deeply( $response, { messages => ['a database error has occured.'], success => 'false' },
+            'error returned for order_by on non-existing col' );
+	}
 }
 
 {
 	my $uri = URI->new( $cd_list_url );
-	$uri->query_form({ 'search.title' => 'Spoonful of bees', 'search.tracks.position' => 1 });
+	$uri->query_form({ 'list_ordered_by' => 'invalid_column', 'list_count' => 2, 'list_page' => 1 });
 	my $req = GET( $uri, 'Accept' => 'text/x-json' );
 	$mech->request($req);
-	if (cmp_ok( $mech->status, '==', 200, 'search on col which exists for me and related table okay' )) {
-		my @expected_response = map { { $_->get_columns } } $schema->resultset('CD')->search({'me.title' => 'Spoonful of bees', 'tracks.position' => 1}, { join => 'tracks' })->all;
+	if (cmp_ok( $mech->status, '==', 400, 'order_by on invalid col with paging returns error' )) {
 		my $response = JSON::Syck::Load( $mech->content);
-		#use Data::Dumper; warn Dumper($response, \@expected_response);
-		is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned for search on col which exists for me and related table' );
+		is_deeply( $response, { messages => ['a database error has occured.'], success => 'false' },
+            'error returned for order_by on non-existing col with paging' );
 	}
 }
+
+done_testing();

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_json_search.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_json_search.t	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_json_search.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 use RestTest;
 use DBICTest;
 use URI;
-use Test::More qw(no_plan);
+use Test::More;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -27,12 +27,11 @@
 	my $req = GET( $uri, 'Accept' => 'text/x-json' );
 	$mech->request($req);
 	cmp_ok( $mech->status, '==', 400, 'attempt with gibberish json not okay' );
-	
 	my $response = JSON::Syck::Load( $mech->content);
-	is_deeply( { messages => ['can not parse search arg'], success => 'false' }, $response, 'correct data returned for gibberish in search' );
+    is($response->{success}, 'false', 'correct data returned for gibberish in search' );
+	like($response->{messages}->[0], qr/Attribute \(search\) does not pass the type constraint because: Validation failed for 'HashRef' failed with value \{"gibberish\}/, 'correct data returned for gibberish in search' );
 }
 
-
 {
 	my $uri = URI->new( $artist_list_url );
 	$uri->query_form({ 'search' => '{"name":{"LIKE":"%waul%"}}' });
@@ -55,3 +54,5 @@
 	my $response = JSON::Syck::Load( $mech->content);
 	is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned for complex query' );
 }
+
+done_testing();

Added: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_prefetch.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_prefetch.t	                        (rev 0)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_prefetch.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -0,0 +1,78 @@
+use 5.6.0;
+
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+my $base = 'http://localhost';
+
+use RestTest;
+use DBICTest;
+use URI;
+use Test::More tests => 17;
+use Test::WWW::Mechanize::Catalyst 'RestTest';
+use HTTP::Request::Common;
+use JSON::Syck;
+
+my $mech = Test::WWW::Mechanize::Catalyst->new;
+ok(my $schema = DBICTest->init_schema(), 'got schema');
+
+my $artist_list_url = "$base/api/rpc/artist/list";
+my $cd_list_url = "$base/api/rpc/cd/list";
+
+foreach my $req_params ({ 'list_prefetch' => '["cds"]' }, { 'list_prefetch' => 'cds' }) {
+	my $uri = URI->new( $artist_list_url );
+	$uri->query_form($req_params);
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	cmp_ok( $mech->status, '==', 200, 'search with simple prefetch request okay' );
+	my $rs = $schema->resultset('Artist')->search(undef, { prefetch => ['cds'] });
+	$rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
+	my @rows = $rs->all;
+	my $expected_response = { list => \@rows, success => 'true' };
+	my $response = JSON::Syck::Load( $mech->content);
+	#use Data::Dumper; warn Dumper($response, $expected_response);
+	is_deeply( $expected_response, $response, 'correct data returned for search with simple prefetch specified as param' );
+}
+
+foreach my $req_params ({ 'list_prefetch' => '{"cds":"tracks"}' }, { 'list_prefetch.cds' => 'tracks' }) {
+	my $uri = URI->new( $artist_list_url );
+	$uri->query_form($req_params);
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	cmp_ok( $mech->status, '==', 200, 'search with multi-level prefetch request okay' );
+	my $rs = $schema->resultset('Artist')->search(undef, { prefetch => {'cds' => 'tracks'} });
+	$rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
+	my @rows = $rs->all;
+	my $expected_response = { list => \@rows, success => 'true' };
+	my $response = JSON::Syck::Load( $mech->content);
+	#use Data::Dumper; warn Dumper($response, $expected_response);
+	is_deeply( $expected_response, $response, 'correct data returned for search with multi-level prefetch specified as param' );
+}
+
+foreach my $req_params ({ 'list_prefetch' => '["artist"]' }, { 'list_prefetch' => 'artist' }) {
+	my $uri = URI->new( $cd_list_url );
+	$uri->query_form($req_params);	
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	cmp_ok( $mech->status, '==', 400, 'prefetch of artist not okay' );
+
+	my $expected_response = map { { $_->get_columns } } $schema->resultset('CD')->all;
+	my $response = JSON::Syck::Load( $mech->content);
+	#use Data::Dumper; warn Dumper($response, $expected_response);
+    is($response->{success}, 'false', 'correct message returned' );
+	like($response->{messages}->[0], qr/not an allowed prefetch in:/, 'correct message returned' );
+}
+
+{
+	my $uri = URI->new( $cd_list_url );
+	$uri->query_form({ 'list_prefetch' => 'tracks', 'list_ordered_by' => 'title', 'list_count' => 2, 'list_page' => 1 });
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	if (cmp_ok( $mech->status, '==', 400, 'order_by on non-unique col with paging returns error' )) {
+		my $response = JSON::Syck::Load( $mech->content);
+		is_deeply( $response, { messages => ['a database error has occured.'], success => 'false' },
+            'error returned for order_by on non-existing col with paging' );
+	}
+}

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_search_allows.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_search_allows.t	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/list_search_allows.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 use RestTest;
 use DBICTest;
 use URI;
-use Test::More qw(no_plan);
+use Test::More;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -31,6 +31,7 @@
 
   my @expected_response = map { { $_->get_columns } } $base_rs->all;
   my $response = JSON::Syck::Load( $mech->content);
+  #use Data::Dumper; warn Dumper($response, \@expected_response);
   is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct message returned' );
 }
 
@@ -44,6 +45,7 @@
   cmp_ok( $mech->status, '==', 200, 'search on position okay' );
   my @expected_response = map { { $_->get_columns } } $base_rs->search({ position => 1 })->all;
   my $response = JSON::Syck::Load( $mech->content);
+  #use Data::Dumper; warn Dumper($response, \@expected_response);
   is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct message returned' );
 }
 
@@ -56,9 +58,11 @@
   $mech->request($req);
   cmp_ok( $mech->status, '==', 400, 'search on title not okay' );
 
-  my $expected_response = map { { $_->get_columns } } $base_rs->search({ position => 1 })->all;
+  my @expected_response = map { { $_->get_columns } } $base_rs->search({ position => 1 })->all;
   my $response = JSON::Syck::Load( $mech->content);
-  is_deeply({ success => 'false',messages => ["title is not a valid column"]}, $response, 'correct message returned' );
+  #use Data::Dumper; warn Dumper($response, \@expected_response);
+  is($response->{success}, 'false', 'correct message returned');
+  like($response->{messages}->[0], qr/is not an allowed search term/, 'correct message returned');
 }
 
 {
@@ -72,7 +76,9 @@
 
   my $expected_response = map { { $_->get_columns } } $base_rs->search({ position => 1 })->all;
   my $response = JSON::Syck::Load( $mech->content);
-  is_deeply({ success => 'false',messages => ["title is not a valid column"]}, $response, 'correct message returned' );
+  #use Data::Dumper; warn Dumper($response);
+  is($response->{success}, 'false', 'correct message returned');
+  like($response->{messages}->[0], qr/is not an allowed search term/, 'correct message returned');
 }
 
 {
@@ -84,7 +90,9 @@
   $mech->request($req);
   cmp_ok( $mech->status, '==', 400, 'search on various cd fields not okay' );
   my $response = JSON::Syck::Load( $mech->content);
-  is_deeply({ success => 'false',messages => ["artist is not a valid column"]}, $response, 'correct message returned' );
+  #use Data::Dumper; warn Dumper($response);
+  is($response->{success}, 'false', 'correct message returned');
+  like($response->{messages}->[0], qr/is not an allowed search term/, 'correct message returned');
 }
 
 {
@@ -97,6 +105,7 @@
   cmp_ok( $mech->status, '==', 200, 'search on various cd fields okay' );
   my @expected_response = map { { $_->get_columns } } $base_rs->search({ 'cd.year' => '1999', 'cd.title' => 'Spoonful of bees' }, { join => 'cd' })->all;
   my $response = JSON::Syck::Load( $mech->content);
+  #use Data::Dumper; warn Dumper($response, \@expected_response);
   is_deeply({ success => 'true',list => \@expected_response }, $response, 'correct message returned' );
 }
 
@@ -110,6 +119,7 @@
   cmp_ok( $mech->status, '==', 200, 'search with custom col okay' );
   my @expected_response = map { { $_->get_columns } } $base_rs->search({ 'cd.year' => '1999', 'cd.title' => 'Spoonful of bees' }, { join => 'cd' })->all;
   my $response = JSON::Syck::Load( $mech->content);
+  #use Data::Dumper; warn Dumper($response, \@expected_response);
   is_deeply({ success => 'true',list => \@expected_response }, $response, 'correct message returned' );
 }
 
@@ -123,5 +133,8 @@
   cmp_ok( $mech->status, '==', 200, 'search on artist field okay due to wildcard' );
   my @expected_response = map { { $_->get_columns } } $base_rs->search({ 'artist.name' => 'Random Boy Band' }, { join => { cd => 'artist' } })->all;
   my $response = JSON::Syck::Load( $mech->content);
+  #use Data::Dumper; warn Dumper($response, \@expected_response);
   is_deeply({ success => 'true',list => \@expected_response }, $response, 'correct message returned' );
 }
+
+done_testing();

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/setup_dbic_args.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/setup_dbic_args.t	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/setup_dbic_args.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 use RestTest;
 use DBICTest;
 use URI;
-use Test::More qw(no_plan);
+use Test::More;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -33,3 +33,5 @@
   my $response = JSON::Syck::Load( $mech->content);
   is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct message returned' );
 }
+
+done_testing();

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/update.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/update.t	2009-05-07 13:19:53 UTC (rev 10040)
+++ Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/rpc/update.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 
 use RestTest;
 use DBICTest;
-use Test::More tests => 23;
+use Test::More tests => 25;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -118,3 +118,14 @@
   $track->discard_changes;
   is($track->title, 'baa', 'Title changed');
 }
+
+{
+  my $req = POST( $any_track_update_url, {
+	  position => '14'
+  });
+  $mech->request($req, $content_type);
+  cmp_ok( $mech->status, '==', 200, 'Position update okay' );
+
+  $track->discard_changes;
+  is($track->get_column('position'), '14', 'Position changed');
+}

Modified: Catalyst-Controller-DBIC-API/1.002/branches/moosify-validation-configurability/t/var/DBIxClass.db
===================================================================
(Binary files differ)

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/Changes
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/Changes	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/Changes	2009-12-21 17:28:49 UTC (rev 12448)
@@ -1,8 +1,28 @@
 Revision history for Catalyst-Controller-DBIC-API
 
-1.002001
-- Minor change to make this module work with Catalyst::Runtime 5.80
+1.003004
+- Database errors are also handled for searches + tests
+- totalcount isn't included in the response if a db error occurs while fetching data
+- converted no_plan tests to done_testing (required Test::More 0.88)
 
+1.003003
+- Database errors are properly handled + test
+- Fixed isa redefined warnings
+- Fixed bug preventing compat with future Catalyst::Action::Deserialize versions
+
+1.003002
+- Added totalcount to paged list responses
+- Fixed some tests weren't run in t/rpc/list.t
+- Fixed wrong setup_dbic_args_method error message
+
+1.003001
+- Minor fix to prevent failing test
+
+1.003000
+- Added prefetch support
+- Refactored to ensure all request params accept JSON, CGI::Expand or standard params
+- Doc improvements
+
 1.002000
 - Better error handing when unable to parse search arg
 - Added setup_dbic_args_method config option

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/Makefile.PL
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/Makefile.PL	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/Makefile.PL	2009-12-21 17:28:49 UTC (rev 12448)
@@ -4,13 +4,14 @@
 perl_version '5.006001';
 all_from 'lib/Catalyst/Controller/DBIC/API.pm';
 
-requires 'DBIx::Class' => 0.08;
+requires 'DBIx::Class' => 0.08103;
 requires 'Catalyst::Runtime' => 5.7010;
 requires 'Catalyst::Action::REST' => 0.60;
 requires 'CGI::Expand' => 2.02;
 requires 'JSON::Any' => 1.19;
+requires 'Test::Deep' => 0.104;
 
-build_requires 'Test::More'       => 0.7;
+build_requires 'Test::More'       => 0.88;
 build_requires 'Catalyst::Model::DBIC::Schema' => 0.20;
 build_requires 'Test::WWW::Mechanize'       => 0.20;
 build_requires 'Test::WWW::Mechanize::Catalyst'       => 0.37;
@@ -21,6 +22,6 @@
 
 auto_install;
 
-resources repository => 'http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Controller-DBIC-API/1.002/trunk/';
+resources repository => 'http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Controller-DBIC-API/1.003/trunk/';
 
 WriteAll;

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/Base.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/Base.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/Base.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -3,14 +3,22 @@
 
 use strict;
 use warnings;
-use base qw/Catalyst::Controller CGI::Expand/;
+use base qw/Catalyst::Controller/;
+use CGI::Expand qw/expand_hash/;
 
 use DBIx::Class::ResultClass::HashRefInflator;
 use JSON::Any;
+use Test::Deep::NoTest qw/eq_deeply/;
 
-__PACKAGE__->mk_accessors(qw(
-															class create_requires update_requires update_allows $self->rs_stash_key create_allows list_count list_returns list_grouped_by list_search_exposes list_ordered_by rs_stash_key object_stash_key setup_list_method setup_dbic_args_method
-													));
+__PACKAGE__->mk_accessors(qw/
+    class create_requires
+    update_requires update_allows
+    create_allows
+    list_count list_returns list_prefetch list_prefetch_allows
+    list_grouped_by list_search_exposes list_ordered_by
+    rs_stash_key object_stash_key
+    setup_list_method setup_dbic_args_method
+/);
 
 __PACKAGE__->config(
 	class => undef,
@@ -19,6 +27,8 @@
 	update_requires => [],
 	update_allows => [],
 	list_returns => [],
+	list_prefetch => undef,
+	list_prefetch_allows => [],
 	list_grouped_by => [],
 	list_search_exposes => [],
 	list_ordered_by => [],
@@ -27,63 +37,96 @@
 	rs_stash_key => 'class_rs'
 );
 
-sub begin :Private {
+sub setup :Chained('specify.in.subclass.config') :CaptureArgs(0) :PathPart('specify.in.subclass.config') {
 	my ($self, $c) = @_;
 
-	$c->forward('deserialize');
-	if ($c->req->data) {
-		$c->req->params($c->req->data);
-	}
-	$self->NEXT::begin($c);	
+	$c->stash->{$self->rs_stash_key} = $c->model($self->class);
 }
 
-sub setup :Chained('specify.in.subclass.config') :CaptureArgs(0) :PathPart('specify.in.subclass.config') {
+# from Catalyst::Action::Serialize
+sub deserialize :ActionClass('Deserialize') {
 	my ($self, $c) = @_;
 
-	$c->stash->{$self->rs_stash_key} = $c->model($self->class);
+	my $req_params;
+	if ($c->req->data && scalar(keys %{$c->req->data})) {
+		$req_params = $c->req->data;
+	} else {
+		$req_params = expand_hash($c->req->params);
+		foreach my $param (qw/search list_count list_ordered_by list_grouped_by list_prefetch/) {
+			# these params can also be composed of JSON
+			eval {
+				my $deserialized = JSON::Any->from_json($req_params->{$param});
+				$req_params->{$param} = $deserialized;
+			};
+		}
+	}
+	$c->stash->{_dbic_api}->{req_params} = $req_params;
 }
 
 sub list :Private {
 	my ($self, $c) = @_;
 
+	return if $self->get_errors($c);
 	my $ret = $c->forward('generate_dbic_search_args');
 	return unless ($ret && ref $ret);
 	my ($params, $args) = @{$ret};
 	return if $self->get_errors($c);
 
 	$c->stash->{$self->rs_stash_key} = $c->stash->{$self->rs_stash_key}->search($params, $args);
-	$c->forward('format_list');
+    # add the total count of all rows in case of a paged resultset
+    eval {
+        $c->stash->{_dbic_api}->{totalcount} = $c->stash->{$self->rs_stash_key}->pager->total_entries
+            if $args->{page};
+    };
+    if ($@) {
+        $c->log->error($@);
+        # send a generic error to the client to not give out infos about
+        # the database schema
+        $self->push_error($c, { message => 'a database error has occured.' });
+    }
+    else {
+        $c->forward('format_list');
+    }
 }
 
 sub generate_dbic_search_args :Private {
 	my ($self, $c) = @_;
-
-	my $req_params = (grep { ref $_ } values %{$c->req->params}) ? $c->req->params : $self->expand_hash($c->req->params);
-	# if expand_hash didn't do anything, try json
-	unless (exists $req_params->{search} && ref $req_params->{search} eq 'HASH') {
-		eval {
-			$req_params->{search} = exists $c->req->params->{search} ? JSON::Any->from_json($c->req->params->{search}) : undef;
-		};
-		if ($@) {
-			# json didn't work either. looks like it's screwed.
-			$self->push_error($c, { message => "can not parse search arg" });
-			return;
+  
+	my $args = {};
+	my $req_params = $c->stash->{_dbic_api}->{req_params};
+	my $prefetch = $req_params->{list_prefetch} || $self->list_prefetch || undef;
+	if ($prefetch) {
+		$prefetch = [$prefetch] unless ref $prefetch;
+		# validate the prefetch param against list_prefetch_allows
+		foreach my $prefetch_allows (@{$self->list_prefetch_allows}) {
+			if (eq_deeply($prefetch, $prefetch_allows)) {
+				$args->{prefetch} = $prefetch;
+				# stop looking for a valid prefetch param
+				last;
+			}
 		}
+		unless (exists $args->{prefetch}) {
+			$self->push_error($c, { message => "prefetch validation failed" });
+		}
 	}
 
-	if ( my $a = $self->setup_list_method ) {
-		my $setup_action = $self->action_for($a);
+	if ( my $action_name = $self->setup_list_method ) {
+		my $setup_action = $self->action_for($action_name);
 		if ( defined $setup_action ) {
 			$c->forward("/$setup_action", [ $req_params ]);
 		} else {
-			$c->log->error("setup_list_method was configured, but action $a not found");
+			$c->log->error("setup_list_method was configured, but action $action_name not found");
 		}
 	}
 	my $source = $c->stash->{$self->rs_stash_key}->result_source;
 
 	my ($params, $join);
-	my $args = {};
-	
+
+	if ($req_params->{search} && !ref $req_params->{search}) {
+		$self->push_error($c, { message => "can not parse search arg" });
+		return;
+	}
+
 	($params, $join) = $self->_format_search($c, { params => $req_params->{search}, source => $source }) if ($req_params->{search});
 
 	$args->{group_by} = $req_params->{list_grouped_by} || ((scalar(@{$self->list_grouped_by})) ? $self->list_grouped_by : undef);
@@ -109,15 +152,15 @@
 		$args->{select} = [map { ($_ =~ m/\./) ? $_ : "me.$_" } (ref $args->{select}) ? @{$args->{select}} : $args->{select}];
 	}
 	$args->{join} = $join;
-	if ( my $a = $self->setup_dbic_args_method ) {
-		my $format_action = $self->action_for($a);
+	if ( my $action_name = $self->setup_dbic_args_method ) {
+		my $format_action = $self->action_for($action_name);
 		if ( defined $format_action ) {
 			($params, $args) = @{$c->forward("/$format_action", [ $params, $args ])};
 		} else {
-			$c->log->error("setup_dbic_args_method was configured, but action $a not found");
+			$c->log->error("setup_dbic_args_method was configured, but action $action_name not found");
 		}
 	}
-
+	
 	return [$params, $args];
 }
 
@@ -192,7 +235,23 @@
 	# it still is what they expect (and not inflating to a hash ref)
 	my $rs = $c->stash->{$self->rs_stash_key}->search;
 	$rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
-	$c->stash->{response}->{list} = [ $rs->all ];
+    eval {
+	    $c->stash->{response}->{list} = [ $rs->all ];
+    };
+    if ($@) {
+        $c->log->error($@);
+        # send a generic error to the client to not give out infos about
+        # the database schema
+        $self->push_error($c, { message => 'a database error has occured.' });
+    }
+    else {
+        # only add the totalcount to the response if also data is returned
+        if (my $totalcount = $c->stash->{_dbic_api}->{totalcount}) {
+            # numify which is important for JSON
+            $totalcount += 0;
+            $c->stash->{response}->{totalcount} = $totalcount;
+        }
+    }
 }
 
 sub create :Private {
@@ -213,9 +272,8 @@
 	my ($self, $c) = @_;
 
 	# expand params unless they have already been expanded
-	my $req_params = (grep { ref $_ } values %{$c->req->params}) ? $c->req->params : $self->expand_hash($c->req->params);
+	my $req_params = $c->stash->{_dbic_api}->{req_params};
 
-	$c->req->params($req_params);
 	die "no object to update (looking at " . $self->object_stash_key . ")"
 		unless ( defined $c->stash->{$self->object_stash_key} );
 
@@ -226,7 +284,6 @@
 		die "class resultset not set";
 	}
 
-	#	use Data::Dumper; $c->log->debug(Dumper(\%create_args));
 	my $object = $c->stash->{$self->object_stash_key};
 	$self->validate_and_save_object($c, $object);
 }
@@ -246,7 +303,6 @@
 			if $c->debug;
 		return;
 	}
-
 	if ( $c->debug ) {
 		$c->log->debug("Saving object: $object");
 		$c->log->_dump( $params );
@@ -256,12 +312,12 @@
 
 sub validate {
 	my ($self, $c, $object) = @_;
-	my $params = $c->req->params;
+	my $params = $c->stash->{_dbic_api}->{req_params};
 
 	my %values;
 	my %requires_map = map { $_ => 1 } @{($object->in_storage) ? [] : $c->stash->{create_requires} || $self->create_requires};
 	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)});
-	
+
 	foreach my $key (keys %allows_map) {
 		# check value defined if key required
 		my $allowed_fields = $allows_map{$key};
@@ -271,9 +327,8 @@
 				$self->push_error($c, { message => "${key} is not a valid relation" });
 				next;
 			}
-		   
+
 			my $related_params = $params->{$key};
-
 			# it's an error for $c->req->params->{$key} to be defined but not be an array
 			unless (ref $related_params) {
 				unless (!defined $related_params) {
@@ -326,20 +381,29 @@
 
 sub save_object {
 	my ($self, $c, $object, $params) = @_;
-	
-	if ($object->in_storage) {
-		foreach my $key (keys %{$params}) {
-			if (ref $params->{$key}) {
-				my $related_params = delete $params->{$key};
-				my $row = $object->find_related($key, {} , {});
-				$row->update($related_params);
-			}
-		}
-		$object->update($params);
-	} else {
-		$object->set_columns($params);
-		$object->insert;
-	}
+
+    eval {
+    	if ($object->in_storage) {
+    		foreach my $key (keys %{$params}) {
+    			if (ref $params->{$key}) {
+    				my $related_params = delete $params->{$key};
+    				my $row = $object->find_related($key, {} , {});
+    				$row->update($related_params);
+    			}
+    		}
+    		$object->update($params);
+    	} else {
+    		$object->set_columns($params);
+    		$object->insert;
+    	}
+    };
+    if ($@) {
+        $c->log->error($@);
+        # send a generic error to the client to not give out infos about
+        # the database schema
+        $self->push_error($c, { message => 'a database error has occured.' });
+    }
+
 	return $object;
 }
 

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/REST.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/REST.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/REST.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -74,10 +74,10 @@
 
 =cut 
 
-# from Catalyst::Action::Serialize
-sub deserialize :ActionClass('Deserialize') {
+sub begin :Private {
 	my ($self, $c) = @_;
 
+	$c->forward('deserialize');
 }
 
 sub object :Chained('setup') :Args(1) :PathPart('') :ActionClass('REST') {

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/RPC.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/RPC.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API/RPC.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -86,10 +86,11 @@
 
 =cut 
 
-# from Catalyst::Action::Serialize
-sub deserialize :ActionClass('Deserialize') {
+sub begin :Private {
 	my ($self, $c) = @_;
 
+	$c->forward('deserialize');
+	$c->req->params($c->stash->{_dbic_api}->{req_params});
 }
 
 sub object :Chained('setup') :CaptureArgs(1) :PathPart('id') {

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/lib/Catalyst/Controller/DBIC/API.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -5,11 +5,11 @@
 
 =head1 VERSION
 
-Version 1.002000
+Version 1.003004
 
 =cut
 
-our $VERSION = '1.002001';
+our $VERSION = '1.003004';
 
 =head1 NAME
 
@@ -28,8 +28,15 @@
       update_allows => ['name', 'age', 'nickname'], # columns that update allows
       update_allows => ['name', 'age', 'nickname'], # columns that update allows
       list_returns => [qw/name age/], # columns that list returns
+      list_prefetch => ['cds'], # relationships that are prefetched when no prefetch param is passed
+      list_prefetch_allows => [ # every possible prefetch param allowed
+          'cds',
+          qw/ cds /,
+          { cds => 'tracks' },
+          { cds => [qw/ tracks /] }
+      ],
       list_ordered_by => [qw/age/], # order of generated list
-      list_search_exposes => [qw/age nickname/, { cd => [qw/title year/] }], # columns that can be searched on via list
+      list_search_exposes => [qw/age nickname/, { cds => [qw/title year/] }], # columns that can be searched on via list
       );
 
   # Provides the following functional endpoints:
@@ -68,11 +75,13 @@
       return;
     }
 
-    $self->NEXT::setup($c);
+    $self->next::method($c);
   }
 
 Generally it's better to have one controller for each DBIC source with the config hardcoded, but in some cases this isn't possible.
 
+Note that the Chained, CaptureArgs and PathPart are just standard Catalyst configuration parameters and that then endpoint specified in Chained - in this case '/api/rpc/rpc_base' - must actually exist elsewhere in your application. See L<Catalyst::DispatchType::Chained> for more details.
+
 =head2 class
 
 Whatever you would pass to $c->model to get a resultset for this class. MyAppDB::Track for example.
@@ -93,6 +102,20 @@
 
 Arguments to pass to L<DBIx::Class::ResultSet/select> when performing search for L</list>.
 
+=head2 list_prefetch
+
+Arguments to pass to L<DBIx::Class::ResultSet/prefetch> when performing search for L</list>.
+
+=head2 list_prefetch_allows
+
+Arrayref listing relationships that are allowed to be prefetched.
+This is necessary to avoid denial of service attacks in form of
+queries which would return a large number of data
+and unwanted disclosure of data.
+Every element of the arrayref is one allowed parameter to prefetch.
+So for three searches, all requiring different prefetch parameters,
+three elements have to be passed to list_prefetch_allows in the controller.
+
 =head2 list_grouped_by
 
 Arguments to pass to L<DBIx::Class::ResultSet/group_by> when performing search for L</list>.
@@ -210,7 +233,7 @@
   sub setup :Chained('/api/rpc_base') :CaptureArgs(0) :PathPart('track') {
     my ($self, $c) = @_;
 
-    $self->NEXT::setup($c);
+    $self->next::method($c);
   }
 
 This action will populate $c->stash->{$self->rs_stash_key} with $c->model($self->class) for other actions in the chain to use.
@@ -285,7 +308,7 @@
     my ($self, $c) = @_;
 
     # will set $c->stash->{created_object} if successful
-    $self->NEXT::create($c);
+    $self->next::method($c);
 
     if ($c->stash->{created_object}) {    
       # $c->stash->{response} will be serialized in the end action
@@ -313,7 +336,7 @@
 
   Zbigniew Lukasiak <zzbbyy at gmail.com>
 
-  Alexander Hartmaier <alex_hartmaier at hotmail.com>
+  Alexander Hartmaier <abraxxa at cpan.org>
 
 =head1 SPECIAL THANKS
 

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Artist.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Artist.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Artist.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,8 @@
       class => 'RestTestDB::Artist',
       create_requires => ['name'],
       create_allows => ['name'],
-      update_allows => ['name']
+      update_allows => ['name'],
+      list_prefetch_allows => [[qw/ cds /],{ 'cds' => 'tracks'}],
       );
 
 1;

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Producer.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Producer.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/REST/Producer.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -15,7 +15,7 @@
 
 sub create :Private {
   my ($self, $c) = @_;
-  $self->NEXT::create($c);
+  $self->next::method($c);
 
   if ($c->stash->{created_object}) {
     %{$c->stash->{response}->{new_producer}} = $c->stash->{created_object}->get_columns;

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Artist.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Artist.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Artist.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,8 @@
       class => 'RestTestDB::Artist',
       create_requires => ['name'],
       create_allows => ['name'],
-      update_allows => ['name']
+      update_allows => ['name'],
+      list_prefetch_allows => [[qw/ cds /],{ 'cds' => 'tracks'}],
       );
 
 1;

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/CD.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/CD.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/CD.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -9,7 +9,8 @@
     ( action => { setup => { PathPart => 'cd', Chained => '/api/rpc/rpc_base' } },
       class => 'RestTestDB::CD',
       create_requires => ['artist', 'title', 'year' ],
-      update_allows => ['title', 'year']
+      update_allows => ['title', 'year'],
+      list_prefetch_allows => [[qw/ tracks /]],
       );
 
 1;

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Producer.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Producer.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Controller/API/RPC/Producer.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -9,13 +9,14 @@
     ( action => { setup => { PathPart => 'producer', Chained => '/api/rpc/rpc_base' } },
       class => 'RestTestDB::Producer',
       create_requires => ['name'],
+      create_allows => ['producerid'],
       update_allows => ['name'],
       list_returns => ['name']
       );
 
 sub create :Chained('setup') :Args(0) :PathPart('create') {
   my ($self, $c) = @_;
-  $self->NEXT::create($c);
+  $self->next::method($c);
 
   if ($c->stash->{created_object}) {
     %{$c->stash->{response}->{new_producer}} = $c->stash->{created_object}->get_columns;

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/Result/Track.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/Result/Track.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/Result/Track.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -14,7 +14,7 @@
   'position' => {
     data_type => 'integer',
     accessor => 'pos',
-	default_value => 0
+		default_value => 0
   },
   'title' => {
     data_type => 'varchar',

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/ResultSet/Track.pm
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/ResultSet/Track.pm	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/lib/RestTest/Schema/ResultSet/Track.pm	2009-12-21 17:28:49 UTC (rev 12448)
@@ -7,10 +7,12 @@
 	my $self = shift;
 	my ($clause, $params) = @_;
 
-	# test custom attrs
-	if (my $pretend = delete $clause->{pretend}) {
-		$clause->{'cd.year'} = $pretend;
-	}
+  if (ref $clause eq 'HASH') {
+    # test custom attrs
+    if (my $pretend = delete $clause->{pretend}) {
+      $clause->{'cd.year'} = $pretend;
+    }
+  }
   my $rs = $self->SUPER::search(@_);	
 }
 

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/rest/create.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rest/create.t	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/rest/create.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -61,9 +61,8 @@
 						 );
 	$req->content( $test_data );
 	$mech->request($req);
-
 	cmp_ok( $mech->status, '==', 200, 'request with valid content okay' );
-        my $new_obj = $schema->resultset('Producer')->find({ name => 'king luke' });
+	my $new_obj = $schema->resultset('Producer')->find({ name => 'king luke' });
 	ok($new_obj, 'record created with specified name');
 
 	my $response = JSON::Syck::Load( $mech->content);

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/create.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/create.t	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/create.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 
 use RestTest;
 use DBICTest;
-use Test::More tests => 11;
+use Test::More tests => 12;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -73,3 +73,15 @@
   my $response = JSON::Syck::Load( $mech->content);
   is_deeply( $response, { success => 'true' }, 'json for new artist returned' );
 }
+
+# test create returns an error as expected when passing invalid value
+{
+  my $long_string = '-' x 1024;
+
+  my $req = POST( $producer_create_url, {
+	  producerid => $long_string,
+      name       => $long_string,
+  }, 'Accept' => 'text/json' );
+  $mech->request($req, $content_type);
+  cmp_ok( $mech->status, '==', 400, 'invalid param value produces error' );
+}

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list.t	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 use RestTest;
 use DBICTest;
 use URI;
-use Test::More qw(no_plan);
+use Test::More;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -155,7 +155,7 @@
 	my @expected_response = map { { $_->get_columns } } $schema->resultset('Track')->search({}, { group_by => 'position', order_by => 'position ASC', select => 'position', rows => 2, page => 2 })->all;
 	my $response = JSON::Syck::Load( $mech->content);
 	#  use Data::Dumper; warn Dumper($response, \@expected_response);
-	is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned' );
+    is_deeply( { list => \@expected_response, success => 'true', totalcount => 3 }, $response, 'correct data returned' );
 }
 
 {
@@ -197,26 +197,39 @@
 
 {
 	my $uri = URI->new( $cd_list_url );
-	$uri->query_form({ 'search.artist.artistid' => 1 });
+	$uri->query_form({ 'search.title' => 'Spoonful of bees', 'search.tracks.position' => 1 });
 	my $req = GET( $uri, 'Accept' => 'text/x-json' );
 	$mech->request($req);
-	cmp_ok( $mech->status, '==', 200, 'search on rel column with same name fk request okay' );
+	if (cmp_ok( $mech->status, '==', 200, 'search on col which exists for me and related table okay' )) {
+		my @expected_response = map { { $_->get_columns } } $schema->resultset('CD')->search({'me.title' => 'Spoonful of bees', 'tracks.position' => 1}, { join => 'tracks' })->all;
+		my $response = JSON::Syck::Load( $mech->content);
+		#use Data::Dumper; warn Dumper($response, \@expected_response);
+		is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned for search on col which exists for me and related table' );
+	}
+}
 
-	my @expected_response = map { { $_->get_columns } } $schema->resultset('CD')->search({'artist.artistid' => 1}, { join => 'artist' })->all;
-	my $response = JSON::Syck::Load( $mech->content);
-	#use Data::Dumper; warn Dumper($response, \@expected_response);
-	is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned for search on rel column with same name rel fk' );
+{
+	my $uri = URI->new( $cd_list_url );
+	$uri->query_form({ 'list_ordered_by' => 'invalid_column' });
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	if (cmp_ok( $mech->status, '==', 400, 'order_by on non-existing col returns error' )) {
+		my $response = JSON::Syck::Load( $mech->content);
+		is_deeply( $response, { messages => ['a database error has occured.'], success => 'false' },
+            'error returned for order_by on non-existing col' );
+	}
 }
 
 {
 	my $uri = URI->new( $cd_list_url );
-	$uri->query_form({ 'search.title' => 'Spoonful of bees', 'search.tracks.position' => 1 });
+	$uri->query_form({ 'list_ordered_by' => 'invalid_column', 'list_count' => 2, 'list_page' => 1 });
 	my $req = GET( $uri, 'Accept' => 'text/x-json' );
 	$mech->request($req);
-	if (cmp_ok( $mech->status, '==', 200, 'search on col which exists for me and related table okay' )) {
-		my @expected_response = map { { $_->get_columns } } $schema->resultset('CD')->search({'me.title' => 'Spoonful of bees', 'tracks.position' => 1}, { join => 'tracks' })->all;
+	if (cmp_ok( $mech->status, '==', 400, 'order_by on invalid col with paging returns error' )) {
 		my $response = JSON::Syck::Load( $mech->content);
-		#use Data::Dumper; warn Dumper($response, \@expected_response);
-		is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned for search on col which exists for me and related table' );
+		is_deeply( $response, { messages => ['a database error has occured.'], success => 'false' },
+            'error returned for order_by on non-existing col with paging' );
 	}
 }
+
+done_testing();

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_json_search.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_json_search.t	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_json_search.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 use RestTest;
 use DBICTest;
 use URI;
-use Test::More qw(no_plan);
+use Test::More;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -32,7 +32,6 @@
 	is_deeply( { messages => ['can not parse search arg'], success => 'false' }, $response, 'correct data returned for gibberish in search' );
 }
 
-
 {
 	my $uri = URI->new( $artist_list_url );
 	$uri->query_form({ 'search' => '{"name":{"LIKE":"%waul%"}}' });
@@ -55,3 +54,5 @@
 	my $response = JSON::Syck::Load( $mech->content);
 	is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct data returned for complex query' );
 }
+
+done_testing();

Added: Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_prefetch.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_prefetch.t	                        (rev 0)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_prefetch.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -0,0 +1,75 @@
+use 5.6.0;
+
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+my $base = 'http://localhost';
+
+use RestTest;
+use DBICTest;
+use URI;
+use Test::More tests => 15;
+use Test::WWW::Mechanize::Catalyst 'RestTest';
+use HTTP::Request::Common;
+use JSON::Syck;
+
+my $mech = Test::WWW::Mechanize::Catalyst->new;
+ok(my $schema = DBICTest->init_schema(), 'got schema');
+
+my $artist_list_url = "$base/api/rpc/artist/list";
+my $cd_list_url = "$base/api/rpc/cd/list";
+
+foreach my $req_params ({ 'list_prefetch' => '["cds"]' }, { 'list_prefetch' => 'cds' }) {
+	my $uri = URI->new( $artist_list_url );
+	$uri->query_form($req_params);
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	cmp_ok( $mech->status, '==', 200, 'search with simple prefetch request okay' );
+	my $rs = $schema->resultset('Artist')->search(undef, { prefetch => ['cds'] });
+	$rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
+	my @rows = $rs->all;
+	my $expected_response = { list => \@rows, success => 'true' };
+	my $response = JSON::Syck::Load( $mech->content);
+	is_deeply( $expected_response, $response, 'correct data returned for search with simple prefetch specified as param' );
+}
+
+foreach my $req_params ({ 'list_prefetch' => '{"cds":"tracks"}' }, { 'list_prefetch.cds' => 'tracks' }) {
+	my $uri = URI->new( $artist_list_url );
+	$uri->query_form($req_params);
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	cmp_ok( $mech->status, '==', 200, 'search with multi-level prefetch request okay' );
+	my $rs = $schema->resultset('Artist')->search(undef, { prefetch => {'cds' => 'tracks'} });
+	$rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
+	my @rows = $rs->all;
+	my $expected_response = { list => \@rows, success => 'true' };
+	my $response = JSON::Syck::Load( $mech->content);
+	#use Data::Dumper; warn Dumper($response, $expected_response);
+	is_deeply( $expected_response, $response, 'correct data returned for search with multi-level prefetch specified as param' );
+}
+
+foreach my $req_params ({ 'list_prefetch' => '["artist"]' }, { 'list_prefetch' => 'artist' }) {
+	my $uri = URI->new( $cd_list_url );
+	$uri->query_form($req_params);	
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	cmp_ok( $mech->status, '==', 400, 'prefetch of artist not okay' );
+
+	my $expected_response = map { { $_->get_columns } } $schema->resultset('CD')->all;
+	my $response = JSON::Syck::Load( $mech->content);
+	is_deeply({ success => 'false',messages => ["prefetch validation failed"]}, $response, 'correct message returned' );
+}
+
+{
+	my $uri = URI->new( $cd_list_url );
+	$uri->query_form({ 'list_prefetch' => 'tracks', 'list_ordered_by' => 'title', 'list_count' => 2, 'list_page' => 1 });
+	my $req = GET( $uri, 'Accept' => 'text/x-json' );
+	$mech->request($req);
+	if (cmp_ok( $mech->status, '==', 400, 'order_by on non-unique col with paging returns error' )) {
+		my $response = JSON::Syck::Load( $mech->content);
+		is_deeply( $response, { messages => ['a database error has occured.'], success => 'false' },
+            'error returned for order_by on non-existing col with paging' );
+	}
+}

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_search_allows.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_search_allows.t	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/list_search_allows.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 use RestTest;
 use DBICTest;
 use URI;
-use Test::More qw(no_plan);
+use Test::More;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -125,3 +125,5 @@
   my $response = JSON::Syck::Load( $mech->content);
   is_deeply({ success => 'true',list => \@expected_response }, $response, 'correct message returned' );
 }
+
+done_testing();

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/setup_dbic_args.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/setup_dbic_args.t	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/setup_dbic_args.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 use RestTest;
 use DBICTest;
 use URI;
-use Test::More qw(no_plan);
+use Test::More;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -33,3 +33,5 @@
   my $response = JSON::Syck::Load( $mech->content);
   is_deeply( { list => \@expected_response, success => 'true' }, $response, 'correct message returned' );
 }
+
+done_testing();

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/update.t
===================================================================
--- Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/update.t	2009-12-21 17:23:56 UTC (rev 12447)
+++ Catalyst-Controller-DBIC-API/1.002/trunk/t/rpc/update.t	2009-12-21 17:28:49 UTC (rev 12448)
@@ -10,7 +10,7 @@
 
 use RestTest;
 use DBICTest;
-use Test::More tests => 23;
+use Test::More tests => 25;
 use Test::WWW::Mechanize::Catalyst 'RestTest';
 use HTTP::Request::Common;
 use JSON::Syck;
@@ -118,3 +118,14 @@
   $track->discard_changes;
   is($track->title, 'baa', 'Title changed');
 }
+
+{
+  my $req = POST( $any_track_update_url, {
+	  position => '14'
+  });
+  $mech->request($req, $content_type);
+  cmp_ok( $mech->status, '==', 200, 'Position update okay' );
+
+  $track->discard_changes;
+  is($track->get_column('position'), '14', 'Position changed');
+}

Modified: Catalyst-Controller-DBIC-API/1.002/trunk/t/var/DBIxClass.db
===================================================================
(Binary files differ)




More information about the Catalyst-commits mailing list