File Coverage

blib/lib/Rose/DBx/CannedQuery.pm
Criterion Covered Total %
statement 31 85 36.4
branch 4 32 12.5
condition 1 8 12.5
subroutine 10 19 52.6
pod 6 6 100.0
total 52 150 34.6


line stmt bran cond sub pod time code
1             #!perl
2             #
3              
4 2     2   185258 use strict;
  2         3  
  2         67  
5 2     2   8 use warnings;
  2         3  
  2         88  
6              
7             package Rose::DBx::CannedQuery;
8              
9             our ($VERSION) = '1.00';
10              
11 2     2   7 use Carp;
  2         2  
  2         113  
12 2     2   6 use Scalar::Util;
  2         2  
  2         60  
13              
14 2     2   820 use Moo 2;
  2         15898  
  2         8  
15 2     2   2586 use Rose::DB; # Marker for prerequisite
  2         160202  
  2         55  
16 2     2   992 use Types::Standard qw/ Str HashRef InstanceOf Object /;
  2         85128  
  2         23  
17              
18             has 'sql' => ( is => 'ro', isa => Str, required => 1 );
19              
20             has 'rdb_class' => (
21             is => 'ro',
22             isa => Str,
23             required => 0,
24             lazy => 1,
25             builder => '_retcon_rdb_class'
26             );
27              
28             sub _retcon_rdb_class {
29 0     0   0 my $self = shift;
30 0 0       0 Carp::croak "Can't recover Rose::DB class information without a handle"
31             unless $self->_has_rdb; # Use predicate to avoid recursion with _init_rdb
32 0         0 my $class = Scalar::Util::blessed $self->rdb;
33              
34             # Ugly hack
35             # Rose::DB creates handles in private classes; this may fail in the
36             # future, as it relies on undocumented Rose::DB behavior
37 0         0 $class =~ s/::__RoseDBPrivate__.*$//;
38 0         0 return $class;
39             }
40              
41             has 'rdb_params' => (
42             is => 'ro',
43             isa => HashRef,
44             required => 0,
45             lazy => 1,
46             builder => '_retcon_rdb_params'
47             );
48              
49             sub _retcon_rdb_params {
50 0     0   0 my $self = shift;
51 0 0       0 Carp::croak "Can't recover Rose::DB datasource information without a handle"
52             unless $self->_has_rdb; # Use predicate to avoid recursion with _init_rdb
53 0         0 my $rdb = $self->rdb;
54 0         0 return { type => $rdb->type, domain => $rdb->domain };
55             }
56              
57             has 'rdb' => (
58             is => 'ro',
59             isa => InstanceOf ['Rose::DB'],
60             required => 0,
61             lazy => 1,
62             predicate => '_has_rdb',
63             builder => '_init_rdb',
64             handles => ['dbh']
65             );
66              
67             sub _default_rdb_params {
68             return {
69 1     1   310 connect_options => {
70             RaiseError => 1,
71             PrintError => 0,
72             AutoCommit => 1
73             }
74             };
75             }
76              
77             sub _init_rdb {
78 1     1   452 my $self = shift;
79 1         4 my $class = $self->rdb_class;
80 1 50 33     485 defined $class and eval "$class->can('new_or_cached') || require $class"
81             or Carp::croak "Failed to load class $class: $@";
82 0         0 return $self->rdb_class->new_or_cached( %{ $self->_default_rdb_params },
  0         0  
83 0         0 %{ $self->rdb_params } );
84             }
85              
86             has 'sth' => (
87             is => 'ro',
88             isa => Object, # DBI's sth class is private
89             required => 0,
90             init_arg => undef,
91             lazy => 1,
92             builder => '_init_sth'
93             );
94              
95             sub setup_dbh_for_query {
96 0     0 1 0 my ($self) = @_;
97 0         0 my $dbh = $self->rdb->dbh;
98 0         0 $dbh->{FetchHashKeyName} = 'NAME_lc';
99 0         0 return $dbh;
100             }
101              
102             sub _init_sth {
103 0     0   0 my $self = shift;
104 0         0 my $dbh = $self->setup_dbh_for_query;
105 0         0 my $sth = $dbh->prepare( $self->sql );
106 0 0       0 unless ($sth) {
107 0         0 Carp::croak 'Error preparing query: '
108             . $dbh->errstr
109             . "\nSQL was:\n\t"
110             . join( "\n\t", split( /\n/, $self->sql ) ) . "\n";
111 0         0 return;
112             }
113 0         0 return $sth;
114             }
115              
116             sub execute {
117 0     0 1 0 my ( $self, @args ) = @_;
118 0         0 my $sth = $self->sth;
119              
120 0 0       0 unless ( $sth->execute(@args) ) {
121 0         0 Carp::croak 'Error executing query: '
122             . $sth->errstr
123             . "\nArguments were:\n\t"
124             . join( "\n\t", @args ) . "\n";
125 0         0 return;
126             }
127 0         0 return $sth;
128             }
129              
130             sub resultref {
131 0     0 1 0 my ( $self, $args, $opts ) = @_;
132 0   0     0 $opts ||= [ {} ];
133 0 0       0 my $sth = $self->execute( @{ $args || [] } );
  0         0  
134 0         0 return $sth->fetchall_arrayref(@$opts);
135             }
136              
137             sub results {
138 0     0 1 0 my ( $self, @args ) = @_;
139 0         0 return @{ $self->resultref( \@args ) };
  0         0  
140             }
141              
142             sub BUILDARGS {
143 2     2 1 2073 my ( $self, @args ) = @_;
144 2         10 my $canon = $self->SUPER::BUILDARGS(@args);
145 2 50       16 if ( not exists $canon->{rdb} ) {
146 2 100       5 unless ( exists $canon->{rdb_class} ) {
147 1         190 Carp::croak
148             'Need either Rose::DB object or information to construct one';
149 0         0 return;
150             }
151             }
152 1         13 return $canon;
153             }
154              
155             # Simple class data - at least for now, the added dependency of
156             # MooX::ClassAttribute seems more weight than it's worth
157             {
158             my $Query_cache;
159              
160             sub _query_cache {
161 0     0     my $self = shift;
162              
163 0 0 0       Carp::croak("_query_cache() may only be set via a class method call")
164             if ref $self and @_;
165              
166 0 0         if (@_) {
    0          
167 0 0         $Query_cache->clear() if $Query_cache;
168 0           $Query_cache = $_[0];
169             }
170             elsif ( not defined $Query_cache ) {
171 0           require Rose::DBx::CannedQuery::SimpleQueryCache;
172 0           $Query_cache = Rose::DBx::CannedQuery::SimpleQueryCache->new();
173             }
174              
175 0           return $Query_cache;
176             }
177             }
178              
179             sub new_or_cached {
180 0     0 1   my ( $self, @args ) = @_;
181 0 0         my $args_for_cache_key = ref $args[0] ? $args[0] : {@args};
182 0 0         @args = {@args} unless ref $args[0];
183 0           my $query = $self->_query_cache->get_query_from_cache($args_for_cache_key);
184 0 0         if ( not $query ) {
185 0           $query = $self->new(@args);
186 0 0         $self->_query_cache->add_query_to_cache( $query, $args_for_cache_key )
187             if $query;
188             }
189 0           return $query;
190             }
191              
192             1;
193              
194             __END__