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   1090730 use strict;
  11         76  
  11         480  
8 11     11   77 use warnings;
  11         44  
  11         381  
9 11     11   72 use base qw(Class::Accessor);
  11         31  
  11         3941  
10 11     11   25680 use Config::IniFiles;
  11         263134  
  11         464  
11 11     11   119 use Carp;
  11         31  
  11         641  
12 11     11   5352 use POSIX qw(strftime);
  11         75609  
  11         95  
13 11     11   20417 use English qw(-no_match_vars);
  11         7673  
  11         107  
14 11     11   11500 use ClearPress::driver;
  11         54  
  11         529  
15 11     11   7291 use CGI;
  11         392326  
  11         94  
16 11     11   5724 use IO::Capture::Stderr;
  11         25849  
  11         404  
17 11     11   3375 use Data::UUID;
  11         6464  
  11         1973  
18              
19             our $VERSION = q[477.1.2];
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         29  
  11         57  
  11         2587  
  0         0  
30              
31 11 50   11   91 if(MP2) {
32 0         0 carp q[Using request-based singletons [mod_perl2 found]];
33             } else {
34 11         20209 carp q[Using process-based singletons [mod_perl2 not found]];
35             }
36             }
37              
38             sub _singleton_key {
39 42     42   164 my ($self) = @_;
40             #########
41             # classic mode
42             #
43 42   66     369 my $class = ref $self || $self;
44 42         120 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       351 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         183 return $singleton_key;
64             }
65              
66             sub new {
67 26     26 1 179530 my ($class, $ref) = @_;
68              
69 26         1070 my $self = {};
70 26         544 my $singleton_key = $class->_singleton_key;
71              
72 26 100       192 if(exists $INSTANCES->{$singleton_key}) {
73 8         35 $self = $INSTANCES->{$singleton_key};
74             }
75              
76 26 100 66     167 if($ref && ref $ref eq 'HASH') {
77 2         6 while(my ($k, $v) = each %{$ref}) {
  4         19  
78 2         8 $self->{$k} = $v;
79             }
80             }
81              
82 26 100       199 if(!exists $self->{transactions}) {
83 18         125 $self->{transactions} = $DEFAULT_TRANSACTIONS;
84             }
85              
86 26         103 $INSTANCES->{$singleton_key} = bless $self, $class;
87              
88 26         120 return $INSTANCES->{$singleton_key};
89             }
90              
91             sub cgi {
92 51     51 1 1029508 my ($self, $cgi) = @_;
93              
94 51 100       217 if($cgi) {
95 16         145 $self->{cgi} = $cgi;
96             }
97              
98 51 100       230 if(!$self->{cgi}) {
99 3         28 $self->{cgi} = CGI->new();
100             }
101              
102 51         1425 return $self->{cgi};
103             }
104              
105             sub data_path {
106 2     2 1 16 return q(data);
107             }
108              
109             sub configpath {
110 654     654 1 1617 my ($self, @args) = @_;
111              
112 654 100       2171 if(scalar @args) {
113 1         5 $self->{configpath} = shift @args;
114             }
115              
116 654   66     3787 return $self->{configpath} || $self->data_path().'/config.ini';
117             }
118              
119             sub dbsection {
120 107   100 107 1 1042 return $ENV{dev} || 'live';
121             }
122              
123             sub config {
124 652     652 1 3651 my $self = shift;
125 652   50     2353 my $configpath = $self->configpath() || q();
126 652         10219 my $dtconfigpath;
127              
128 652 100       2051 if(!$self->{config}) {
129 20         183 ($dtconfigpath) = $configpath =~ m{([[:lower:][:digit:]_/.\-]+)}smix;
130 20   50     107 $dtconfigpath ||= q();
131              
132 20 50       98 if($dtconfigpath ne $configpath) {
133 0         0 croak qq(Failed to detaint configpath: '$configpath');
134             }
135              
136 20 100       397 if(!-e $dtconfigpath) {
137 1         103 croak qq(No such file: $dtconfigpath);
138             }
139              
140 19   33     401 $self->{config} ||= Config::IniFiles->new(
141             -file => $dtconfigpath,
142             );
143             }
144              
145 651 50       225802 if(!$self->{config}) {
146 0         0 croak qq(No configuration available:\n). join q(, ), @Config::IniFiles::errors; ## no critic (Variables::ProhibitPackageVars)
147             }
148              
149 651         2934 return $self->{config};
150             }
151              
152             sub dbh {
153 157     157 1 5644 my $self = shift;
154              
155 157         749 return $self->driver->dbh();
156             }
157              
158             sub quote {
159 1     1 1 1203 my ($self, $str) = @_;
160 1         9 return $self->dbh->quote($str);
161             }
162              
163             sub driver {
164 202     202 1 1317 my ($self, @args) = @_;
165              
166 202 100       1244 if(!$self->{driver}) {
167 15         128 my $dbsection = $self->dbsection();
168 15         84 my $config = $self->config();
169              
170 15 50 33     135 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     600 my $drivername = $config->val($dbsection, 'driver') || $DEFAULT_DRIVER;
175 15         649 my $ref = {};
176              
177 15         67 for my $field (qw(dbname dbhost dbport dbuser dbpass dsn_opts)) {
178 90         679 $ref->{$field} = $self->$field()
179             }
180              
181 15         216 $self->{driver} = ClearPress::driver->new_driver($drivername, $ref);
182             }
183              
184 202         1194 return $self->{driver};
185             }
186              
187             sub log { ## no critic (homonym)
188 1     1 1 12908 my ($self, @args) = @_;
189 1 50       5 print {*STDERR} map { (strftime '[%Y-%m-%dT%H:%M:%S] ', localtime). "$_\n" } @args or croak $ERRNO;
  1         9  
  1         737  
190 1         15 return 1;
191             }
192              
193             sub cleanup {
194 16     16 1 53 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         91 my $singleton_key = $self->_singleton_key;
203              
204 16         67 delete $INSTANCES->{$singleton_key};
205              
206 16 50       81 if(exists $self->{dbh}) {
207 0         0 $self->{dbh}->disconnect();
208             }
209              
210 16         51 return 1;
211             }
212              
213             sub db_credentials {
214 90     90 1 201 my $self = shift;
215 90         259 my $cfg = $self->config();
216 90         342 my $dbsection = $self->dbsection();
217 90         277 my $ref = {};
218              
219 90         233 for my $field (qw(dbuser dbpass dbhost dbport dbname dsn_opts)) {
220 540         15390 $ref->{$field} = $cfg->val($dbsection, $field);
221             }
222              
223 90         3537 return $ref;
224             }
225              
226             sub dbname {
227 15     15 1 52 my $self = shift;
228 15         147 return $self->db_credentials->{dbname};
229             }
230              
231             sub dbuser {
232 15     15 1 50 my $self = shift;
233 15         56 return $self->db_credentials->{dbuser};
234             }
235              
236             sub dbpass {
237 15     15 1 50 my $self = shift;
238 15         58 return $self->db_credentials->{dbpass};
239             }
240              
241             sub dbhost {
242 15     15 1 48 my $self = shift;
243 15         62 return $self->db_credentials->{dbhost};
244             }
245              
246             sub dbport {
247 15     15 1 68 my $self = shift;
248 15         73 return $self->db_credentials->{dbport};
249             }
250              
251             sub dsn_opts {
252 15     15 1 56 my $self = shift;
253 15         83 return $self->db_credentials->{dsn_opts};
254             }
255              
256             END {
257             # dereferences and causes orderly destruction of all instances
258 10     10   15555 my $cap = IO::Capture::Stderr->new();
259 10         627 $cap->start;
260 10         2598 undef $INSTANCES;
261 10         128 $cap->stop;
262 10         888 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__