File Coverage

blib/lib/Lazy/Utils.pm
Criterion Covered Total %
statement 31 222 13.9
branch 0 106 0.0
condition 0 42 0.0
subroutine 10 30 33.3
pod 13 19 68.4
total 54 419 12.8


line stmt bran cond sub pod time code
1             package Lazy::Utils;
2             =head1 NAME
3              
4             Lazy::Utils - Utility functions
5              
6             =head1 VERSION
7              
8             version 1.22
9              
10             =head1 SYNOPSIS
11              
12             use Lazy::Utils;
13            
14             trim($str);
15             ltrim($str);
16             rtrim($str);
17             file_get_contents($path, $prefs);
18             file_put_contents($path, $contents, $prefs);
19             shellmeta($s, $nonquoted);
20             system2($cmd, @argv);
21             bash_readline($prompt);
22             cmdargs($prefs, @argv);
23             whereis($name, $path);
24             file_cache($tag, $expiry, $coderef);
25             get_pod_text($file_name, $section, $exclude_section);
26             array_to_hash(@array);
27              
28             =head1 DESCRIPTION
29              
30             Collection of utility functions all of exported by default.
31              
32             =cut
33 1     1   93848 use strict;
  1         4  
  1         31  
34 1     1   7 use warnings;
  1         2  
  1         34  
35 1     1   16 use v5.10.1;
  1         8  
36 1     1   7 use feature qw(switch);
  1         2  
  1         132  
37 1     1   993 no if ($] >= 5.018), 'warnings' => 'experimental';
  1         15  
  1         7  
38             require bytes;
39             require utf8;
40 1     1   848 use FindBin;
  1         1121  
  1         47  
41 1     1   767 use JSON;
  1         13518  
  1         8  
42 1     1   843 use Pod::Simple::Text;
  1         45350  
  1         145  
