File Coverage

blib/lib/Query/Abstract.pm
Criterion Covered Total %
statement 62 68 91.1
branch 15 22 68.1
condition n/a
subroutine 12 14 85.7
pod 3 5 60.0
total 92 109 84.4


line stmt bran cond sub pod time code
1             package Query::Abstract;
2              
3 3     3   65356 use v5.10;
  3         11  
  3         146  
4 3     3   18 use strict;
  3         4  
  3         123  
5 3     3   16 use warnings;
  3         10  
  3         155  
6              
7 3     3   2000 use Class::Load qw/load_class/;
  3         89844  
  3         176  
8 3     3   21 use Carp qw/croak/;
  3         4  
  3         120  
9 3     3   1715 use Data::Dumper;
  3         17421  
  3         1897  
10              
11             our $VERSION = '0.01';
12              
13             sub new {
14 2     2 0 28 my $class = shift;
15 2         8 my %args = @_;
16 2         5 my $driver = $args{driver};
17 2 50       10 croak "Wrong driver" unless ref $driver;
18              
19 2         7 my $self = bless {}, $class;
20              
21 2 50       9 if ( ref $driver eq 'ARRAY' ) {
    0          
22 2         7 my $driver_class = 'Query::Abstract::Driver::' . $driver->[0];
23 2         11 load_class($driver_class);
24              
25 2 100       65 $self->{driver} = $driver_class->new( @{ $driver->[1] || [] } );
  2         41  
26             } elsif ( $driver->isa('Query::Abstract::Driver::Base') ) {
27 0         0 $self->{driver} = $driver;
28             } else {
29 0         0 croak "Wrong driver [$driver]";
30             }
31              
32 2         9 $self->init();
33              
34 2         8 return $self;
35             }
36              
37             sub init {
38 2     2 0 4 my $self = shift;
39 2         16 $self->{driver}->init(@_);
40             }
41              
42              
43             sub convert_query {
44 19     19 1 41579 my ($self, @args) = @_;
45 19         60 my %query = $self->_normalize_query(@args);
46 19         93 return $self->{driver}->convert_query(%query);
47             }
48              
49             sub convert_filter {
50 0     0 1 0 my ($self, $filter) = @_;
51              
52 0         0 return $self->{driver}->convert_filter(
53             $self->_normalize_where($filter)
54             );
55             }
56              
57              
58             sub convert_sort {
59 0     0 1 0 my ($self, $sort_by) = @_;
60              
61 0         0 return $self->{driver}->convert_sort(
62             $self->_normalize_sort_by($sort_by)
63             );
64             }
65              
66             sub _normalize_query {
67 19     19   23 my $self = shift;
68 19         24 my %query;
69              
70 19 100       58 if ( ref($_[0]) eq 'ARRAY' ) {
71 15         31 $query{where} = $_[0];
72             } else {
73 4         9 %query = @_;
74             }
75              
76 19         51 my $where = $self->_normalize_where($query{where});
77 19         59 my $sort_by = $self->_normalize_sort_by($query{sort_by});
78              
79             return (
80 19         77 where => $where,
81             sort_by => $sort_by
82             );
83             }
84              
85             sub _normalize_where {
86 19     19   25 my ($self, $where) = @_;
87 19 50       50 return [] unless $where;
88              
89 19         14 my @norm_where;
90              
91 19         62 for (my $i = 0; $i < @$where; $i+=2) {
92 25         59 my $field = $where->[$i];
93 25         21 my ($oper, $restriction);
94 25 100       55 if ( ref($where->[$i+1]) eq 'HASH' ) {
95 22         30 my $condition = $where->[$i+1];
96 22         64 ($oper, $restriction) = %$condition;
97             } else {
98 3 50       15 $oper = ref($where->[$i+1]) eq 'ARRAY' ? 'in' : 'eq';
99 3         4 $restriction = $where->[$i+1];
100             }
101              
102 325         381 die "UNSUPPORTED OPERATOR [$oper]"
103 25 50       58 unless grep { $oper eq $_ } qw/eq in ne gt lt le gt ge like < > <= >=/;
104              
105 25         95 push @norm_where, $field => {$oper => $restriction} ;
106             }
107              
108 19         38 return \@norm_where;
109             }
110              
111             sub _normalize_sort_by {
112 19     19   37 my ($self, $sort_by) = @_;
113 19 100       49 return [] unless $sort_by;
114 3 100       12 return $sort_by if ref $sort_by eq 'ARRAY';
115             # TODO add validation
116              
117 2         9 return [ split(/\s*,\s*/, $sort_by, 2) ];
118             }
119              
120             1; # End of Query::Abstract
121              
122             =head1 NAME
123              
124             Query::Abstract - Create filters in Perlish way and transforms them into coderefs or SQL
125              
126             =head1 SYNOPSIS
127              
128             # Pure Perl filtering
129             my $qa = Query::Abstract->new( driver => ['ArrayOfHashes'] );
130              
131             my $query_sub = $qa->convert_query(
132             where => [
133             name => 'John',
134             age => { '>' => 25 },
135             last_name => { like => 'ing' }
136             ],
137             sort_by => 'last_name DESC, login ASC'
138             );
139              
140             $filtered_and_sorted_users = $query_sub->(\@users);
141              
142             # Preparing SQL statement
143             my $qa = Query::Abstract->new( driver => ['SQL' => [table => 'users']] );
144              
145             ## The same but explicilty creating driver object.
146             my $qa = Query::Abstract->new( driver => Query::Abstract::Driver::SQL->new(table => 'users') );
147              
148             my $sql_statement = $qa->convert_query(
149             where => [
150             name => 'John',
151             age => { '>' => 25 },
152             last_name => { like => 'ing' }
153             ],
154             sort_by => 'last_name DESC, login ASC'
155             );
156              
157             =head1 WARNING
158              
159             This software is under the heavy development and considered ALPHA quality.
160             Things might be broken, not all features have been implemented, and APIs will be likely to change.
161             YOU HAVE BEEN WARNED.
162              
163             =head1 DESCRIPTION
164              
165             L - allows you to write queries and then tranform them into another format(depends in driver). Queries are almost compatible with Rose::DB::Object queries.
166             This module apperared because I wanted to have pure Perl queries but with ability to convert them into SQL(or other format).
167              
168             Currently this module has two standard drivers - ArrayOfHashes and SQL.(You can write your own)
169              
170             =head1 METHODS
171              
172             =head2 C
173              
174             $self->convert_filter([ name => 'John', age => { '>' => 25 }, last_name => { like => 'ing' } ]);
175              
176             "SQL" Driver will return 'WHERE' clause and bind values.
177              
178             "ArrayOfHashes" will return a coderef which takes hashref and returns true or false depending on condition testing result.
179              
180             my $tester = $self->convert_filter([ name => 'John', age => { '>' => 25 }, last_name => { like => 'ing' } ]);
181             @filtered = grep { $tester->($_) } ( {name => 'Anton', age => 37, last_name => 'Corning'}, {name => 'John'} ... )
182              
183             =head2 C
184              
185             $self->convert_sort('name DESC, age ASC, last_name DESC');
186              
187             "SQL" Driver will return 'ORDER BY' clause.
188              
189             "ArrayOfHashes" will return a coderef for "sort" function
190              
191             my $sort_sub = $self->convert_sort(...);
192             @sorted = sort $sort_sub @data;
193              
194             =head2 C
195              
196             $self->convert_query( where => [name => 'John'], sort_by => 'last_name DESC' );
197              
198             "SQL" Driver will return 'SELECT' with 'WHERE' and 'ORDER BY' conditions.
199              
200             "ArrayOfHashes" will return a coderef for quering data
201              
202             my $query_sub = $self->convert_query(...);
203             $filtered_and_sorted = $query_sub->( \@data );
204              
205             =head1 AUTHOR
206              
207             Viktor Turskyi
208              
209             =head1 BUGS
210              
211             Please report any bugs or feature requests to Github L
212              
213             =head1 SEE ALSO
214              
215             L, L, L
216              
217             =cut