File Coverage

blib/lib/Elive/Util.pm
Criterion Covered Total %
statement 64 119 53.7
branch 17 70 24.2
condition 2 19 10.5
subroutine 18 23 78.2
pod 4 4 100.0
total 105 235 44.6


line stmt bran cond sub pod time code
1             package Elive::Util;
2 36     36   30455 use warnings; use strict;
  36     36   71  
  36         1119  
  36         198  
  36         80  
  36         1623  
3              
4 36     36   38891 use Term::ReadKey;
  36         199851  
  36         3773  
5 36     36   51249 use Term::ReadLine;
  36         245577  
  36         1296  
6 36     36   37277 use IO::Interactive;
  36         518714  
  36         264  
7 36     36   1710 use Scalar::Util;
  36         84  
  36         1696  
8 36     36   33689 use Clone;
  36         37697  
  36         2004  
9 36     36   2308 use YAML::Syck;
  36         5049  
  36         2517  
10 36     36   2334 use Try::Tiny;
  36         3266  
  36         2927  
11              
12             our $VERSION = '0.03';
13              
14 36     36   24902 use Elive::Util::Type;
  36         125  
  36         68230  
15              
16             =head1 NAME
17              
18             Elive::Util - Utility functions for Elive
19              
20             =cut
21              
22             =head1 METHODS
23              
24             =cut
25              
26             =head2 inspect_type
27              
28             $type = Elive::Util::inspect_type('Elive::Entity::Participants');
29             if ($type->is_array) {
30             # ...
31             }
32              
33             Returns an object of type L.
34              
35             =cut
36              
37             sub inspect_type {
38 58     58 1 109 my $type_union = shift;
39              
40 58         206 my @types = split(/\|/, $type_union);
41              
42 58         476 return Elive::Util::Type->new($types[0])
43             }
44              
45             sub _freeze {
46 254     254   448 my ($val, $type) = @_;
47              
48 254         463 for ($val) {
49              
50 254 50       514 if (!defined) {
51 0         0 warn "undefined value of type $type\n"
52             }
53             else {
54 254         482 $_ = string($_, $type);
55 254         389 my $raw_val = $_;
56              
57 254 50       1065 if ($type =~ m{^Bool}ix) {
    100          
    50          
    0          
58              
59             #
60             # DBize boolean flags..
61             #
62 0 0       0 $_ = $_ ? 'true' : 'false';
63             }
64             elsif ($type =~ m{^(Str|enum)}ix) {
65              
66             #
67             # low level check for taintness. Only applicible when
68             # perl program is running in taint mode
69             #
70 126 50       1304 die "attempt to freeze tainted data (type $type): $_"
71             if _tainted($_);
72             #
73             # l-r trim
74             #
75 126 50       842 $_ = $1
76             if m{^ \s* (.*?) \s* $}x;
77 126 100       377 $_ = lc if $type =~ m{^enum};
78             }
79             elsif ($type =~ m{^(Int|HiResDate)}ix) {
80 128         3135 $_ = _tidy_decimal("$_");
81             }
82             elsif ($type =~ m{^Ref|Any}ix) {
83 0         0 $_ = undef;
84             }
85             else {
86 0 0       0 die "unable to convert $raw_val to $type\n"
87             unless defined;
88             }
89             }
90             };
91              
92 254         1958 return $val;
93             }
94              
95             #
96             # thawing of elementry datatypes
97             #
98              
99             sub _thaw {
100 0     0   0 my ($val, $type) = @_;
101              
102 0 0 0     0 return $val if $type =~ m{Ref}i
103             || ref( $val);
104              
105 0 0       0 return unless defined $val;
106              
107 0         0 for ($val) {
108              
109 0 0       0 if ($type =~ m{^Bool}i) {
    0          
    0          
    0          
110             #
111             # Perlise boolean flags..
112             #
113 0 0       0 $_ = m{^(true|1)$}i ? 1 : 0;
114             }
115             elsif ($type =~ m{^(Str|enum)}i) {
116             #
117             # l-r trim
118             #
119 0 0       0 $_ = $1
120             if m{^ \s* (.*?) \s* $}x;
121 0 0       0 $_ = lc if $type =~ m{^enum}i;
122             }
123             elsif ($type =~ m{^Int|HiResDate}i) {
124              
125 0         0 $_ = _tidy_decimal("$_");
126              
127             }
128             elsif ($type eq 'Any') {
129             # more or less a placeholder type
130 0         0 $_ = string($_);
131             }
132             else {
133 0         0 die "unknown type: $type";
134             }
135             };
136              
137 0         0 return $val;
138             }
139              
140             #
141             # _tidy_decimal(): general cleanup and normalisation of an integer.
142             # used to clean up numbers for data storage or comparison
143              
144             sub _tidy_decimal {
145 128     128   382 my ($i) = @_;
146             #
147             # well a number really. don't convert or sprintf etc
148             # to avoid overflow. Just normalise it for potential
149             # string comparisons
150             #
151             # l-r trim, also untaint
152             #
153 128 50       757 if ($i =~ m{^ [\s\+]* (-?\d+) \s* $}x) {
154 128         330 $i = $1;
155             }
156             else {
157 0         0 return;
158             }
159              
160             #
161             # remove any leading zeros:
162             # 000123 => 123
163             # -00045 => -45
164             # -000 => 0
165             #
166              
167 128         1077 $i =~ s{^
168             (-?) # leading minus retained (for now)
169             0* # leading zeros discarded
170             (\d+?) # number - retained
171             $}
172             {$1$2}x;
173              
174             #
175             # reduce -0 => 0
176 128 50       515 $i = 0 if ($i eq '-0');
177              
178             #
179             # sanity check.
180             #
181 128 50       650 die "bad integer: $_[0]"
182             unless $i =~ m{^[+-]?\d+$};
183              
184 128         632 return $i;
185             }
186              
187             =head2 prompt
188              
189             my $password = Elive::Util::prompt('Password: ', password => 1)
190              
191             Prompt for user input
192              
193             =cut
194              
195             sub prompt {
196 0     0 1 0 my ($prompt,%opt) = @_;
197              
198 0   0     0 chomp($prompt ||= 'input:');
199              
200 0 0       0 ReadMode $opt{password}? 2: 1; # Turn off controls keys
201              
202 0         0 my $input;
203 0         0 my $n = 0;
204              
205 0   0     0 do {
206 0 0       0 die "giving up on input of $prompt" if ++$n > 100;
207 0 0       0 print $prompt if IO::Interactive::is_interactive();
208 0         0 $input = ReadLine(0);
209             return
210 0 0       0 unless (defined $input);
211 0         0 chomp($input);
212             } until (defined($input) && length($input));
213              
214 0         0 ReadMode 0; # Reset tty mode before exiting
215              
216 0         0 return $input;
217             }
218              
219             sub _reftype {
220 1013   100 1013   5646 return Scalar::Util::reftype( shift() ) || '';
221             }
222              
223             sub _clone {
224 5     5   6436 return Clone::clone(shift);
225             }
226              
227             sub _tainted {
228 126     126   220 return grep { Scalar::Util::tainted($_) } @_;
  126         543  
229             }
230              
231             #
232             # Hex encoding/decoding. Use for data streaming. E.g. upload & download
233             # of preload data.
234             #
235              
236             sub _hex_decode {
237 0     0   0 my $data = shift;
238              
239             return
240 0 0       0 unless defined $data;
241              
242 0 0       0 $data = '0'.$data
243             unless length($data) % 2 == 0;
244              
245 0         0 my ($non_hex_char) = ($data =~ m{([^0-9a-f])}ix);
246              
247 0 0       0 die "non hex character in data: ".$non_hex_char
248             if (defined $non_hex_char);
249             #
250             # Works for simple ascii
251 0         0 $data =~ s{(..)}{chr(hex($1))}gex;
  0         0  
252              
253 0         0 return $data;
254             }
255              
256             sub _hex_encode {
257 0     0   0 my $data = shift;
258              
259 0         0 $data =~ s{(.)}{sprintf("%02x", ord($1))}gesx;
  0         0  
260              
261 0         0 return $data;
262             }
263              
264             =head2 string
265              
266             print Elive::Util::string($myscalar);
267             print Elive::Util::string($myobj);
268             print Elive::Util::string($myref, $datatype);
269              
270             Return a string for an object. This method is widely used for casting
271             objects to ids.
272              
273             =over 4
274              
275             =item
276              
277             If it's a simple scalar, just pass the value back.
278              
279             =item
280              
281             If it's an object use the C method.
282              
283             =item
284              
285             If it's a reference, resolve datatype to a class, and use its
286             C method.
287              
288             =back
289              
290             =cut
291              
292             sub string {
293 418     418 1 728 my $obj = shift;
294 418         607 my $data_type = shift;
295              
296 418         734 for ($obj) {
297              
298 418 100       1430 if ($data_type) {
299 360         1156 my ($dt) = ($data_type =~ m{(.*)});
300              
301 360     360   15636 return $dt->stringify($_)
302 360 100       3263 if try {$dt->can('stringify')};
303             }
304              
305 312         5131 my $reftype = _reftype($_);
306              
307 312 50       1593 return $_
308             unless $reftype;
309              
310 0 0 0       return $_->stringify
311             if (Scalar::Util::blessed($_) && $_->can('stringify'));
312              
313 0 0         if ($reftype eq 'ARRAY') {
314 0           return join(',', map {string($_ => $data_type)} @$_)
  0            
315             }
316             }
317              
318             #
319             # Nothing else worked; dump it.
320             #
321 0           return YAML::Syck::Dump($obj);
322             }
323              
324             =head2 next_quarter_hour
325              
326             Quarter hour advancement for the Time Module impoverished.
327              
328             my $start = Elive::Util::next_quarter_hour();
329             my $end = Elive::Util::next_quarter_hour($start);
330              
331             Advance to the next quarter hour without the use of any supporting
332             time modules. We just simply increment in seconds until C
333             indicates that we're exactly on a quarter hour and ahead of the start time.
334              
335             A small initial increment is added to ensure that the date remains
336             in the future, allowing for minor gotchas such as leap seconds, general
337             latency and smallish time drifts between the client and server.
338              
339             =cut
340              
341             sub next_quarter_hour {
342 0   0 0 1   my $time = shift || time();
343              
344 0           $time += 30;
345              
346 0           for (;;) {
347 0           my @t = localtime(++$time);
348 0           my $sec = $t[0];
349 0           my $min = $t[1];
350              
351 0 0 0       last unless $min % 15 || $sec;
352             }
353              
354 0           return $time;
355             }
356              
357             1;