43              
44              
45             BEGIN
46             {
47 1     1   12 require Exporter;
48 1         3 our $VERSION = '1.22';
49 1         14 our @ISA = qw(Exporter);
50 1         7 our @EXPORT = qw(trim ltrim rtrim file_get_contents file_put_contents shellmeta system2 _system
51             bash_readline bashReadLine cmdargs commandArgs cmdArgs whereis whereisBin file_cache fileCache
52             get_pod_text getPodText array_to_hash);
53 1         775 our @EXPORT_OK = qw();
54             }
55              
56              
57             =head1 FUNCTIONS
58              
59             =head2 trim($str)
60              
61             trims given string
62              
63             $str: I
64              
65             return value: I
66              
67             =cut
68             sub trim
69             {
70 0     0 1   my ($s) = @_;
71 0           $s =~ s/^\s+|\s+$//g;
72 0           return $s
73             }
74              
75             =head2 ltrim($str)
76              
77             trims left given string
78              
79             $str: I
80              
81             return value: I
82              
83             =cut
84             sub ltrim
85             {
86 0     0 1   my ($s) = @_;
87 0           $s =~ s/^\s+//;
88 0           return $s
89             }
90              
91             =head2 rtrim($str)
92              
93             trims right given string
94              
95             $str: I
96              
97             return value: I
98              
99             =cut
100             sub rtrim
101             {
102 0     0 1   my ($s) = @_;
103 0           $s =~ s/\s+$//;
104 0           return $s
105             }
106              
107             =head2 file_get_contents($path, $prefs)
108              
109             gets all contents of file in string type
110              
111             $path: I
112              
113             $prefs: I
114              
115             =over
116              
117             utf8: I
118              
119             =back
120              
121             return value: I
122              
123             =cut
124             sub file_get_contents
125             {
126 0     0 1   my ($path, $prefs) = @_;
127 0 0         $prefs = {} unless ref($prefs) eq 'HASH';
128             my $result = do
129 0           {
130 0           local $/ = undef;
131 0           my $mode = "";
132 0 0         $mode .= " :utf8" if $prefs->{utf8};
133 0 0         open my $fh, "<$mode", $path or return;
134 0           my $result = <$fh>;
135 0           close $fh;
136 0           $result;
137             };
138 0           return $result;
139             }
140              
141             =head2 file_put_contents($path, $contents, $prefs)
142              
143             puts all contents of file in string type
144              
145             $path: I
146              
147             $contents: I
148              
149             $prefs: I
150              
151             =over
152              
153             utf8: I
154              
155             =back
156              
157             return value: I
158              
159             =cut
160             sub file_put_contents
161             {
162 0     0 1   my ($path, $contents, $prefs) = @_;
163 0 0 0       return if not defined($contents) or ref($contents);
164 0 0         $prefs = {} unless ref($prefs) eq 'HASH';
165             my $result = do
166 0           {
167 0           local $\ = undef;
168 0           my $mode = "";
169 0 0         $mode .= " :utf8" if $prefs->{utf8};
170 0 0         open my $fh, ">$mode", $path or return;
171 0           my $result = print $fh $contents;
172 0           close $fh;
173 0           $result;
174             };
175 0           return $result;
176             }
177              
178             =head2 shellmeta($s, $nonquoted)
179              
180             escapes metacharacters of interpolated shell string
181              
182             $s: I
183              
184             $nonquoted: I
185              
186             return value: I
187              
188             =cut
189             sub shellmeta
190             {
191 0     0 1   my ($s, $nonquoted) = @_;
192 0 0         return unless defined $s;
193 0           $s =~ s/(\\|\"|\$)/\\$1/g;
194 0 0         $s =~ s/(\s|\*)/\\$1/g if $nonquoted;
195 0           return $s;
196             }
197              
198             =head2 system2($cmd, @argv)
199              
200             B<_system($cmd, @argv)> I
201              
202             alternative implementation of perls core system subroutine that executes a system command
203              
204             $cmd: I
205              
206             @argv: I
207              
208             return value: I
209              
210             returned $!: I
211              
212             returned $?: I
213              
214             =cut
215             sub system2
216             {
217 0     0 1   my $pid;
218 0 0         return -1 unless defined($pid = fork);
219 0 0         unless ($pid)
220             {
221 1     1   11 no warnings FATAL => 'exec';
  1         4  
  1         2730  
222 0           exec(@_);
223 0           exit 255;
224             }
225 0 0         return -1 unless waitpid($pid, 0) > 0;
226 0           return $? >> 8;
227             }
228             sub _system
229             {
230 0     0     return system2(@_);
231             }
232              
233             =head2 bash_readline($prompt)
234              
235             B I
236              
237             reads a line from STDIN using Bash
238              
239             $prompt: I
240              
241             return value: I
242              
243             =cut
244             sub bash_readline
245             {
246 0     0 1   my ($prompt) = @_;
247 0 0         $prompt = "" unless defined($prompt);
248 0           my $in = \*STDIN;
249 0 0         unless (-t $in)
250             {
251 0           my $line = <$in>;
252 0 0         chomp $line if defined $line;
253 0           return $line;
254             }
255 0           local $/ = "\n";
256 0           my $cmd = '/usr/bin/env bash -c "read -p \"'.shellmeta(shellmeta($prompt)).'\" -r -e && echo -n \"\$REPLY\" 2>/dev/null"';
257 0           $_ = `$cmd`;
258 0 0         return (not $?)? $_: undef;
259             }
260             sub bashReadLine
261             {
262 0     0 0   return bash_readline(@_);
263             }
264              
265             =head2 cmdargs([$prefs, ]@argv)
266              
267             B I
268              
269             B I
270              
271             resolves command line arguments
272              
273             $prefs: I
274              
275             =over
276              
277             valuableArgs: I
278              
279             noCommand: I
280              
281             optionAtAll: I
282              
283             =back
284              
285             @argv: I
286              
287             -a -b=c -d e --f g --h --i=j k l -- m n
288              
289             by default, return value:
290              
291             { -a => '', -b => 'c', -d => '', --f => '', --h => '', --i => 'j', command => 'e', parameters => ['g', 'k', 'l'], late_parameters => ['m', 'n'] }
292              
293             if valuableArgs is on, return value;
294              
295             { -a => '', -b => 'c', -d => 'e', --f => 'g', --h => '', --i => 'j', command => 'k', parameters => ['l'], late_parameters => ['m', 'n'] }
296              
297             if noCommand is on, return value:
298              
299             { -a => '', -b => 'c', -d => '', --f => '', --h => '', --i => 'j', command => undef, parameters => ['e', 'g', 'k', 'l'], late_parameters => ['m', 'n'] }
300              
301             if optionAtAll is off, return value:
302              
303             { -a => '', -b => 'c', -d => '', command => 'e', parameters => ['--f', 'g', '--h', '--i=j', 'k', 'l', '--','m', 'n'], late_parameters => [] }
304              
305             =cut
306             sub cmdargs
307             {
308 0     0 1   my $prefs = {};
309 0 0 0       $prefs = shift if @_ >= 1 and ref($_[0]) eq 'HASH';
310 0           my @argv = @_;
311 0           my %result;
312 0           $result{command} = undef;
313 0           $result{parameters} = undef;
314              
315 0           my @parameters;
316             my @late_parameters;
317 0           my $late;
318 0           my $opt;
319 0           while (@argv)
320             {
321 0           my $argv = shift @argv;
322 0 0 0       next unless defined($argv) and not ref($argv);
323              
324 0 0 0       if (not (not defined($prefs->{optionAtAll}) or $prefs->{optionAtAll}) and @parameters)
      0        
325             {
326 0           push @parameters, $argv;
327 0           next;
328             }
329              
330 0 0         if ($late)
331             {
332 0           push @late_parameters, $argv;
333 0           next;
334             }
335              
336 0 0         if (substr($argv, 0, 2) eq '--')
337             {
338 0           $opt = undef;
339 0 0         if (length($argv) == 2)
340             {
341 0           $late = 1;
342 0           next;
343             }
344 0           my @arg = split('=', $argv, 2);
345 0           $result{$arg[0]} = $arg[1];
346 0 0         unless (defined($result{$arg[0]}))
347             {
348 0           $result{$arg[0]} = "";
349 0           $opt = $arg[0];
350             }
351 0           next;
352             }
353              
354 0 0 0       if (substr($argv, 0, 1) eq '-' and length($argv) != 1)
355             {
356 0           $opt = undef;
357 0           my @arg = split('=', $argv, 2);
358 0           $result{$arg[0]} = $arg[1];
359 0 0         unless (defined($result{$arg[0]}))
360             {
361 0           $result{$arg[0]} = "";
362 0           $opt = $arg[0];
363             }
364 0           next;
365             }
366              
367 0 0 0       if ($prefs->{valuableArgs} and $opt)
368             {
369 0           $result{$opt} = $argv;
370 0           $opt = undef;
371 0           next;
372             }
373 0           $opt = undef;
374              
375 0           push @parameters, $argv;
376             }
377              
378 0 0         $result{command} = shift @parameters unless $prefs->{noCommand};
379 0           $result{parameters} = \@parameters;
380 0           $result{late_parameters} = \@late_parameters;
381              
382 0           return \%result;
383             }
384             sub commandArgs
385             {
386 0     0 0   return cmdargs(@_);
387             }
388             sub cmdArgs
389             {
390 0     0 0   return cmdargs(@_);
391             }
392              
393             =head2 whereis($name, $path)
394              
395             B I
396              
397             searches valid binary in search path
398              
399             $name: I
400              
401             $path: I
402              
403             return value: I
404              
405             =cut
406             sub whereis
407             {
408 0     0 1   my ($name, $path) = @_;
409 0 0         return () unless $name;
410 0 0         $path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" unless $path;
411 0           return grep(-x $_, map("$_/$name", split(":", $path)));
412             }
413             sub whereisBin
414             {
415 0     0 0   return whereis(@_);
416             }
417              
418             =head2 file_cache($tag, $expiry, $coderef)
419              
420             B I
421              
422             gets most recent cached value in file cache by given tag and caller function if there is cached value in expiry period. Otherwise tries to get current value using $coderef, puts value in cache and cleanups old cache values.
423              
424             $tag: I
425              
426             $expiry: I
427              
428             =over
429              
430             E0: I
431              
432             =0: I
433              
434             E0: I
435              
436             =back
437              
438             $coderef: I
439              
440             return value: I
441              
442             =cut
443             sub file_cache
444             {
445 0     0 1   my ($tag, $expiry, $coderef) = @_;
446 0           my $result;
447 0           my $now = time();
448 0           my @cleanup;
449 0           my $caller = (caller(1))[3];
450 0 0         $caller = (caller(0))[0] unless $caller;
451 0           $caller = (caller(0))[3].",$caller";
452 0           my $tag_encoded = "";
453 0           for (0..(bytes::length($tag)-1))
454             {
455 0           my $c = bytes::substr($tag, $_, 1);
456 0 0         if ($c =~ /\W/)
457             {
458 0           $c = uc(sprintf("%%%x", bytes::ord($c)));
459             }
460 0           $tag_encoded .= $c;
461             }
462 0           my $tmp_base = "/tmp/";
463 0           my $tmp_prefix = $caller;
464 0           $tmp_prefix =~ s/\Q::\E/-/g;
465 0           $tmp_prefix .= ".$tag_encoded,";
466 0           for my $tmp_path (sort {$b cmp $a} glob("${tmp_base}$tmp_prefix*"))
  0            
467             {
468 0 0         if (my ($epoch, $pid) = $tmp_path =~ /^\Q${tmp_base}$tmp_prefix\E(\d*)\.(\d*)/)
469             {
470 0 0 0       if ($expiry < 0 or ($expiry > 0 and $now-$epoch < $expiry))
      0        
471             {
472 0 0         if (not defined($result))
473             {
474 0           my $tmp;
475 0           $tmp = file_get_contents($tmp_path);
476 0 0         if ($tmp)
477             {
478 0 0         if ($tmp =~ /^SCALAR\n(.*)/)
479             {
480 0           $result = $1;
481             } else
482             {
483 0           eval { $result = from_json($tmp, {utf8 => 1}) };
  0            
484             }
485             }
486             }
487 0           next;
488             }
489             }
490 0           unshift @cleanup, $tmp_path;
491             }
492 0 0         if (not defined($result))
493             {
494 0 0         $result = $coderef->() if ref($coderef) eq 'CODE';
495 0 0         if (defined($result))
496             {
497 0           my $tmp;
498 0 0         unless (ref($result))
499             {
500 0           $tmp = "SCALAR\n$result";
501             } else
502             {
503 0 0 0       eval { $tmp = to_json($result, {utf8 => 1, pretty => 1}) } if ref($result) eq "ARRAY" or ref($result) eq "HASH";
  0            
504             }
505 0 0 0       if ($tmp and file_put_contents("${tmp_base}tmp.$tmp_prefix$now.$$", $tmp) and rename("${tmp_base}tmp.$tmp_prefix$now.$$", "${tmp_base}$tmp_prefix$now.$$"))
      0        
506             {
507 0           pop @cleanup;
508 0           for (@cleanup)
509             {
510 0           unlink($_);
511             }
512             }
513             }
514             }
515 0           return $result;
516             }
517             sub fileCache
518             {
519 0     0 0   return file_cache(@_);
520             }
521              
522             =head2 get_pod_text($file_name, $section, $exclude_section)
523              
524             B I
525              
526             gets a text of pod contents in given file
527              
528             $file_name: I
529              
530             $section: I
531              
532             $exclude_section: I
533              
534             return value: I
535              
536             =cut
537             sub get_pod_text
538             {
539 0     0 1   my ($file_name, $section, $exclude_section) = @_;
540 0 0         $file_name = "$FindBin::Bin/$FindBin::Script" unless $file_name;
541 0 0         return unless -e $file_name;
542 0           my $parser = Pod::Simple::Text->new();
543 0           my $text;
544 0           $parser->output_string(\$text);
545 0           eval { $parser->parse_file($file_name) };
  0            
546 0 0         return if $@;
547 0           utf8::decode($text);
548 0 0         $section = ltrim($section) if $section;
549 0           my @text = split(/^/m, $text);
550 0           my $result;
551             my @result;
552 0           for my $line (@text)
553             {
554 0           chomp $line;
555 0 0 0       if (defined($section) and not defined($result))
556             {
557 0 0         if ($line eq $section)
558             {
559 0 0         unless ($exclude_section)
560             {
561 0           $result = "$line\n";
562 0           push @result, $line;
563             } else
564             {
565 0           $result = "";
566             }
567             }
568 0           next;
569             }
570 0 0 0       last if defined($section) and $line =~ /^\S+/;
571 0 0         $result = "" unless defined($result);
572 0           $result .= "$line\n";
573 0           push @result, $line;
574             }
575 0 0         return @result if wantarray;
576 0           return $result;
577             }
578             sub getPodText
579             {
580 0     0 0   return get_pod_text(@_);
581             }
582              
583             =head2 array_to_hash(@array)
584              
585             returns hash with indexes for given array
586              
587             @array: I
588              
589             return value: I>
590              
591             =cut
592             sub array_to_hash
593             {
594 0     0 1   my %h;
595 0           my $i = 0;
596 0           %h = map { $i++ => $_ } @_;
  0            
597 0 0         return \%h unless wantarray;
598 0           return %h;
599             }
600              
601              
602             1;
603             __END__