don't document private method that probabliy no one sane would override
[catagits/Catalyst-Controller-DBIC-API.git] / lib / Catalyst / Controller / DBIC / API / RequestArguments.pm
index adba96d..107dc80 100644 (file)
@@ -6,497 +6,426 @@ use Catalyst::Controller::DBIC::API::Types(':all');
 use MooseX::Types::Moose(':all');
 use Scalar::Util('reftype');
 use Data::Dumper;
+use Catalyst::Controller::DBIC::API::Validator;
 use namespace::autoclean;
 
 use Catalyst::Controller::DBIC::API::JoinBuilder;
 
-
 =attribute_private search_validator
 
-A Catalyst::Controller::DBIC::API::Validator instance used solely to validate search parameters
+A Catalyst::Controller::DBIC::API::Validator instance used solely to validate
+search parameters.
 
 =cut
 
-with 'MooseX::Role::BuildInstanceOf' =>
-{
-    'target' => 'Catalyst::Controller::DBIC::API::Validator',
-    'prefix' => 'search_validator',
-};
-
 =attribute_private select_validator
 
-A Catalyst::Controller::DBIC::API::Validator instance used solely to validate select parameters
+A Catalyst::Controller::DBIC::API::Validator instance used solely to validate
+select parameters.
 
 =cut
 
-with 'MooseX::Role::BuildInstanceOf' =>
-{
-    'target' => 'Catalyst::Controller::DBIC::API::Validator',
-    'prefix' => 'select_validator',
-};
-
 =attribute_private prefetch_validator
 
-A Catalyst::Controller::DBIC::API::Validator instance used solely to validate prefetch parameters
+A Catalyst::Controller::DBIC::API::Validator instance used solely to validate
+prefetch parameters.
 
 =cut
 
-with 'MooseX::Role::BuildInstanceOf' =>
-{
-    'target' => 'Catalyst::Controller::DBIC::API::Validator',
-    'prefix' => 'prefetch_validator',
-};
+has [qw( search_validator select_validator )] => (
+    is      => 'ro',
+    isa     => 'Catalyst::Controller::DBIC::API::Validator',
+    lazy    => 1,
+    builder => '_build_validator',
+);
+
+sub _build_validator {
+    return Catalyst::Controller::DBIC::API::Validator->new;
+}
 
 parameter static => ( isa => Bool, default => 0 );
 
 role {
-
     my $p = shift;
 
-    if($p->static)
-    {
-        requires qw/check_has_relation check_column_relation/;
+    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
+=attribute_public count
 
-count is the number of rows to be returned during paging
+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',
     );
 
-=attribute_public page is: ro, isa: Int
+=attribute_public page
 
-page is what page to return while paging
+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',
     );
 
-=attribute_public offset is ro, isa: Int
+=attribute_public offset
 
-offset specifies where to start the paged result (think SQL LIMIT)
+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',
     );
 
-=attribute_public ordered_by is: ro, isa: L<Catalyst::Controller::DBIC::API::Types/OrderedBy>
+=attribute_public ordered_by
 
-ordered_by is passed to ->search to determine sorting
+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<Catalyst::Controller::DBIC::API::Types/GroupedBy>
+=attribute_public groupd_by
 
-grouped_by is passed to ->search to determine aggregate results
+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<Catalyst::Controller::DBIC::API::Types/Prefetch>
+=attribute_public prefetch
 
-prefetch is passed to ->search to optimize the number of database fetches for joins
+Is passed to ->search to optimize the number of database fetches for joins.
 
 =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) = @_;
