This action is the chain root of the controller. It must either be overridden or configured to provide a base pathpart to the action and also a parent action. For example, for class MyAppDB::Track you might have
package MyApp::Controller::API::RPC::Track;
- use Moose;
+ use Moose;
BEGIN { extends 'Catalyst::Controller::DBIC::API::RPC'; }
__PACKAGE__->config
- ( action => { setup => { PathPart => 'track', Chained => '/api/rpc/rpc_base' } },
+ ( action => { setup => { PathPart => 'track', Chained => '/api/rpc/rpc_base' } },
...
);
{
$req_params = $c->req->data;
}
- else
+ else
{
$req_params = CGI::Expand->expand_hash($c->req->params);
$req_params->{$param}->{$key} = $deserialized;
}
catch
- {
+ {
$c->log->debug("Param '$param.$key' did not deserialize appropriately: $_")
if $c->debug;
}
$req_params->{$param} = $deserialized;
}
catch
- {
+ {
$c->log->debug("Param '$param' did not deserialize appropriately: $_")
if $c->debug;
}
}
}
}
-
+
$self->inflate_request($c, $req_params);
}
}
=method_protected inflate_request
-
+
inflate_request is called at the end of deserialize to populate key portions of the request with the useful bits
=cut
try
{
# set static arguments
- $c->req->_set_controller($self);
+ $c->req->_set_controller($self);
# set request arguments
$c->req->_set_request_data($params);
# set the current resultset
$c->req->_set_current_result_set($self->generate_rs($c));
-
+
}
catch
{
sub objects_no_id :Chained('deserialize') :CaptureArgs(0) :PathPart('')
{
my ($self, $c) = @_;
-
+
if($c->req->has_request_data)
{
my $data = $c->req->request_data;
my $vals;
-
+
if(exists($data->{$self->data_root}) && defined($data->{$self->data_root}))
{
my $root = $data->{$self->data_root};
?page=2&count=20
Would result in this search:
-
+
$rs->search({}, { page => 2, rows => 20 })
=cut
$self->list_format_output($c);
# make sure there are no objects lingering
- $c->req->clear_objects();
+ $c->req->clear_objects();
}
=method_protected list_munge_parameters
sub list_perform_search
{
my ($self, $c) = @_;
-
- try
+
+ try
{
my $req = $c->req;
-
+
my $rs = $req->current_result_set->search
(
- $req->search_parameters,
+ $req->search_parameters,
$req->search_attributes
);
my $rs = $c->req->current_result_set->search;
$rs->result_class('DBIx::Class::ResultClass::HashRefInflator');
-
+
try
{
my $output = {};
my $formatted = [];
-
+
foreach my $row ($rs->all)
{
push(@$formatted, $self->row_format_output($c, $row));
}
-
+
$output->{$self->data_root} = $formatted;
if ($c->req->has_search_total_entries)
sub update_or_create
{
my ($self, $c) = @_;
-
+
if($c->req->has_objects)
{
$self->validate_objects($c);
sub transact_objects
{
my ($self, $c, $coderef) = @_;
-
+
try
{
$self->stored_result_source->schema->txn_do
my %requires_map = map
{
$_ => 1
- }
+ }
@{
- ($object->in_storage)
- ? []
+ ($object->in_storage)
+ ? []
: $c->stash->{create_requires} || $self->create_requires
};
-
+
my %allows_map = map
{
(ref $_) ? %{$_} : ($_ => 1)
- }
+ }
(
- keys %requires_map,
+ keys %requires_map,
@{
- ($object->in_storage)
- ? ($c->stash->{update_allows} || $self->update_allows)
+ ($object->in_storage)
+ ? ($c->stash->{update_allows} || $self->update_allows)
: ($c->stash->{create_allows} || $self->create_allows)
}
);
{
# 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);
my $related_params = $params->{$key};
my %allowed_related_map = map { $_ => 1 } @$allowed_fields;
my $allowed_related_cols = ($allowed_related_map{'*'}) ? [$related_source->columns] : $allowed_fields;
-
+
foreach my $related_col (@{$allowed_related_cols})
{
if (my $related_col_value = $related_params->{$related_col}) {
}
}
}
- else
+ else
{
my $value = $params->{$key};
}
}
}
-
+
# check for multiple values
if (ref($value) && !($value == JSON::Any::true || $value == JSON::Any::false))
{
}
}
- unless (keys %values || !$object->in_storage)
+ unless (keys %values || !$object->in_storage)
{
die 'No valid keys passed';
}
- return \%values;
+ return \%values;
}
=method_protected delete
sub delete
{
my ($self, $c) = @_;
-
+
if($c->req->has_objects)
{
$self->transact_objects($c, sub { $self->delete_objects($c, @_) });
{
$self->update_object_from_params($c, $object, $params);
}
- else
+ else
{
$self->insert_object_from_params($c, $object, $params);
}
$self->update_object_relation($c, $object, delete $params->{$key}, $key);
}
}
-
+
$object->update($params);
}
$c->stash->{response}->{success} = $self->use_json_boolean ? JSON::Any::true : 'true';
$default_status = 200;
}
-
+
unless ($default_status == 200)
{
delete $c->stash->{response}->{$self->data_root};
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;
-
+
...
-
+
__PACKAGE__->config
( ...,
search_exposes => [qw/position title custom_column/],
and then in your custom resultset:
package RestTest::Schema::ResultSet::Track;
-
+
use base 'RestTest::Schema::ResultSet';
-
+
sub search {
my $self = shift;
my ($clause, $params) = @_;
# $c->req->all_objects will contain all of the created
$self->next::method($c);
- if ($c->req->has_objects) {
+ if ($c->req->has_objects) {
# $c->stash->{response} will be serialized in the end action
$c->stash->{response}->{$self->data_root} = [ map { { $_->get_inflated_columns } } ($c->req->all_objects) ] ;
}
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
+If more extensive customization is required, it is recommened to peer into the roles that comprise the system and make use
=head1 NOTES
sub _build_joins
{
my ($self) = @_;
-
+
my $parent;
while(my $found = $self->parent)
{
=head1 DESCRIPTION
-Provides a REST style API interface to the functionality described in L<Catalyst::Controller::DBIC::API>.
+Provides a REST style API interface to the functionality described in L<Catalyst::Controller::DBIC::API>.
By default provides the following endpoints:
As described in L<Catalyst::Controller::DBIC::API/setup>, this action is the chain root of the controller but has no pathpart or chain parent defined by default, so these must be defined in order for the controller to function. The neatest way is normally to define these using the controller's config.
__PACKAGE__->config
- ( action => { setup => { PathPart => 'track', Chained => '/api/rest/rest_base' } },
+ ( action => { setup => { PathPart => 'track', Chained => '/api/rest/rest_base' } },
...
);
BEGIN { extends 'Catalyst::Controller::DBIC::API'; }
__PACKAGE__->config(
- 'action' => { object_with_id => { PathPart => 'id' } },
+ 'action' => { object_with_id => { PathPart => 'id' } },
'default' => 'application/json',
'stash_key' => 'response',
'map' => {
=head1 DESCRIPTION
-Provides an RPC API interface to the functionality described in L<Catalyst::Controller::DBIC::API>.
+Provides an RPC API interface to the functionality described in L<Catalyst::Controller::DBIC::API>.
By default provides the following endpoints:
As described in L<Catalyst::Controller::DBIC::API/setup>, this action is the chain root of the controller but has no pathpart or chain parent defined by default, so these must be defined in order for the controller to function. The neatest way is normally to define these using the controller's config.
__PACKAGE__->config
- ( action => { setup => { PathPart => 'track', Chained => '/api/rpc/rpc_base' } },
+ ( action => { setup => { PathPart => 'track', Chained => '/api/rpc/rpc_base' } },
...
);
=cut
-sub create :Chained('objects_no_id') :PathPart('create') :Args(0)
+sub create :Chained('objects_no_id') :PathPart('create') :Args(0)
{
my ($self, $c) = @_;
$self->update_or_create($c);
=cut
-sub list :Chained('deserialize') :PathPart('list') :Args(0)
+sub list :Chained('deserialize') :PathPart('list') :Args(0)
{
my ($self, $c) = @_;
$self->next::method($c);
=cut
-sub item :Chained('object_with_id') :PathPart('') :Args(0)
+sub item :Chained('object_with_id') :PathPart('') :Args(0)
{
my ($self, $c) = @_;
$self->next::method($c);
=cut
-sub update :Chained('object_with_id') :PathPart('update') :Args(0)
+sub update :Chained('object_with_id') :PathPart('update') :Args(0)
{
my ($self, $c) = @_;
$self->update_or_create($c);
=cut
-sub delete :Chained('object_with_id') :PathPart('delete') :Args(0)
+sub delete :Chained('object_with_id') :PathPart('delete') :Args(0)
{
my ($self, $c) = @_;
$self->next::method($c);
=cut
-sub update_bulk :Chained('objects_no_id') :PathPart('update') :Args(0)
+sub update_bulk :Chained('objects_no_id') :PathPart('update') :Args(0)
{
my ($self, $c) = @_;
$self->update_or_create($c);
=cut
-sub delete_bulk :Chained('objects_no_id') :PathPart('delete') :Args(0)
+sub delete_bulk :Chained('objects_no_id') :PathPart('delete') :Args(0)
{
my ($self, $c) = @_;
$self->next::method($c);
my ($self, $new) = @_;
$self->_set_class($new->class) if defined($new->class);
- $self->_set_application($new->_application);
+ $self->_set_application($new->_application);
$self->_set_prefetch_allows($new->prefetch_allows);
$self->_set_search_exposes($new->search_exposes);
$self->_set_select_exposes($new->select_exposes);
isa => ArrayRef[ Tuple[ Object, Maybe[HashRef] ] ],
traits => [ 'Array' ],
default => sub { [] },
- handles =>
+ handles =>
{
all_objects => 'elements',
add_object => 'push',
parameter static => ( isa => Bool, default => 0 );
role {
-
+
my $p = shift;
-
+
if($p->static)
{
requires qw/check_has_relation check_column_relation/;
(
is => 'ro',
writer => '_set_prefetch',
- isa => Prefetch,
+ isa => Prefetch,
default => sub { $p->static ? [] : undef },
coerce => 1,
trigger => sub
(
is => 'ro',
writer => '_set_prefetch_allows',
- isa => ArrayRef[ArrayRef|Str|HashRef],
+ isa => ArrayRef[ArrayRef|Str|HashRef],
default => sub { [ ] },
predicate => 'has_prefetch_allows',
trigger => sub
trigger => sub
{
my ($self, $new) = @_;
-
+
if($self->has_search_exposes and @{$self->search_exposes})
{
foreach my $foo (@$new)
}
}
}
-
+
my ($search_parameters, $search_attributes) = $self->generate_parameters_attributes($new);
$self->_set_search_parameters($search_parameters);
$self->_set_search_attributes($search_attributes);
default => sub { $p->static ? [] : undef },
coerce => 1,
trigger => sub
- {
+ {
my ($self, $new) = @_;
if($self->has_select_exposes)
{
method format_search_parameters => sub
{
my ($self, $params) = @_;
-
+
my $genparams = [];
foreach my $param (@$params)
next;
}
- %$search_params =
+ %$search_params =
%{
$self->generate_column_parameters
(
{
my ($self, $args) = @_;
my $static = $self->_controller;
- my $search_attributes =
+ my $search_attributes =
{
group_by => $self->grouped_by || ((scalar(@{$static->grouped_by})) ? $static->grouped_by : undef),
order_by => $self->ordered_by || ((scalar(@{$static->ordered_by})) ? $static->ordered_by : undef),
$search_attributes->{page} = $search_attributes->{offset} / $search_attributes->{rows} + 1;
delete $search_attributes->{offset};
}
-
- $search_attributes =
- {
+
+ $search_attributes =
+ {
map { @$_ }
grep
{
- defined($_->[1])
- ?
+ defined($_->[1])
+ ?
(ref($_->[1]) && reftype($_->[1]) eq 'HASH' && keys %{$_->[1]})
|| (ref($_->[1]) && reftype($_->[1]) eq 'ARRAY' && @{$_->[1]})
|| length($_->[1])
if ($search_attributes->{page} && !$search_attributes->{rows}) {
die 'list_page can only be used with list_count';
}
-
+
if ($search_attributes->{select}) {
# make sure all columns have an alias to avoid ambiguous issues
# but allow non strings (eg. hashrefs for db procs like 'count')
}
return $search_attributes;
-
+
};
};
traits => ['Array'],
default => sub { [] },
trigger => sub
- {
+ {
my ($self, $new) = @_;
$self->check_column_relation($_, 1) for @$new;
},
);
sub _build_stored_model
-{
+{
return $_[0]->_application->model($_[0]->class);
}
sub check_has_relation
{
my ($self, $rel, $other, $nest, $static) = @_;
-
+
$nest ||= $self->stored_result_source;
if(HashRef->check($other))
=method_public check_column_relation
-Convenience method to first check if the provided argument is a valid relation (if it is a HashRef) or column.
+Convenience method to first check if the provided argument is a valid relation (if it is a HashRef) or column.
=cut
sub check_column_relation
{
my ($self, $col_rel, $static) = @_;
-
+
if(HashRef->check($col_rel))
{
try
around visit_value => sub
{
my ($orig, $self, $val) = @_;
-
+
if($self->value_type eq 'NONE')
{
$self->dive();
{
$self->append_text($val);
warn 'VALUE: ' . $self->current_template if DEBUG;
- }
+ }
else
{
$self->$orig($val);
-package # hide from PAUSE
+package # hide from PAUSE
DBICTest;
use strict;
use lib qw(t/lib);
use DBICTest;
use Test::More;
-
+
my $schema = DBICTest->init_schema();
=head1 DESCRIPTION
-This module provides the basic utilities to write tests against
+This module provides the basic utilities to write tests against
DBIx::Class.
=head1 METHODS
no_populate=>1,
);
-This method removes the test SQLite database in t/var/DBIxClass.db
+This method removes the test SQLite database in t/var/DBIxClass.db
and then creates a new, empty database.
-This method will call deploy_schema() by default, unless the
+This method will call deploy_schema() by default, unless the
no_deploy flag is set.
-Also, by default, this method will call populate_schema() by
+Also, by default, this method will call populate_schema() by
default, unless the no_deploy or no_populate flags are set.
=cut
close IN;
($schema->storage->dbh->do($_) || print "Error on SQL: $_\n") for split(/;\n/, $sql);
}
-
+
=head2 clear_schema
DBICTest->populate_schema( $schema );
-After you deploy your schema you can use this method to populate
+After you deploy your schema you can use this method to populate
the tables with test data.
=cut
# -Debug: activates the debug mode for very useful log messages
# ConfigLoader: will load the configuration from a YAML file in the
# application's home directory
-# Static::Simple: will serve static files from the application's root
+# Static::Simple: will serve static files from the application's root
# directory
use Catalyst;
our $VERSION = '0.01';
-# Configure the application.
+# Configure the application.
#
# Note that settings in RestTest.yml (or other external
# configuration file that you set up manually) take precedence
sub end :Private {
my ( $self, $c ) = @_;
-
+
}
1;
sub end :Private {
my ( $self, $c ) = @_;
-
+
}
1;
Attempt to render a view, if needed.
-=cut
+=cut
sub end : Private {}
"",
"",
{AutoCommit => 1}
- ]
+ ]
);
1;
-package # hide from PAUSE
+package # hide from PAUSE
RestTest::Schema::Result::Artist;
use base 'DBIx::Class';
-package # hide from PAUSE
+package # hide from PAUSE
RestTest::Schema::Result::CD;
use base 'DBIx::Class::Core';
-package # hide from PAUSE
+package # hide from PAUSE
RestTest::Schema::Result::CD_to_Producer;
use base 'DBIx::Class::Core';
-package # hide from PAUSE
+package # hide from PAUSE
RestTest::Schema::Result::Producer;
use base 'DBIx::Class::Core';
-package # hide from PAUSE
+package # hide from PAUSE
RestTest::Schema::Result::Tag;
use base qw/DBIx::Class::Core/;
-package # hide from PAUSE
+package # hide from PAUSE
RestTest::Schema::Result::Track;
use base 'DBIx::Class::Core';
-package # hide from PAUSE
+package # hide from PAUSE
RestTest::Schema::ResultSet;
use base 'DBIx::Class::ResultSet';
-package # hide from PAUSE
+package # hide from PAUSE
RestTest::Schema::ResultSet::Track;
use base 'RestTest::Schema::ResultSet';
$clause->[0]->{'cd.year'} = $pretend;
}
}
- my $rs = $self->SUPER::search(@_);
+ my $rs = $self->SUPER::search(@_);
}
1;