File Coverage

blib/lib/ClearPress/util.pm
Criterion Covered Total %
statement 130 147 88.4
branch 25 44 56.8
condition 13 24 54.1
subroutine 33 33 100.0
pod 18 18 100.0
total 219 266 82.3


line stmt bran cond sub pod time code
1             # -*- mode: cperl; tab-width: 8; indent-tabs-mode: nil; basic-offset: 2 -*-
2             # vim:ts=8:sw=2:et:sta:sts=2
3             #########
4             # Author: rmp
5             #
6             package ClearPress::util;
7 11     11   1016791 use strict;
  11         78  
  11         500  
8 11     11   76 use warnings;
  11         51  
  11         382  
9 11     11   73 use base qw(Class::Accessor);
  11         27  
  11         4112  
10 11     11   25762 use Config::IniFiles;
  11         267177  
  11         508  
11 11     11   132 use Carp;
  11         35  
  11         787  
12 11     11   5259 use POSIX qw(strftime);
  11         79898  
  11         98  
13 11     11   21943 use English qw(-no_match_vars);
  11         7587  
  11         94  
14 11     11   10416 use ClearPress::driver;
  11         45  
  11         480  
15 11     11   6508 use CGI;
  11         301197  
  11         91  
16 11     11   5641 use IO::Capture::Stderr;
  11         26627  
  11         379  
17 11     11   3434 use Data::UUID;
  11         6606  
  11         2065  
