File Coverage

blib/lib/Querylet/Query.pm
Criterion Covered Total %
statement 137 148 92.5
branch 46 50 92.0
condition 3 4 75.0
subroutine 33 35 94.2
pod 32 32 100.0
total 251 269 93.3


line stmt bran cond sub pod time code
1 18     18   188692 use strict;
  18         59  
  18         440  
2 18     18   156 use warnings;
  18         39  
  18         625  
3             package Querylet::Query 0.402;
4             # ABSTRACT: renders and performs queries for Querylet
5              
6             ## no critic RequireCarping
7              
8 18     18   84 use Carp ();
  18         47  
  18         32050  
9              
10             #pod =head1 SYNOPSIS
11             #pod
12             #pod use DBI;
13             #pod my $dbh = DBI->connect('dbi:Pg:dbname=drinks');
14             #pod
15             #pod use Querylet::Query;
16             #pod # Why am I using this package? I'm a human, not Querylet!
17             #pod
18             #pod my $q = new Querylet::Query;
19             #pod
20             #pod $q->set_dbh($dbh);
21             #pod
22             #pod $q->set_query("
23             #pod SELECT *
24             #pod FROM drinks d
25             #pod WHERE abv > [% min_abv %]
26             #pod AND ? IN (
27             #pod SELECT liquor FROM ingredients WHERE i i.drink_id = d.drink_id
28             #pod )
29             #pod ORDER BY d.name
30             #pod ");
31             #pod
32             #pod $q->set_query_vars({ min_abv => 25 });
33             #pod
34             #pod $q->bind("rum");
35             #pod
36             #pod $q->run;
37             #pod
38             #pod $q->output_type('html');
39             #pod
40             #pod $q->output;
41             #pod
42             #pod =head1 DESCRIPTION
43             #pod
44             #pod Querylet::Query is used by Querylet-generated code to make that code go. It
45             #pod renders templatized queries, executes them, and hangs on to the results until
46             #pod they're ready to go to output.
47             #pod
48             #pod This module is probably not particularly useful outside of its use in code
49             #pod written by Querylet, but there you have it.
50             #pod
51             #pod =head1 METHODS
52             #pod
53             #pod =over 4
54             #pod
55             #pod =item new
56             #pod
57             #pod Querylet::Query->new;
58             #pod
59             #pod This creates and returns a new Querylet::Query.
60             #pod
61             #pod =cut
62              
63             sub new {
64 16     16 1 211246 bless {
65             bind_parameters => [],
66             output_type => 'csv',
67             input_type => 'term'
68             } => (shift);
69             }
70              
71             #pod =item set_dbh
72             #pod
73             #pod $q->set_dbh($dbh);
74             #pod
75             #pod This method sets the database handle to be used for running the query.
76             #pod
77             #pod =cut
78              
79             sub set_dbh {
80 14     14 1 24538 my $self = shift;
81 14         47 my $dbh = shift;
82 14         84 $self->{dbh} = $dbh;
83             }
84              
85             #pod =item set_query
86             #pod
87             #pod $q->set_query($query);
88             #pod
89             #pod This method sets the query to run. The query may be a plain SQL query or a
90             #pod template to be rendered later.
91             #pod
92             #pod =cut
93              
94             sub set_query {
95 14     14 1 89 my ($self, $sql) = @_;
96              
97 14         60 $self->{query} = $sql;
98             }
99              
100             #pod =item bind
101             #pod
102             #pod $q->bind(@parameters);
103             #pod
104             #pod This method sets the bind parameters, overwriting any existing parameters.
105             #pod
106             #pod =cut
107              
108             sub bind { ## no critic Homonym
109 1     1 1 7 my ($self, @parameters) = @_;
110 1         6 $self->{bind_parameters} = [ @parameters ];
111             }
112              
113             #pod =item bind_more
114             #pod
115             #pod $q->bind_more(@parameters);
116             #pod
117             #pod This method pushes the given parameters onto the list of bind parameters to use
118             #pod when executing the query.
119             #pod
120             #pod =cut
121              
122             sub bind_more {
123 7     7 1 731 my ($self, @parameters) = @_;
124 7         16 push @{$self->{bind_parameters}}, @parameters;
  7         28  
125             }
126              
127             #pod =item set_query_vars
128             #pod
129             #pod $q->set_query_vars(\%variables);
130             #pod
131             #pod This method sets the given variables, to be used when rendering the query.
132             #pod It also indicates that the query that was given is a template, and should be
133             #pod rendered. (In other words, if this method is called at least once, even with
134             #pod an empty hashref, the query will be considered a template, and rendered.)
135             #pod
136             #pod Note that if query variables are set, but the template rendering engine can't
137             #pod be loaded, the program will die.
138             #pod
139             #pod =cut
140              
141             sub set_query_vars {
142 3     3 1 24 my ($self, $vars) = @_;
143              
144 3   100     20 $self->{query_vars} ||= {};
145 3         5 $self->{query_vars} = { %{$self->{query_vars}}, %$vars };
  3         11  
146             }
147              
148             #pod =item render_query
149             #pod
150             #pod $q->render_query;
151             #pod
152             #pod This method renders the query using a templating engine (Template Toolkit, by
153             #pod default) and returns the result. This method is called internally by the run
154             #pod method, if query variables have been set.
155             #pod
156             #pod Normal Querylet code will not need to call this method.
157             #pod
158             #pod =cut
159              
160             sub render_query {
161 1     1 1 2 my $self = shift;
162 1         1 my $rendered_query;
163              
164 1         13 require Template;
165 1         10 my $tt = new Template;
166 1         18886 $tt->process(\($self->{query}), $self->{query_vars}, \$rendered_query);
167              
168 1         24566 return $rendered_query;
169             }
170              
171             #pod =item run
172             #pod
173             #pod $q->run;
174             #pod
175             #pod This method runs the query and sets up the results. It is called internally by
176             #pod the results method, if the query has not yet been run.
177             #pod
178             #pod Normal Querylet code will not need to call this method.
179             #pod
180             #pod =cut
181              
182             sub run {
183 14     14 1 46 my $self = shift;
184              
185 14 100       64 $self->{query} = $self->render_query if $self->{query_vars};
186              
187 14         194 my $sth = $self->{dbh}->prepare($self->{query});
188 14         4794 $sth->execute(@{$self->{bind_parameters}});
  14         3957  
189              
190 14         371 $self->{columns} = $sth->{NAME};
191              
192 14         284 $self->{results} = $sth->fetchall_arrayref({});
193             }
194              
195             #pod =item results
196             #pod
197             #pod $q->results;
198             #pod
199             #pod This method returns the results of the query, first running the query (by
200             #pod calling C) if needed.
201             #pod
202             #pod The results are returned as a reference to an array of rows, each row a
203             #pod reference to a hash. These are not copies, and may be altered in place.
204             #pod
205             #pod =cut
206              
207             sub results {
208 56     56 1 10510 my $self = shift;
209 56 100       247 return $self->{results} if $self->{results};
210 5         22 $self->run;
211             }
212              
213             #pod =item set_results
214             #pod
215             #pod $q->set_results( \@new_results );
216             #pod
217             #pod This method replaces the result set with the provided results. This method
218             #pod does not call the results method, so if the query has not been run, it will not
219             #pod be run by this method.
220             #pod
221             #pod =cut
222              
223             sub set_results {
224 1     1 1 1054 my $self = shift;
225 1         12 $self->{results} = shift;
226             }
227              
228             #pod =item columns
229             #pod
230             #pod $q->columns;
231             #pod
232             #pod This method returns the column names (as an arrayref) for the query's results.
233             #pod The query will first be run (by calling C) if needed.
234             #pod
235             #pod =cut
236              
237             sub columns {
238 4022     4022 1 507552 my $self = shift;
239 4022 100       10851 return $self->{columns} if $self->{columns};
240 9         54 $self->run;
241 9         7343 return $self->{columns};
242             }
243              
244             #pod =item set_columns
245             #pod
246             #pod $q->set_columns( \@new_columns );
247             #pod
248             #pod This method replaces the list of column names for the current query result. It
249             #pod does not call the columns method, so if the query has not been run, it will not
250             #pod be run by this method.
251             #pod
252             #pod =cut
253              
254             sub set_columns {
255 8     8 1 77 my $self = shift;
256 8         23 $self->{columns} = shift;
257             }
258              
259             #pod =item header
260             #pod
261             #pod $q->header( $column );
262             #pod
263             #pod This method returns the header name for the given column, or the column name,
264             #pod if none is defined.
265             #pod
266             #pod =cut
267              
268             sub header {
269 10     10 1 1944 my $self = shift;
270 10         15 my $column = shift;
271             return exists $self->{headers}{$column}
272 10 50       75 ? $self->{headers}{$column}
273             : $column;
274             }
275              
276             #pod =item set_headers
277             #pod
278             #pod $q->set_headers( \%headers );
279             #pod
280             #pod This method sets up header names for columns. It's passed a list of
281             #pod column-header pairs, which it stores for lookup with the C
method.
282             #pod
283             #pod =cut
284              
285             sub set_headers {
286 0     0 1 0 my $self = shift;
287 0         0 my $headers = shift;
288 0         0 while (my ($column, $header) = each %$headers) {
289 0         0 $self->{headers}{$column} = $header;
290             }
291             }
292              
293             #pod =item option
294             #pod
295             #pod $q->option($option_name);
296             #pod
297             #pod This method returns the named option's value. At present, this just retrieves
298             #pod a scratchpad entry.
299             #pod
300             #pod =cut
301              
302             sub option {
303 5     5 1 32 my ($self, $option_name) = @_;
304 5 100       20 return $self->scratchpad->{$option_name} unless @_ > 2;
305 3         12 return $self->scratchpad->{$option_name} = $_[2];
306             }
307              
308             #pod =item scratchpad
309             #pod
310             #pod $q->scratchpad;
311             #pod
312             #pod This method returns a reference to a hash for general-purpose note-taking.
313             #pod I've put this here for really simple, mediocre communication between handlers.
314             #pod I'm tempted to warn you that it might go away, but I think it's unlikely.
315             #pod
316             #pod =cut
317              
318             sub scratchpad {
319 7     7 1 12 my $self = shift;
320 7 100       22 $self->{scratchpad} = {} unless $self->{scratchpad};
321 7         23 return $self->{scratchpad};
322             }
323              
324             #pod =item input_type
325             #pod
326             #pod $q->input_type($type);
327             #pod
328             #pod This method sets or retrieves the input type, which is used to find the input
329             #pod handler.
330             #pod
331             #pod =cut
332              
333             my %input_handler;
334              
335             sub input_type {
336 17     17 1 26 my $self = shift;
337 17 100       129 return $self->{input_type} unless @_;
338 4         12 return $self->{input_type} = shift;
339             }
340              
341             #pod =item input
342             #pod
343             #pod $q->input($parameter);
344             #pod
345             #pod This method tells the Query to ask the current input handler to request that
346             #pod the named parameter be received from input.
347             #pod
348             #pod =cut
349              
350             sub input {
351 6     6 1 28 my ($self, $parameter) = @_;
352              
353 6 100       25 $self->{input} = {} unless $self->{input};
354 6 100       20 return $self->{input}->{$parameter} if exists $self->{input}->{$parameter};
355              
356 5 100       11 unless ($input_handler{$self->input_type}) {
357 3         7 warn "unknown input type: ", $self->input_type," \n";
358 3         19 return;
359             } else {
360 2         4 $input_handler{$self->input_type}->($self, $parameter);
361             }
362             }
363              
364             #pod =item register_input_handler
365             #pod
366             #pod Querylet::Query->register_input_handler($type => \&handler);
367             #pod
368             #pod This method registers an input handler routine for the given type.
369             #pod
370             #pod If a type is registered that already has a handler, the old handler is quietly
371             #pod replaced. (This makes replacing the built-in, naive handlers quite painless.)
372             #pod
373             #pod =cut
374              
375             sub register_input_handler {
376 21     21 1 11056 shift;
377 21         41 my ($type, $handler) = @_;
378 21         48 $input_handler{$type} = $handler;
379             }
380              
381             #pod =item output_filename
382             #pod
383             #pod $q->output_filename($filename);
384             #pod
385             #pod This method sets a filename to which output should be directed.
386             #pod
387             #pod If called with no arguments, it returns the name. If called with C, it
388             #pod unassigns the currently assigned filename.
389             #pod
390             #pod =cut
391              
392             sub output_filename {
393 19     19 1 10598 my $self = shift;
394 19 100       345 return $self->{output_filename} unless @_;
395              
396 7         17 my $filename = shift;
397              
398 7 100       55 $self->write_type($filename ? 'file' : undef);
399 7         21 return $self->{output_filename} = $filename;
400             }
401              
402             #pod =item write_type
403             #pod
404             #pod $q->write_type($type);
405             #pod
406             #pod This method sets or retrieves the write-out method for the query.
407             #pod
408             #pod =cut
409              
410             my %write_handler;
411              
412             sub write_type {
413 22     22 1 68 my $self = shift;
414 22 100       106 return $self->{write_type} unless @_;
415 8         41 return $self->{write_type} = shift;
416             }
417              
418             #pod =item output_type
419             #pod
420             #pod $q->output_type($type);
421             #pod
422             #pod This method sets or retrieves the format of the output to be generated.
423             #pod
424             #pod =cut
425              
426             my %output_handler;
427              
428             sub output_type {
429 30     30 1 94 my $self = shift;
430 30 100       156 return $self->{output_type} unless @_;
431 9         53 return $self->{output_type} = shift;
432             }
433              
434             #pod =item output
435             #pod
436             #pod $q->output;
437             #pod
438             #pod This method tells the Query to send the current results to the proper output
439             #pod handler and return them. If the outputs have already been generated, they are
440             #pod not re-generated.
441             #pod
442             #pod =cut
443              
444             sub output {
445 17     17 1 7080 my $self = shift;
446              
447 17 100       756 return $self->{output} if exists $self->{output};
448              
449 9 100       39 unless ($output_handler{$self->output_type}) {
450 1         4 warn "unknown output type: ", $self->output_type," \n";
451 1         10 return;
452             } else {
453 8         28 $self->{output} = $output_handler{$self->output_type}->($self);
454 8 100       303 unless ($self->{output}) {
455 1         19 warn "no output received from output handler!\n";
456 1         9 return;
457             }
458 7         42 return $self->{output};
459             }
460             }
461              
462             #pod =item write
463             #pod
464             #pod $q->write;
465             #pod
466             #pod This method tells the Query to send its formatted output to the writing handler
467             #pod and return them.
468             #pod
469             #pod =cut
470              
471             sub write { ## no critic Homonym
472 4     4 1 11 my ($self) = @_;
473              
474 4 100       13 $self->write_type('stdout') unless $self->write_type;
475              
476 4 50       20 unless ($write_handler{$self->write_type}) {
477 0         0 warn "unknown write type: ", $self->write_type," \n";
478 0         0 return;
479             } else {
480 4         12 $write_handler{$self->write_type}->($self);
481             }
482             }
483              
484             #pod =item write_output
485             #pod
486             #pod $q->write_output;
487             #pod
488             #pod This method tells the Query to write the query output. If no filename has been
489             #pod set for output, the results are just printed.
490             #pod
491             #pod If the result of the output method is a coderef, the coderef will be evaluated
492             #pod and nothing will be printed.
493             #pod
494             #pod =cut
495              
496             sub write_output {
497 6     6 1 2062 my ($self) = @_;
498 6         20 my $output = $self->output;
499              
500 6 100       29 if (ref $output eq 'CODE') {
501 2 50       6 warn "using coderef output, but write_type set\n" if $self->write_type;
502 2         5 $output->($self->output_filename);
503             } else {
504 4         17 $self->write($self);
505             }
506             }
507              
508             #pod =item register_output_handler
509             #pod
510             #pod Querylet::Query->register_output_handler($type => \&handler);
511             #pod
512             #pod This method registers an output handler routine for the given type. (The
513             #pod prototype sort of documents itself, doesn't it?)
514             #pod
515             #pod It can be called on an instance, too. It doesn't mind.
516             #pod
517             #pod If a type is registered that already has a handler, the old handler is quietly
518             #pod replaced. (This makes replacing the built-in, naive handlers quite painless.)
519             #pod
520             #pod =cut
521              
522             sub register_output_handler {
523 58     58 1 102 shift;
524 58         110 my ($type, $handler) = @_;
525 58         132 $output_handler{$type} = $handler;
526             }
527              
528             #pod =item as_csv
529             #pod
530             #pod as_csv($q);
531             #pod
532             #pod This is the default, built-in output handler. It outputs the results of the
533             #pod query as a CSV file. That is, a series of comma-delimited fields, with each
534             #pod record separated by a newline.
535             #pod
536             #pod If a output filename was specified, the output is sent to that file (unless it
537             #pod exists). Otherwise, it's printed standard output.
538             #pod
539             #pod =cut
540              
541             __PACKAGE__->register_output_handler(csv => \&as_csv);
542             sub as_csv {
543 4     4 1 11 my $q = shift;
544 4         8 my $csv;
545 4         14 my $results = $q->results;
546 4         11 my $columns = $q->columns;
547 4         13 $csv = join(q{,}, map { $q->header($_) } @$columns) . "\n";
  9         25  
548 4         14 foreach my $row (@$results) {
549             $csv .= join(q{,},
550 18000 100       24522 map { (my $v = defined $_ ? $_ : q{}) =~ s/"/\\"/g; qq!"$v"! }
  18000         31061  
551 8000         11426 @$row{@$columns}
552             )
553             . "\n";
554             }
555              
556 4         158 return $csv;
557             }
558              
559             #pod =item as_template
560             #pod
561             #pod as_template($q);
562             #pod
563             #pod This is the default, built-in output handler. It outputs the results of the
564             #pod query by rendering a template using Template Toolkit. If the option
565             #pod "template_file" is set, the file named in that option is used as the template.
566             #pod If no template_file is set, a built-in template is used, generating a simple
567             #pod HTML document.
568             #pod
569             #pod This handler is by default registered to the types "template" and "html".
570             #pod
571             #pod =cut
572              
573             __PACKAGE__->register_output_handler(template => \&as_template);
574             __PACKAGE__->register_output_handler(html => \&as_template);
575             sub as_template {
576 2     2 1 4 my $query = shift;
577 2         5 my $output;
578 2         14 my $template = $query->option('template_file');
579 2 100       11 unless ($template) {
580 1         2 $template = \(<<'END')
581            
582            
583             results of query
584            
585            
586             [% FOREACH column = query.columns -%][%- END %][% END %]
587            
588             [% FOREACH column = query.columns %]
589             [% query.header(column) %]
590             [% END %]
591            
592             [% FOREACH row = query.results %]
593            
[%- row.$column -%]
594            
595            
596            
597             END
598             }
599              
600 2         24 require Template;
601 2         24 my $tt = new Template({ RELATIVE => 1});
602 2         40200 $tt->process($template, { query => $query }, \$output);
603 2         1141 return $output;
604             }
605              
606             #pod =item register_write_handler
607             #pod
608             #pod Querylet::Query->register_write_handler($type => \&handler);
609             #pod
610             #pod This method registers a write handler routine for the given type.
611             #pod
612             #pod If a type is registered that already has a handler, the old handler is quietly
613             #pod replaced.
614             #pod
615             #pod =cut
616              
617             sub register_write_handler {
618 36     36 1 48 shift;
619 36         55 my ($type, $handler) = @_;
620 36         70 $write_handler{$type} = $handler;
621             }
622              
623             #pod =item to_file
624             #pod
625             #pod This write handler sends the output to a file on the disk.
626             #pod
627             #pod =cut
628              
629             __PACKAGE__->register_write_handler(file => \&to_file);
630             sub to_file {
631 3     3 1 9 my ($query) = @_;
632              
633 3 50       10 if ($query->output_filename) {
634 3 100       22 if (open(my $output_file, '>', $query->output_filename)) {
635 2         10 binmode $output_file;
636 2         68 print $output_file $query->output;
637 2         730 close $output_file;
638             } else {
639 1         6 warn "can't open " . $query->output_filename . " for output\n";
640 1         14 return;
641             }
642             }
643             }
644              
645             #pod =item to_stdout
646             #pod
647             #pod This write handler sends the output to the currently selected output stream.
648             #pod
649             #pod =cut
650              
651             __PACKAGE__->register_write_handler(stdout => \&to_stdout);
652             sub to_stdout {
653 1     1 1 2 my ($query) = @_;
654 1   50     3 print $query->output || '';
655             }
656              
657             #pod =item from_term($q, $parameter)
658             #pod
659             #pod This is a simple built-in input handler to prompt the user interactively for
660             #pod parameter inputs. It is the default input handler.
661             #pod
662             #pod =cut
663              
664             __PACKAGE__->register_input_handler(term => \&from_term);
665             sub from_term {
666 0     0 1   my ($q, $parameter) = @_;
667              
668 0           print "enter $parameter: ";
669 0           my $value = ;
670 0           chomp $value;
671 0           $q->{input}->{$parameter} = $value;
672             }
673              
674             #pod =back
675             #pod
676             #pod =head1 SEE ALSO
677             #pod
678             #pod L, L, L
679             #pod
680             #pod =cut
681              
682             "I do endeavor to give satisfaction, sir.";
683              
684             __END__