-            if($self->has_prefetch_allows and @{$self->prefetch_allows})
-            {
-                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];
-                    }
+        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
-            {
-                return if not defined($new);
-                die 'Prefetching is not allowed' if @$new;
-            }
-        },
-    );
-
-=attribute_public prefetch_allows is: ro, isa: ArrayRef[ArrayRef|Str|HashRef]
-
-prefetch_allows limits what relations may be prefetched when executing searches with joins. 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.
-
-Like the synopsis in DBIC::API shows, you can declare a "template" of what is allowed (by using an '*'). Each element passed in, will be converted into a Data::DPath and added to the validator.
-
-    prefetch_allows => [ 'cds', { cds => tracks }, { cds => producers } ] # to be explicit
-    prefetch_allows => [ 'cds', { cds => '*' } ] # wildcard means the same thing
-
-=cut
-
-    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) = @_;
-
-            sub check_rel {
-                my ($self, $rel, $static) = @_;
-                if(ArrayRef->check($rel))
-                {
-                    foreach my $rel_sub (@$rel)
-                    {
-                        $self->check_rel($rel_sub, $static);
-                    }
+                else {
+                    die
+                        qq|'$pf' is not an allowed prefetch in: ${\join("\n", @{$self->prefetch_validator->templates})}|
+                        unless $self->prefetch_validator->validate(
+                        { $pf => 1 } )->[0];
                 }
-                elsif(HashRef->check($rel))
-                {
-                    while(my($k,$v) = each %$rel)
-                    {
-                        $self->check_has_relation($k, $v, undef, $static);
-                    }
-                    $self->prefetch_validator->load($rel);
-                }
-                else
-                {
-                    $self->check_has_relation($rel, undef, undef, $static);
-                    $self->prefetch_validator->load($rel);
-                }
-            }
-
-            foreach my $rel (@$new)
-            {
-                $self->check_rel($rel, $p->static);
             }
         },
     );
 
-=attribute_public search_exposes is: ro, isa: ArrayRef[Str|HashRef]
+=attribute_public search_exposes
 
-search_exposes limits what can actually be searched. If a certain column isn't indexed or perhaps a BLOB, you can explicitly say which columns can be search and exclude that one.
+Limits what can actually be searched. If a certain column isn't indexed or
+perhaps a BLOB, you can explicitly say which columns can be search to exclude
+that one.
 
-Like the synopsis in DBIC::API shows, you can declare a "template" of what is allowed (by using an '*'). Each element passed in, will be converted into a Data::DPath and added to the validator.
+Like the synopsis in DBIC::API shows, you can declare a "template" of what is
+allowed (by using '*'). Each element passed in, will be converted into a
+Data::DPath and added to the validator.
 
 =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;
         },
     );
 
-=attribute_public search is: ro, isa: L<Catalyst::Controller::DBIC::API::Types/SearchParameters>
+=attribute_public search
 
-search contains the raw search parameters. Upon setting, a trigger will fire to format them, set search_parameters, and set search_attributes.
+Contains the raw search parameters. Upon setting, a trigger will fire to format
+them, set search_parameters and search_attributes.
 
 Please see L</generate_parameters_attributes> for details on how the format works.
 
 =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);
 
         },
     );
 
-=attribute_public search_parameters is:ro, isa: L<Catalyst::Controller::DBIC::API::Types/SearchParameters>
+=attribute_public search_parameters
 
-search_parameters stores the formatted search parameters that will be passed to ->search
+Stores the formatted search parameters that will be passed to ->search.
 
 =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
+=attribute_public search_attributes
 
-search_attributes stores the formatted search attributes that will be passed to ->search
+Stores the formatted search attributes that will be passed to ->search.
 
 =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,
     );
 
-=attribute_public search_total_entries is: ro, isa: Int
+=attribute_public search_total_entries
 
-search_total_entries stores the total number of entries in a paged search result
+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',
     );
 
-=attribute_public select_exposes is: ro, isa: ArrayRef[Str|HashRef]
+=attribute_public select_exposes
 
-select_exposes limits what can actually be selected. Use this to whitelist database functions (such as COUNT).
+Limits what can actually be selected. Use this to whitelist database functions
+(such as COUNT).
 