18              
19             our $VERSION = q[477.1.4];
20              
21             our $DEBUG_UTIL = 0;
22             our $DEFAULT_TRANSACTIONS = 1;
23             our $DEFAULT_DRIVER = 'mysql';
24             my $INSTANCES = {}; # per-process table of singletons (nasty!)
25              
26             __PACKAGE__->mk_accessors(qw(transactions username requestor profiler session));
27              
28             BEGIN {
29 11 0   11   97 use constant MP2 => eval { require Apache2::RequestUtil; Apache2::RequestUtil->can('request') && $Apache2::RequestUtil::VERSION > 1.99 }; ## no critic (ProhibitConstantPragma, RequireCheckingReturnValueOfEval)
  11         31  
  11         29  
  11         2369  
  0         0  
30              
31 11 50   11   86 if(MP2) {
32 0         0 carp q[Using request-based singletons [mod_perl2 found]];
33             } else {
34 11         18739 carp q[Using process-based singletons [mod_perl2 not found]];
35             }
36             }
37              
38             sub _singleton_key {
39 42     42   135 my ($self) = @_;
40             #########
41             # classic mode
42             #
43 42   66     367 my $class = ref $self || $self;
44 42         115 my $singleton_key = $class;
45              
46             #########
47             # per-request mode - should support mpm_worker & mpm_event
48             # Could this be done using $ENV{request-id} ||= uuid->new in regular CGI mode?
49             #
50 42 50       291 if(MP2) {
51 0         0 my $request = Apache2::RequestUtil->request;
52 0         0 $singleton_key = $request->pnotes($class);
53              
54 0 0       0 if(!$singleton_key) {
55 0         0 $singleton_key = Data::UUID->new->create_str;
56 0         0 $request->pnotes($class => $singleton_key);
57 0 0       0 $DEBUG_UTIL and carp qq[new util singleton = $singleton_key];
58             } else {
59 0 0       0 $DEBUG_UTIL and carp qq[reuse util singleton = $singleton_key];
60             }
61             }
62              
63 42         204 return $singleton_key;
64             }
65              
66             sub new {
67 26     26 1 88953 my ($class, $ref) = @_;
68              
69 26         147 my $self = {};
70 26         179 my $singleton_key = $class->_singleton_key;
71              
72 26 100       324 if(exists $INSTANCES->{$singleton_key}) {
73 8         32 $self = $INSTANCES->{$singleton_key};
74             }
75              
76 26 100 66     165 if($ref && ref $ref eq 'HASH') {
77 2         6 while(my ($k, $v) = each %{$ref}) {
  4         15  
78 2         7 $self->{$k} = $v;
79             }
80             }
81              
82 26 100       128 if(!exists $self->{transactions}) {
83 18         223 $self->{transactions} = $DEFAULT_TRANSACTIONS;
84             }
85              
86 26         94 $INSTANCES->{$singleton_key} = bless $self, $class;
87              
88 26         107 return $INSTANCES->{$singleton_key};
89             }
90              
91             sub cgi {
92 51     51 1 1028076 my ($self, $cgi) = @_;
93              
94 51 100       195 if($cgi) {
95 16         114 $self->{cgi} = $cgi;
96             }
97              
98 51 100       207 if(!$self->{cgi}) {
99 3         27 $self->{cgi} = CGI->new();
100             }
101              
102 51         1332 return $self->{cgi};
103             }
104              
105             sub data_path {
106 2     2 1 17 return q(data);
107             }
108              
109             sub configpath {
110 690     690 1 1549 my ($self, @args) = @_;
111              
112 690 100       1925 if(scalar @args) {
113 1         5 $self->{configpath} = shift @args;
114             }
115              
116 690   66     3548 return $self->{configpath} || $self->data_path().'/config.ini';
117             }
118              
119             sub dbsection {
120 107   100 107 1 1022 return $ENV{dev} || 'live';
121             }
122              
123             sub config {
124 688     688 1 3515 my $self = shift;
125 688   50     1772 my $configpath = $self->configpath() || q();
126 688         7177 my $dtconfigpath;
127              
128 688 100       2297 if(!$self->{config}) {
129 20         163 ($dtconfigpath) = $configpath =~ m{([[:lower:][:digit:]_/.\-]+)}smix;
130 20   50     93 $dtconfigpath ||= q();
131              
132 20 50       87 if($dtconfigpath ne $configpath) {
133 0         0 croak qq(Failed to detaint configpath: '$configpath');
134             }
135              
136 20 100       412 if(!-e $dtconfigpath) {
137 1         166 croak qq(No such file: $dtconfigpath);
138             }
139              
140 19   33     366 $self->{config} ||= Config::IniFiles->new(
141             -file => $dtconfigpath,
142             );
143             }
144              
145 687 50       153163 if(!$self->{config}) {
146 0         0 croak qq(No configuration available:\n). join q(, ), @Config::IniFiles::errors; ## no critic (Variables::ProhibitPackageVars)
147             }
148              
149 687         2650 return $self->{config};
150             }
151              
152             sub dbh {
153 169     169 1 4771 my $self = shift;
154              
155 169         661 return $self->driver->dbh();
156             }
157              
158             sub quote {
159 1     1 1 810 my ($self, $str) = @_;
160 1         6 return $self->dbh->quote($str);
161             }
162              
163             sub driver {
164 214     214 1 682 my ($self, @args) = @_;
165              
166 214 100       863 if(!$self->{driver}) {
167 15         114 my $dbsection = $self->dbsection();
168 15         116 my $config = $self->config();
169              
170 15 50 33     124 if(!$dbsection || !$config->SectionExists($dbsection)) {
171 0         0 croak q[Unable to determine config set to use. Try adding [live] [dev] or [test] sections to config.ini];
172             }
173              
174 15   33     432 my $drivername = $config->val($dbsection, 'driver') || $DEFAULT_DRIVER;
175 15         835 my $ref = {};
176              
177 15         63 for my $field (qw(dbname dbhost dbport dbuser dbpass dsn_opts)) {
178 90         665 $ref->{$field} = $self->$field()
179             }
180              
181 15         191 $self->{driver} = ClearPress::driver->new_driver($drivername, $ref);
182             }
183              
184 214         1692 return $self->{driver};
185             }
186              
187             sub log { ## no critic (homonym)
188 1     1 1 2054 my ($self, @args) = @_;
189 1 50       2 print {*STDERR} map { (strftime '[%Y-%m-%dT%H:%M:%S] ', localtime). "$_\n" } @args or croak $ERRNO;
  1         6  
  1         118  
190 1         11 return 1;
191             }
192              
193             sub cleanup {
194 16     16 1 43 my $self = shift;
195              
196             #########
197             # cleanup() is called by controller at the end of a request:response
198             # cycle. Here we neutralise the singleton instance so it doesn't
199             # carry over any stateful information to the next request - CGI,
200             # DBH, TT and anything else cached in data members.
201             #
202 16         87 my $singleton_key = $self->_singleton_key;
203              
204 16         62 delete $INSTANCES->{$singleton_key};
205              
206 16 50       79 if(exists $self->{dbh}) {
207 0         0 $self->{dbh}->disconnect();
208             }
209              
210 16         46 return 1;
211             }
212              
213             sub db_credentials {
214 90     90 1 175 my $self = shift;
215 90         259 my $cfg = $self->config();
216 90         223 my $dbsection = $self->dbsection();
217 90         199 my $ref = {};
218              
219 90         215 for my $field (qw(dbuser dbpass dbhost dbport dbname dsn_opts)) {
220 540         13039 $ref->{$field} = $cfg->val($dbsection, $field);
221             }
222              
223 90         2532 return $ref;
224             }
225              
226             sub dbname {
227 15     15 1 47 my $self = shift;
228 15         121 return $self->db_credentials->{dbname};
229             }
230              
231             sub dbuser {
232 15     15 1 47 my $self = shift;
233 15         52 return $self->db_credentials->{dbuser};
234             }
235              
236             sub dbpass {
237 15     15 1 46 my $self = shift;
238 15         51 return $self->db_credentials->{dbpass};
239             }
240              
241             sub dbhost {
242 15     15 1 42 my $self = shift;
243 15         51 return $self->db_credentials->{dbhost};
244             }
245              
246             sub dbport {
247 15     15 1 70 my $self = shift;
248 15         69 return $self->db_credentials->{dbport};
249             }
250              
251             sub dsn_opts {
252 15     15 1 41 my $self = shift;
253 15         52 return $self->db_credentials->{dsn_opts};
254             }
255              
256             END {
257             # dereferences and causes orderly destruction of all instances
258 10     10   14814 my $cap = IO::Capture::Stderr->new();
259 10         472 $cap->start;
260 10         1927 undef $INSTANCES;
261 10         133 $cap->stop;
262 10         1056 while(my $line = $cap->read()) {
263 0 0       0 if($line =~ /MySQL[ ]server[ ]has[ ]gone[ ]away/smix) { # brute force do not display these copious, noisy warnings
264 0         0 next;
265             }
266              
267 0 0       0 print {*STDERR} $line or croak qq[Error printing: $ERRNO];
  0         0  
268             }
269             }
270              
271             1;
272              
273             __END__