X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Controller-DBIC-API.git;a=blobdiff_plain;f=lib%2FCatalyst%2FController%2FDBIC%2FAPI%2FRequestArguments.pm;h=65588db4f1522402b661c30508c341e3461b8d6a;hp=523c07f9e4b4df0788da67b6efcdda5a75a108b4;hb=8ea592cbf64db6d549685f3962c88d925fe7cdb1;hpb=4e5983f2a904f894762e61c2dff962a37da5ef92 diff --git a/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm b/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm index 523c07f..65588db 100644 --- a/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm +++ b/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm @@ -30,9 +30,9 @@ A Catalyst::Controller::DBIC::API::Validator instance used solely to validate pr =cut has [qw( search_validator select_validator )] => ( - is => 'ro', - isa => 'Catalyst::Controller::DBIC::API::Validator', - lazy => 1, + is => 'ro', + isa => 'Catalyst::Controller::DBIC::API::Validator', + lazy => 1, builder => '_build_validator', ); @@ -43,16 +43,14 @@ sub _build_validator { parameter static => ( isa => Bool, default => 0 ); role { - my $p = shift; - if($p->static) - { - requires qw/check_has_relation check_column_relation prefetch_allows /; + if ( $p->static ) { + requires + qw( check_has_relation check_column_relation prefetch_allows ); } - else - { - requires qw/_controller check_has_relation check_column_relation/; + else { + requires qw( _controller check_has_relation check_column_relation ); } =attribute_public count is: ro, isa: Int @@ -61,11 +59,10 @@ count is the number of rows to be returned during paging =cut - has 'count' => - ( - is => 'ro', - writer => '_set_count', - isa => Int, + has 'count' => ( + is => 'ro', + writer => '_set_count', + isa => Int, predicate => 'has_count', ); @@ -75,11 +72,10 @@ page is what page to return while paging =cut - has 'page' => - ( - is => 'ro', - writer => '_set_page', - isa => Int, + has 'page' => ( + is => 'ro', + writer => '_set_page', + isa => Int, predicate => 'has_page', ); @@ -89,11 +85,10 @@ offset specifies where to start the paged result (think SQL LIMIT) =cut - has 'offset' => - ( - is => 'ro', - writer => '_set_offset', - isa => Int, + has 'offset' => ( + is => 'ro', + writer => '_set_offset', + isa => Int, predicate => 'has_offset', ); @@ -103,14 +98,13 @@ ordered_by is passed to ->search to determine sorting =cut - has 'ordered_by' => - ( - is => 'ro', - writer => '_set_ordered_by', - isa => OrderedBy, + has 'ordered_by' => ( + is => 'ro', + writer => '_set_ordered_by', + isa => OrderedBy, predicate => 'has_ordered_by', - coerce => 1, - default => sub { $p->static ? [] : undef }, + coerce => 1, + default => sub { $p->static ? [] : undef }, ); =attribute_public groupd_by is: ro, isa: L @@ -119,14 +113,13 @@ grouped_by is passed to ->search to determine aggregate results =cut - has 'grouped_by' => - ( - is => 'ro', - writer => '_set_grouped_by', - isa => GroupedBy, + has 'grouped_by' => ( + is => 'ro', + writer => '_set_grouped_by', + isa => GroupedBy, predicate => 'has_grouped_by', - coerce => 1, - default => sub { $p->static ? [] : undef }, + coerce => 1, + default => sub { $p->static ? [] : undef }, ); =attribute_public prefetch is: ro, isa: L @@ -135,28 +128,26 @@ prefetch is passed to ->search to optimize the number of database fetches for jo =cut - has prefetch => - ( - is => 'ro', - writer => '_set_prefetch', - isa => Prefetch, + has prefetch => ( + is => 'ro', + writer => '_set_prefetch', + isa => Prefetch, default => sub { $p->static ? [] : undef }, - coerce => 1, - trigger => sub - { - my ($self, $new) = @_; - - foreach my $pf (@$new) - { - if(HashRef->check($pf)) - { - die qq|'${\Dumper($pf)}' is not an allowed prefetch in: ${\join("\n", @{$self->prefetch_validator->templates})}| + coerce => 1, + trigger => sub { + my ( $self, $new ) = @_; + + foreach my $pf (@$new) { + if ( HashRef->check($pf) ) { + die + qq|'${\Dumper($pf)}' is not an allowed 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 { + die + qq|'$pf' is not an allowed prefetch in: ${\join("\n", @{$self->prefetch_validator->templates})}| + unless $self->prefetch_validator->validate( + { $pf => 1 } )->[0]; } } }, @@ -170,16 +161,14 @@ Like the synopsis in DBIC::API shows, you can declare a "template" of what is al =cut - has 'search_exposes' => - ( - is => 'ro', - writer => '_set_search_exposes', - isa => ArrayRef[Str|HashRef], + has 'search_exposes' => ( + is => 'ro', + writer => '_set_search_exposes', + isa => ArrayRef [ Str | HashRef ], predicate => 'has_search_exposes', - default => sub { [ ] }, - trigger => sub - { - my ($self, $new) = @_; + default => sub { [] }, + trigger => sub { + my ( $self, $new ) = @_; $self->search_validator->load($_) for @$new; }, ); @@ -192,41 +181,36 @@ Please see L for details on how the format work =cut - has 'search' => - ( - is => 'ro', - writer => '_set_search', - isa => SearchParameters, + has 'search' => ( + is => 'ro', + writer => '_set_search', + isa => SearchParameters, predicate => 'has_search', - coerce => 1, - trigger => sub - { - my ($self, $new) = @_; + coerce => 1, + trigger => sub { + my ( $self, $new ) = @_; - if($self->has_search_exposes and @{$self->search_exposes}) - { - foreach my $foo (@$new) - { - while( my ($k, $v) = each %$foo) - { + if ( $self->has_search_exposes and @{ $self->search_exposes } ) { + foreach my $foo (@$new) { + while ( my ( $k, $v ) = each %$foo ) { 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]; + 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 - { - foreach my $foo (@$new) - { - while( my ($k, $v) = each %$foo) - { - $self->check_column_relation({$k => $v}); + else { + foreach my $foo (@$new) { + while ( my ( $k, $v ) = each %$foo ) { + $self->check_column_relation( { $k => $v } ); } } } - my ($search_parameters, $search_attributes) = $self->generate_parameters_attributes($new); + my ( $search_parameters, $search_attributes ) = + $self->generate_parameters_attributes($new); $self->_set_search_parameters($search_parameters); $self->_set_search_attributes($search_attributes); @@ -239,14 +223,13 @@ search_parameters stores the formatted search parameters that will be passed to =cut - has search_parameters => - ( - is => 'ro', - isa => SearchParameters, - writer => '_set_search_parameters', + has search_parameters => ( + is => 'ro', + isa => SearchParameters, + writer => '_set_search_parameters', predicate => 'has_search_parameters', - coerce => 1, - default => sub { [{}] }, + coerce => 1, + default => sub { [ {} ] }, ); =attribute_public search_attributes is:ro, isa: HashRef @@ -255,12 +238,11 @@ search_attributes stores the formatted search attributes that will be passed to =cut - has search_attributes => - ( - is => 'ro', - isa => HashRef, - writer => '_set_search_attributes', - predicate => 'has_search_attributes', + has search_attributes => ( + is => 'ro', + isa => HashRef, + writer => '_set_search_attributes', + predicate => 'has_search_attributes', lazy_build => 1, ); @@ -270,11 +252,10 @@ search_total_entries stores the total number of entries in a paged search result =cut - has search_total_entries => - ( - is => 'ro', - isa => Int, - writer => '_set_search_total_entries', + has search_total_entries => ( + is => 'ro', + isa => Int, + writer => '_set_search_total_entries', predicate => 'has_search_total_entries', ); @@ -286,16 +267,14 @@ Like the synopsis in DBIC::API shows, you can declare a "template" of what is al =cut - has 'select_exposes' => - ( - is => 'ro', - writer => '_set_select_exposes', - isa => ArrayRef[Str|HashRef], + has 'select_exposes' => ( + is => 'ro', + writer => '_set_select_exposes', + isa => ArrayRef [ Str | HashRef ], predicate => 'has_select_exposes', - default => sub { [ ] }, - trigger => sub - { - my ($self, $new) = @_; + default => sub { [] }, + trigger => sub { + my ( $self, $new ) = @_; $self->select_validator->load($_) for @$new; }, ); @@ -308,28 +287,23 @@ Please see L for more details. =cut - has select => - ( - is => 'ro', - writer => '_set_select', - isa => SelectColumns, + has select => ( + is => 'ro', + writer => '_set_select', + isa => SelectColumns, predicate => 'has_select', - default => sub { $p->static ? [] : undef }, - coerce => 1, - trigger => sub - { - my ($self, $new) = @_; - if($self->has_select_exposes) - { - foreach my $val (@$new) - { + default => sub { $p->static ? [] : undef }, + 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; + else { + $self->check_column_relation( $_, $p->static ) for @$new; } }, ); @@ -342,22 +316,19 @@ Please see L for more details. =cut - has as => - ( - is => 'ro', - writer => '_set_as', - isa => AsAliases, + has as => ( + is => 'ro', + writer => '_set_as', + isa => AsAliases, default => sub { $p->static ? [] : undef }, - trigger => sub - { - my ($self, $new) = @_; - if($self->has_select) - { - die "'as' argument count (${\scalar(@$new)}) must match 'select' argument count (${\scalar(@{$self->select || []})})" - unless @$new == @{$self->select || []}; + trigger => sub { + my ( $self, $new ) = @_; + if ( $self->has_select ) { + die + "'as' argument count (${\scalar(@$new)}) must match 'select' argument count (${\scalar(@{$self->select || []})})" + unless @$new == @{ $self->select || [] }; } - elsif(defined $new) - { + elsif ( defined $new ) { die "'as' is only valid if 'select is also provided'"; } } @@ -373,15 +344,11 @@ Provides a single handle which returns the 'join' attribute for search_attribute =cut - has joins => - ( - is => 'ro', - isa => JoinBuilder, + has joins => ( + is => 'ro', + isa => JoinBuilder, lazy_build => 1, - handles => - { - build_joins => 'joins', - } + handles => { build_joins => 'joins', } ); =attribute_public request_data is: ro, isa: HashRef @@ -390,30 +357,40 @@ request_data holds the raw (but deserialized) data for ths request =cut - has 'request_data' => - ( - is => 'ro', - isa => HashRef, - writer => '_set_request_data', + has 'request_data' => ( + is => 'ro', + isa => HashRef, + writer => '_set_request_data', predicate => 'has_request_data', - trigger => sub - { - my ($self, $new) = @_; + trigger => sub { + my ( $self, $new ) = @_; my $controller = $self->_controller; return unless defined($new) && keys %$new; - $self->_set_prefetch($new->{$controller->prefetch_arg}) if exists $new->{$controller->prefetch_arg}; - $self->_set_select($new->{$controller->select_arg}) if exists $new->{$controller->select_arg}; - $self->_set_as($new->{$controller->as_arg}) if exists $new->{$controller->as_arg}; - $self->_set_grouped_by($new->{$controller->grouped_by_arg}) if exists $new->{$controller->grouped_by_arg}; - $self->_set_ordered_by($new->{$controller->ordered_by_arg}) if exists $new->{$controller->ordered_by_arg}; - $self->_set_count($new->{$controller->count_arg}) if exists $new->{$controller->count_arg}; - $self->_set_page($new->{$controller->page_arg}) if exists $new->{$controller->page_arg}; - $self->_set_offset($new->{$controller->offset_arg}) if exists $new->{$controller->offset_arg}; - $self->_set_search($new->{$controller->search_arg}) if exists $new->{$controller->search_arg}; + $self->_set_prefetch( $new->{ $controller->prefetch_arg } ) + if exists $new->{ $controller->prefetch_arg }; + $self->_set_select( $new->{ $controller->select_arg } ) + if exists $new->{ $controller->select_arg }; + $self->_set_as( $new->{ $controller->as_arg } ) + if exists $new->{ $controller->as_arg }; + $self->_set_grouped_by( $new->{ $controller->grouped_by_arg } ) + if exists $new->{ $controller->grouped_by_arg }; + $self->_set_ordered_by( $new->{ $controller->ordered_by_arg } ) + if exists $new->{ $controller->ordered_by_arg }; + $self->_set_count( $new->{ $controller->count_arg } ) + if exists $new->{ $controller->count_arg }; + $self->_set_page( $new->{ $controller->page_arg } ) + if exists $new->{ $controller->page_arg }; + $self->_set_offset( $new->{ $controller->offset_arg } ) + if exists $new->{ $controller->offset_arg }; + $self->_set_search( $new->{ $controller->search_arg } ) + if exists $new->{ $controller->search_arg }; } ); - method _build_joins => sub { return Catalyst::Controller::DBIC::API::JoinBuilder->new(name => 'TOP') }; + method _build_joins => sub { + return Catalyst::Controller::DBIC::API::JoinBuilder->new( + name => 'TOP' ); + }; =method_protected format_search_parameters @@ -421,15 +398,19 @@ format_search_parameters iterates through the provided params ArrayRef, calling =cut - method format_search_parameters => sub - { - my ($self, $params) = @_; + method format_search_parameters => sub { + my ( $self, $params ) = @_; my $genparams = []; - foreach my $param (@$params) - { - push(@$genparams, $self->generate_column_parameters($self->stored_result_source, $param, $self->joins)); + foreach my $param (@$params) { + push( + @$genparams, + $self->generate_column_parameters( + $self->stored_result_source, + $param, $self->joins + ) + ); } return $genparams; @@ -441,45 +422,52 @@ generate_column_parameters recursively generates properly aliased parameters for =cut - method generate_column_parameters => sub - { - my ($self, $source, $param, $join, $base) = @_; + method generate_column_parameters => sub { + my ( $self, $source, $param, $join, $base ) = @_; $base ||= 'me'; my $search_params = {}; # build up condition - foreach my $column (keys %$param) - { - if ($source->has_relationship($column)) - { + foreach my $column ( keys %$param ) { + if ( $source->has_relationship($column) ) { + # check if the value isn't a hashref - unless (ref($param->{$column}) && reftype($param->{$column}) eq 'HASH') + unless ( ref( $param->{$column} ) + && reftype( $param->{$column} ) eq 'HASH' ) { - $search_params->{join('.', $base, $column)} = $param->{$column}; + $search_params->{ join( '.', $base, $column ) } = + $param->{$column}; next; } - $search_params = { %$search_params, %{ - $self->generate_column_parameters - ( - $source->related_source($column), - $param->{$column}, - Catalyst::Controller::DBIC::API::JoinBuilder->new(parent => $join, name => $column), - $column - ) - }}; + $search_params = { + %$search_params, + %{ $self->generate_column_parameters( + $source->related_source($column), + $param->{$column}, + Catalyst::Controller::DBIC::API::JoinBuilder->new( + parent => $join, + name => $column + ), + $column + ) + } + }; } - elsif ($source->has_column($column)) - { - $search_params->{join('.', $base, $column)} = $param->{$column}; + elsif ( $source->has_column($column) ) { + $search_params->{ join( '.', $base, $column ) } = + $param->{$column}; } + # might be a sql function instead of a column name # e.g. {colname => {like => '%foo%'}} - else - { + else { # but only if it's not a hashref - unless (ref($param->{$column}) && reftype($param->{$column}) eq 'HASH') { - $search_params->{join('.', $base, $column)} = $param->{$column}; + unless ( ref( $param->{$column} ) + && reftype( $param->{$column} ) eq 'HASH' ) + { + $search_params->{ join( '.', $base, $column ) } = + $param->{$column}; } else { die "$column is neither a relationship nor a column\n"; @@ -496,11 +484,11 @@ generate_parameters_attributes takes the raw search arguments and formats the pa =cut - method generate_parameters_attributes => sub - { - my ($self, $args) = @_; + method generate_parameters_attributes => sub { + my ( $self, $args ) = @_; - return ( $self->format_search_parameters($args), $self->search_attributes ); + return ( $self->format_search_parameters($args), + $self->search_attributes ); }; =method_protected _build_search_attributes @@ -509,61 +497,78 @@ This builder method generates the search attributes =cut - method _build_search_attributes => sub - { - my ($self, $args) = @_; - my $static = $self->_controller; - 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), - select => $self->select || ((scalar(@{$static->select})) ? $static->select : undef), - as => $self->as || ((scalar(@{$static->as})) ? $static->as : undef), + method _build_search_attributes => sub { + my ( $self, $args ) = @_; + my $static = $self->_controller; + 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 + ), + select => $self->select + || ( + ( scalar( @{ $static->select } ) ) ? $static->select + : undef + ), + as => $self->as + || ( ( scalar( @{ $static->as } ) ) ? $static->as : undef ), prefetch => $self->prefetch || $static->prefetch || undef, - rows => $self->count || $static->count, - page => $static->page, - offset => $self->offset, - join => $self->build_joins, + rows => $self->count || $static->count, + page => $static->page, + offset => $self->offset, + join => $self->build_joins, }; - if($self->has_page) - { + if ( $self->has_page ) { $search_attributes->{page} = $self->page; } - elsif(!$self->has_page && defined($search_attributes->{offset}) && defined($search_attributes->{rows})) + elsif (!$self->has_page + && defined( $search_attributes->{offset} ) + && defined( $search_attributes->{rows} ) ) { - $search_attributes->{page} = $search_attributes->{offset} / $search_attributes->{rows} + 1; + $search_attributes->{page} = + $search_attributes->{offset} / $search_attributes->{rows} + 1; delete $search_attributes->{offset}; } - - $search_attributes = - { - map { @$_ } - grep - { - defined($_->[1]) - ? - (ref($_->[1]) && reftype($_->[1]) eq 'HASH' && keys %{$_->[1]}) - || (ref($_->[1]) && reftype($_->[1]) eq 'ARRAY' && @{$_->[1]}) - || length($_->[1]) - : - undef - } - map { [$_, $search_attributes->{$_}] } - keys %$search_attributes + $search_attributes = { + map {@$_} + grep { + defined( $_->[1] ) + ? ( ref( $_->[1] ) + && reftype( $_->[1] ) eq 'HASH' + && keys %{ $_->[1] } ) + || ( ref( $_->[1] ) + && reftype( $_->[1] ) eq 'ARRAY' + && @{ $_->[1] } ) + || length( $_->[1] ) + : undef + } + map { [ $_, $search_attributes->{$_} ] } + keys %$search_attributes }; - - if ($search_attributes->{page} && !$search_attributes->{rows}) { + if ( $search_attributes->{page} && !$search_attributes->{rows} ) { die 'list_page can only be used with list_count'; } - if ($search_attributes->{select}) { + 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') # to pass through unmolested - $search_attributes->{select} = [map { (Str->check($_) && $_ !~ m/\./) ? "me.$_" : $_ } (ref $search_attributes->{select}) ? @{$search_attributes->{select}} : $search_attributes->{select}]; + $search_attributes->{select} = [ + map { ( Str->check($_) && $_ !~ m/\./ ) ? "me.$_" : $_ } + ( ref $search_attributes->{select} ) + ? @{ $search_attributes->{select} } + : $search_attributes->{select} + ]; } return $search_attributes; @@ -571,11 +576,16 @@ This builder method generates the search attributes }; }; + =head1 DESCRIPTION -RequestArguments embodies those arguments that are provided as part of a request or effect validation on request arguments. This Role can be consumed in one of two ways. As this is a parameterized Role, it accepts a single argument at composition time: 'static'. This indicates that those parameters should be stored statically and used as a fallback when the current request doesn't provide them. +RequestArguments embodies those arguments that are provided as part of a request +or effect validation on request arguments. This Role can be consumed in one of +two ways. As this is a parameterized Role, it accepts a single argument at +composition time: 'static'. This indicates that those parameters should be +stored statically and used as a fallback when the current request doesn't +provide them. =cut - 1;