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   20750 use warnings; use strict;
  36     36   46  
  36         1086  
  36         137  
  36         52  
  36         896  
3              
4 36     36   20398 use Term::ReadKey;
  36         124210  
  36         2749  
5 36     36   19553 use Term::ReadLine;
  36         134271  
  36         1165  
6 36     36   17025 use IO::Interactive;
  36         306790  
  36         189  
7 36     36   1468 use Scalar::Util;
  36         62  
  36         1226  
8 36     36   17384 use Clone;
  36         18502  
  36         1496  
9 36     36   1162 use YAML::Syck;
  36         3453  
  36         1952  
10 36     36   1250 use Try::Tiny;
  36         2632  
  36         2054  
11              
12             our $VERSION = '0.03';
13              
14 36     36   13728 use Elive::Util::Type;
  36         84  
  36         48641  
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 100 my $type_union = shift;
39              
40 58         198 my @types = split(/\|/, $type_union);
41              
42 58         406 return Elive::Util::Type->new($types[0])
43             }
44              
45             sub _freeze {
46 254     254   350 my ($val, $type) = @_;
47              
48 254         396 for ($val) {
49              
50 254 50       412 if (!defined) {
51 0         0 warn "undefined value of type $type\n"
52             }
53             else {
54 254         424 $_ = string($_, $type);
55 254         254 my $raw_val = $_;
56              
57 254 50       740 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       984 die "attempt to freeze tainted data (type $type): $_"
71             if _tainted($_);
72             #
73             # l-r trim
74             #
75 126 50       680 $_ = $1
76             if m{^ \s* (.*?) \s* $}x;
77 126 100       301 $_ = lc if $type =~ m{^enum};
78             }
79             elsif ($type =~ m{^(Int|HiResDate)}ix) {
80 128         1533 $_ = _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         1290 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   172 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       525 if ($i =~ m{^ [\s\+]* (-?\d+) \s* $}x) {
154 128         267 $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         719 $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       320 $i = 0 if ($i eq '-0');
177              
178             #
179             # sanity check.
180             #
181 128 50       388 die "bad integer: $_[0]"
182             unless $i =~ m{^[+-]?\d+$};
183              
184 128         467 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   4235 return Scalar::Util::reftype( shift() ) || '';
221             }
222              
223             sub _clone {
224 5     5   4528 return Clone::clone(shift);
225             }
226              
227             sub _tainted {
228 126     126   152 return grep { Scalar::Util::tainted($_) } @_;
  126         403  
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 529 my $obj = shift;
294 418         404 my $data_type = shift;
295              
296 418         464 for ($obj) {
297              
298 418 100       1139 if ($data_type) {
299 360         978 my ($dt) = ($data_type =~ m{(.*)});
300              
301 360     360   10821 return $dt->stringify($_)
302 360 100       2914 if try {$dt->can('stringify')};
303             }
304              
305 312         3006 my $reftype = _reftype($_);
306              
307 312 50       976 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;