-Like the synopsis in DBIC::API shows, you can declare a "template" of what is allowed (by using an '*'). Each element passed in, will be converted into a Data::DPath and added to the validator.
+Like the synopsis in DBIC::API shows, you can declare a "template" of what is
+allowed (by using '*'). Each element passed in, will be converted into a
+Data::DPath and added to the validator.
 
 =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;
         },
     );
 
-=attribute_public select is: ro, isa: L<Catalyst::Controller::DBIC::API::Types/SelectColumns>
+=attribute_public select
 
-select is the search attribute that allows you to both limit what is returned in the result set, and also make use of database functions like COUNT.
+Is the search attribute that allows you to both limit what is returned in the
+result set and also make use of database functions like COUNT.
 
 Please see L<DBIx::Class::ResultSet/select> 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;
             }
         },
     );
 
-=attribute_public as is: ro, isa: L<Catalyst::Controller::DBIC::API::Types/AsAliases>
+=attribute_public as
 
-as is the search attribute compliment to L</select> that allows you to label columns for object inflaction and actually reference database functions like COUNT.
+Is the search attribute compliment to L</select> that allows you to label
+columns for object inflaction and actually reference database functions like
+COUNT.
 
 Please see L<DBIx::Class::ResultSet/as> 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'";
             }
         }
     );
 
-=attribute_public joins is: ro, isa L<Catalyst::Controller::DBIC::API::Types/JoinBuilder>
+=attribute_public joins
 
-joins holds the top level JoinBuilder object used to keep track of joins automagically while formatting complex search parameters.
+Holds the top level JoinBuilder object used to keep track of joins automagically
+while formatting complex search parameters.
 
-Provides a single handle which returns the 'join' attribute for search_attributes:
-
-    build_joins => 'joins'
+Provides the method 'build_joins' which returns the 'join' attribute for
+search_attributes.
 
 =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
+=attribute_public request_data
 
-request_data holds the raw (but deserialized) data for ths request
+Holds the raw (but deserialized) data for this 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
 
-format_search_parameters iterates through the provided params ArrayRef, calling generate_column_parameters on each one
+Iterates through the provided arrayref calling generate_column_parameters on
+each one.
 
 =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;
@@ -504,41 +433,61 @@ format_search_parameters iterates through the provided params ArrayRef, calling
 
 =method_protected generate_column_parameters
 
-generate_column_parameters recursively generates properly aliased parameters for search, building a new JoinBuilder each layer of recursion
+Recursively generates properly aliased parameters for search building a new
+JoinBuilder each layer of recursion.
 
 =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;
+        my $search_params = {};
 
         # build up condition
-        foreach my $column (keys %$param)
-        {
-            if($source->has_relationship($column))
-            {
-                unless (ref($param->{$column}) && reftype($param->{$column}) eq 'HASH')
+        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' )
                 {
-                    $search_params->{join('.', $base, $column)} = $param->{$column};
+                    $search_params->{ join( '.', $base, $column ) } =
+                        $param->{$column};
                     next;
                 }
 
-                %$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
+                        )
+                    }
                 };
             }
-            else
-            {
-                $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 {
+                # but only if it's not a hashref
+                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";
+                }
             }
         }
 
@@ -547,77 +496,94 @@ generate_column_parameters recursively generates properly aliased parameters for
 
 =method_protected generate_parameters_attributes
 
-generate_parameters_attributes takes the raw search arguments and formats the parameters by calling format_search_parameters. Then builds the related attributes, preferring request-provided arguments for things like grouped_by over statically configured options. Finally tacking on the appropriate joins. Returns both formatted search parameters and the search attributes.
+Takes the raw search arguments and formats them by calling
+format_search_parameters. Then builds the related attributes, preferring
+request-provided arguments for things like grouped_by over statically configured
+options. Finally tacking on the appropriate joins.
+
+Returns a list of both formatted search parameters and attributes.
 
 =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
-
-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,
-            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;
@@ -625,11 +591,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;