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   887631 use strict;
  11         29  
  11         325  
8 11     11   100 use warnings;
  11         29  
  11         336  
9 11     11   66 use base qw(Class::Accessor);
  11         44  
  11         3540  
10 11     11   21381 use Config::IniFiles;
  11         213928  
  11         410  
11 11     11   107 use Carp;
  11         24  
  11         577  
12 11     11   4253 use POSIX qw(strftime);
  11         61719  
  11         79  
13 11     11   17347 use English qw(-no_match_vars);
  11         5502  
  11         78  
14 11     11   7763 use ClearPress::driver;
  11         41  
  11         450  
15 11     11   6122 use CGI;
  11         246643  
  11         72  
16 11     11   4446 use IO::Capture::Stderr;
  11         19610  
  11         350  
17 11     11   2919 use Data::UUID;
  11         5556  
  11         1802  
18              
19             our $VERSION = q[476.4.2];
20             our $DEBUG_UTIL = 0;
21             our $DEFAULT_TRANSACTIONS = 1;
22             our $DEFAULT_DRIVER = 'mysql';
23             my $INSTANCES = {}; # per-process table of singletons (nasty!)
24              
25             __PACKAGE__->mk_accessors(qw(transactions username requestor profiler session));
26              
27             BEGIN {
28 11 0   11   90 use constant MP2 => eval { require Apache2::RequestUtil; Apache2::RequestUtil->can('request') && $Apache2::RequestUtil::VERSION > 1.99 }; ## no critic (ProhibitConstantPragma, RequireCheckingReturnValueOfEval)
  11         31  
  11         123  
  11         1851  
  0         0  
29              
30 11 50   11   72 if(MP2) {
31 0         0 carp q[Using request-based singletons [mod_perl2 found]];
32             } else {
33 11         16099 carp q[Using process-based singletons [mod_perl2 not found]];
34             }
35             }
36              
37             sub _singleton_key {
38 42     42   137 my ($self) = @_;
39             #########
40             # classic mode
41             #
42 42   66     314 my $class = ref $self || $self;
43 42         110 my $singleton_key = $class;
44              
45             #########
46             # per-request mode - should support mpm_worker & mpm_event
47             # Could this be done using $ENV{request-id} ||= uuid->new in regular CGI mode?
48             #
49 42 50       252 if(MP2) {
50 0         0 my $request = Apache2::RequestUtil->request;
51 0         0 $singleton_key = $request->pnotes($class);
52              
53 0 0       0 if(!$singleton_key) {
54 0         0 $singleton_key = Data::UUID->new->create_str;
55 0         0 $request->pnotes($class => $singleton_key);
56 0 0       0 $DEBUG_UTIL and carp qq[new util singleton = $singleton_key];
57             } else {
58 0 0       0 $DEBUG_UTIL and carp qq[reuse util singleton = $singleton_key];
59             }
60             }
61              
62 42         130 return $singleton_key;
63             }
64              
65             sub new {
66 26     26 1 75920 my ($class, $ref) = @_;
67              
68 26         84 my $self = {};
69 26         203 my $singleton_key = $class->_singleton_key;
70              
71 26 100       127 if(exists $INSTANCES->{$singleton_key}) {
72 8         30 $self = $INSTANCES->{$singleton_key};
73             }
74              
75 26 100 66     195 if($ref && ref $ref eq 'HASH') {
76 2         4 while(my ($k, $v) = each %{$ref}) {
  4         14  
77 2         5 $self->{$k} = $v;
78             }
79             }
80              
81 26 100       106 if(!exists $self->{transactions}) {
82 18         62 $self->{transactions} = $DEFAULT_TRANSACTIONS;
83             }
84              
85 26         108 $INSTANCES->{$singleton_key} = bless $self, $class;
86              
87 26         100 return $INSTANCES->{$singleton_key};
88             }
89              
90             sub cgi {
91 51     51 1 1024954 my ($self, $cgi) = @_;
92              
93 51 100       187 if($cgi) {
94 16         104 $self->{cgi} = $cgi;
95             }
96              
97 51 100       193 if(!$self->{cgi}) {
98 3         30 $self->{cgi} = CGI->new();
99             }
100              
101 51         1333 return $self->{cgi};
102             }
103              
104             sub data_path {
105 2     2 1 15 return q(data);
106             }
107              
108             sub configpath {
109 605     605 1 1335 my ($self, @args) = @_;
110              
111 605 100       1507 if(scalar @args) {
112 1         2 $self->{configpath} = shift @args;
113             }
114              
115 605   66     2800 return $self->{configpath} || $self->data_path().'/config.ini';
116             }
117              
118             sub dbsection {
119 107   100 107 1 886 return $ENV{dev} || 'live';
120             }
121              
122             sub config {
123 603     603 1 3035 my $self = shift;
124 603   50     1435 my $configpath = $self->configpath() || q();
125 603         5900 my $dtconfigpath;
126              
127 603 100       1690 if(!$self->{config}) {
128 20         140 ($dtconfigpath) = $configpath =~ m{([[:lower:][:digit:]_/.\-]+)}smix;
129 20   50     123 $dtconfigpath ||= q();
130              
131 20 50       85 if($dtconfigpath ne $configpath) {
132 0         0 croak qq(Failed to detaint configpath: '$configpath');
133             }
134              
135 20 100       356 if(!-e $dtconfigpath) {
136 1         134 croak qq(No such file: $dtconfigpath);
137             }
138              
139 19   33     307 $self->{config} ||= Config::IniFiles->new(
140             -file => $dtconfigpath,
141             );
142             }
143              
144 602 50       142243 if(!$self->{config}) {
145 0         0 croak qq(No configuration available:\n). join q(, ), @Config::IniFiles::errors; ## no critic (Variables::ProhibitPackageVars)
146             }
147              
148 602         2172 return $self->{config};
149             }
150              
151             sub dbh {
152 141     141 1 3564 my $self = shift;
153              
154 141         580 return $self->driver->dbh();
155             }
156              
157             sub quote {
158 1     1 1 544 my ($self, $str) = @_;
159 1         3 return $self->dbh->quote($str);
160             }
161              
162             sub driver {
163 186     186 1 556 my ($self, @args) = @_;
164              
165 186 100       720 if(!$self->{driver}) {
166 15         98 my $dbsection = $self->dbsection();
167 15         53 my $config = $self->config();
168              
169 15 50 33     133 if(!$dbsection || !$config->SectionExists($dbsection)) {
170 0         0 croak q[Unable to determine config set to use. Try adding [live] [dev] or [test] sections to config.ini];
171             }
172              
173 15   33     352 my $drivername = $config->val($dbsection, 'driver') || $DEFAULT_DRIVER;
174 15         425 my $ref = {};
175              
176 15         49 for my $field (qw(dbname dbhost dbport dbuser dbpass dsn_opts)) {
177 90         449 $ref->{$field} = $self->$field()
178             }
179              
180 15         175 $self->{driver} = ClearPress::driver->new_driver($drivername, $ref);
181             }
182              
183 186         918 return $self->{driver};
184             }
185              
186             sub log { ## no critic (homonym)
187 1     1 1 1297 my ($self, @args) = @_;
188 1 50       1 print {*STDERR} map { (strftime '[%Y-%m-%dT%H:%M:%S] ', localtime). "$_\n" } @args or croak $ERRNO;
  1         5  
  1         84  
189 1         8 return 1;
190             }
191              
192             sub cleanup {
193 16     16 1 37 my $self = shift;
194              
195             #########
196             # cleanup() is called by controller at the end of a request:response
197             # cycle. Here we neutralise the singleton instance so it doesn't
198             # carry over any stateful information to the next request - CGI,
199             # DBH, TT and anything else cached in data members.
200             #
201 16         74 my $singleton_key = $self->_singleton_key;
202              
203 16         57 delete $INSTANCES->{$singleton_key};
204              
205 16 50       59 if(exists $self->{dbh}) {
206 0         0 $self->{dbh}->disconnect();
207             }
208              
209 16         37 return 1;
210             }
211              
212             sub db_credentials {
213 90     90 1 134 my $self = shift;
214 90         212 my $cfg = $self->config();
215 90         184 my $dbsection = $self->dbsection();
216 90         160 my $ref = {};
217              
218 90         206 for my $field (qw(dbuser dbpass dbhost dbport dbname dsn_opts)) {
219 540         9659 $ref->{$field} = $cfg->val($dbsection, $field);
220             }
221              
222 90         2013 return $ref;
223             }
224              
225             sub dbname {
226 15     15 1 39 my $self = shift;
227 15         99 return $self->db_credentials->{dbname};
228             }
229              
230             sub dbuser {
231 15     15 1 38 my $self = shift;
232 15         40 return $self->db_credentials->{dbuser};
233             }
234              
235             sub dbpass {
236 15     15 1 44 my $self = shift;
237 15         43 return $self->db_credentials->{dbpass};
238             }
239              
240             sub dbhost {
241 15     15 1 58 my $self = shift;
242 15         51 return $self->db_credentials->{dbhost};
243             }
244              
245             sub dbport {
246 15     15 1 37 my $self = shift;
247 15         47 return $self->db_credentials->{dbport};
248             }
249              
250             sub dsn_opts {
251 15     15 1 747 my $self = shift;
252 15         49 return $self->db_credentials->{dsn_opts};
253             }
254              
255             END {
256             # dereferences and causes orderly destruction of all instances
257 10     10   12394 my $cap = IO::Capture::Stderr->new();
258 10         465 $cap->start;
259 10         1358 undef $INSTANCES;
260 10         85 $cap->stop;
261 10         501 while(my $line = $cap->read()) {
262 0 0       0 if($line =~ /MySQL[ ]server[ ]has[ ]gone[ ]away/smix) { # brute force do not display these copious, noisy warnings
263 0         0 next;
264             }
265              
266 0 0       0 print {*STDERR} $line or croak qq[Error printing: $ERRNO];
  0         0  
267             }
268             }
269              
270             1;
271              
272             __END__