File Coverage

lib/Class/Usul/Functions.pm
Criterion Covered Total %
statement 425 451 94.2
branch 120 182 65.9
condition 93 166 56.0
subroutine 116 120 96.6
pod 83 83 100.0
total 837 1002 83.5


line stmt bran cond sub pod time code
1             package Class::Usul::Functions;
2              
3 28     28   806596 use strict;
  28         71  
  28         782  
4 28     28   154 use warnings;
  28         83  
  28         782  
5 28     28   1036 use parent 'Exporter::Tiny';
  28         599  
  28         166  
6              
7 28     28   20922 use Class::Inspector;
  28         83249  
  28         918  
8 28     28   13209 use Class::Null;
  28         9130  
  28         1061  
9 28         329 use Class::Usul::Constants qw( ASSERT DEFAULT_CONFHOME DEFAULT_ENVDIR
10             DIGEST_ALGORITHMS EXCEPTION_CLASS
11             PERL_EXTNS PREFIX UNTAINT_CMDLINE
12 28     28   1513 UNTAINT_IDENTIFIER UNTAINT_PATH UUID_PATH );
  28         73  
13 28     28   36002 use Cwd qw( );
  28         86  
  28         5505  
14             use Data::Printer alias => q(_data_dumper), colored => 1, indent => 3,
15 0         0 filters => { 'DateTime' => sub { $_[ 0 ].q() },
16 8         27196 'File::DataClass::IO' => sub { $_[ 0 ]->pathname },
17 0         0 'JSON::XS::Boolean' => sub { $_[ 0 ].q() },
18 0         0 'Type::Tiny' => sub { $_[ 0 ]->display_name },
19 0         0 'Type::Tiny::Enum' => sub { $_[ 0 ]->display_name },
20 28     28   23367 'Type::Tiny::Union' => sub { $_[ 0 ]->display_name }, };
  28         843809  
  28         1204  
  0         0  
21 28     28   63867 use Digest qw( );
  28         14053  
  28         697  
22 28     28   203 use Digest::MD5 qw( md5 );
  28         79  
  28         2040  
23 28     28   188 use English qw( -no_match_vars );
  28         77  
  28         260  
24 28     28   10591 use Fcntl qw( F_SETFL O_NONBLOCK );
  28         85  
  28         1386  
25 28     28   169 use File::Basename qw( basename dirname );
  28         78  
  28         1972  
26 28     28   14914 use File::DataClass::Functions qw( supported_extensions );
  28         303071  
  28         2201  
27 28     28   20510 use File::DataClass::IO qw( );
  28         2205404  
  28         1196  
28 28     28   326 use File::HomeDir qw( );
  28         98  
  28         860  
29 28     28   213 use File::Spec::Functions qw( canonpath catdir catfile curdir );
  28         83  
  28         2513  
30 28     28   251 use List::Util qw( first );
  28         101  
  28         2158  
31 28     28   246 use Module::Runtime qw( is_module_name require_module );
  28         96  
  28         331  
32 28     28   2277 use Scalar::Util qw( blessed openhandle );
  28         96  
  28         1800  
33 28     28   21119 use Socket qw( AF_UNIX SOCK_STREAM PF_UNSPEC );
  28         118989  
  28         5602  
34 28     28   330 use Symbol;
  28         84  
  28         2131  
35 28     28   258 use Sys::Hostname qw( hostname );
  28         81  
  28         2084  
36 28         350 use Unexpected::Functions qw( is_class_loaded PathAlreadyExists
37 28     28   262 PathNotFound Tainted Unspecified );
  28         108  
38 28     28   42252 use User::pwent;
  28         122219  
  28         189  
39              
40             our @EXPORT_OK = qw( abs_path app_prefix arg_list assert assert_directory
41             base64_decode_ns base64_encode_ns bsonid bsonid_time
42             bson64id bson64id_time canonicalise class2appdir
43             classdir classfile create_token create_token64 cwdp
44             dash2under data_dumper digest distname elapsed emit
45             emit_err emit_to ensure_class_loaded env_prefix
46             escape_TT exception find_apphome find_source first_char
47             fqdn fullname get_cfgfiles get_user hex2str home2appldir
48             io is_arrayref is_coderef is_hashref is_member is_win32
49             list_attr_of loginid logname merge_attributes my_prefix
50             nonblocking_write_pipe_pair ns_environment pad
51             prefix2class socket_pair split_on__ split_on_dash
52             squeeze strip_leader sub_name symlink thread_id throw
53             throw_on_error trim unescape_TT untaint_cmdline
54             untaint_identifier untaint_path untaint_string urandom
55             uuid whiten zip chain compose curry fold Y factorial
56             fibonacci product sum );
57              
58             our %EXPORT_REFS = ( assert => sub { ASSERT }, );
59             our %EXPORT_TAGS = ( all => [ @EXPORT_OK ], );
60              
61             # Package variables
62 24     24   26306 my $bson_id_count : shared = 0;
  24         32484  
  24         221887  
63             my $bson2_id_count = 0;
64             my $bson2_prev_time = 0;
65             my $digest_cache;
66             my $host_id = substr md5( hostname ), 0, 3;
67              
68             # Private functions
69             my $_base64_char_set = sub {
70             return [ 0 .. 9, 'A' .. 'Z', '_', 'a' .. 'z', '~', '+' ];
71             };
72              
73             my $_bsonid_inc = sub {
74             my ($now, $version) = @_;
75              
76             $version or return substr pack( 'N', $bson_id_count++ % 0xFFFFFF ), 1, 3;
77              
78             $bson2_id_count++; $now > $bson2_prev_time and $bson2_id_count = 0;
79             $bson2_prev_time = $now;
80              
81             $version < 2 and return (substr pack( 'n', thread_id() % 0xFF ), 1, 1)
82             .(pack 'n', $bson2_id_count % 0xFFFF);
83              
84             $version < 3 and return (pack 'n', thread_id() % 0xFFFF )
85             .(pack 'n', $bson2_id_count % 0xFFFF);
86              
87             return (pack 'n', thread_id() % 0xFFFF )
88             .(substr pack( 'N', $bson2_id_count % 0xFFFFFF ), 1, 3);
89             };
90              
91             my $_bsonid_time = sub {
92             my ($now, $version) = @_;
93              
94             (not $version or $version < 2) and return pack 'N', $now;
95              
96             $version < 3 and return (substr pack( 'N', $now >> 32 ), 2, 2)
97             .(pack 'N', $now % 0xFFFFFFFF);
98              
99             return (pack 'N', $now >> 32).(pack 'N', $now % 0xFFFFFFFF);
100             };
101              
102             my $_catpath = sub {
103             return untaint_path( catfile( @_ ) );
104             };
105              
106             my $_get_env_var_for_conf = sub {
107             my $file = $ENV{ ($_[ 0 ] || return) };
108             my $path = $file ? dirname( $file ) : q();
109              
110             return $path = assert_directory( $path ) ? $path : undef;
111             };
112              
113             my $_get_pod_content_for_attr = sub {
114             my ($class, $attr) = @_; my $pod;
115              
116             my $src = find_source( $class )
117             or throw( 'Class [_1] cannot find source', [ $class ] );
118             my $events = Pod::Eventual::Simple->read_file( $src );
119              
120             for (my $ev_no = 0, my $max = @{ $events }; $ev_no < $max; $ev_no++) {
121             my $ev = $events->[ $ev_no ]; $ev->{type} eq 'command' or next;
122              
123             $ev->{content} =~ m{ (?: ^|[< ]) $attr (?: [ >]|$ ) }msx or next;
124              
125             $ev_no++ while ($ev = $events->[ $ev_no + 1 ] and $ev->{type} eq 'blank');
126              
127             $ev and $ev->{type} eq 'text' and $pod = $ev->{content} and last;
128             }
129              
130             $pod //= 'Undocumented'; chomp $pod; $pod =~ s{ [\n] }{ }gmx;
131              
132             $pod = squeeze( $pod ); $pod =~ m{ \A (.+) \z }msx and $pod = $1;
133              
134             return $pod;
135             };
136              
137             my $_index64 = sub {
138             return [ qw(XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
139             XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
140             XX XX XX XX XX XX XX XX XX XX XX 64 XX XX XX XX
141             0 1 2 3 4 5 6 7 8 9 XX XX XX XX XX XX
142             XX 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
143             25 26 27 28 29 30 31 32 33 34 35 XX XX XX XX 36
144             XX 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
145             52 53 54 55 56 57 58 59 60 61 62 XX XX XX 63 XX
146              
147             XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
148             XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
149             XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
150             XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
151             XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
152             XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
153             XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
154             XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX) ];
155             };
156              
157             my $_pseudo_random = sub {
158             return join q(), time, rand 10_000, $PID, {};
159             };
160              
161             my $_bsonid = sub {
162             my $version = shift;
163             my $now = time;
164             my $time = $_bsonid_time->( $now, $version );
165             my $pid = pack 'n', $PID % 0xFFFF;
166              
167             return $time.$host_id.$pid.$_bsonid_inc->( $now, $version );
168             };
169              
170             my $_find_cfg_in_inc = sub {
171             my ($classdir, $file, $extns) = @_;
172              
173             for my $dir (grep { defined and -d $_ }
174             map { abs_path( catdir( $_, $classdir ) ) } @INC) {
175             for my $extn (@{ $extns // [ supported_extensions() ] }) {
176             my $path = $_catpath->( $dir, $file.$extn );
177              
178             -f $path and return dirname( $path );
179             }
180             }
181              
182             return;
183             };
184              
185             my $_read_variable = sub {
186             my ($dir, $file, $variable) = @_; my $path;
187              
188             ($dir and $file and $variable) or return;
189             is_arrayref( $dir ) and $dir = catdir( @{ $dir } );
190             $path = io( $_catpath->( $dir, $file ) )->chomp;
191             ($path->exists and $path->is_file) or return;
192              
193             return first { length }
194             map { trim( (split '=', $_)[ 1 ] ) }
195             grep { m{ \A \s* $variable \s* [=] }mx }
196             reverse $path->getlines;
197             };
198              
199             my $_get_file_var = sub {
200             my ($dir, $file, $classdir) = @_;
201              
202             my $path; $path = $_read_variable->( $dir, ".${file}", 'APPLDIR' )
203             and $path = catdir( $path, 'lib', $classdir );
204              
205             return $path = assert_directory( $path ) ? $path : undef;
206             };
207              
208             my $_get_known_file_var = sub {
209             my ($appname, $classdir) = @_; length $appname or return;
210              
211             my $path; $path = $_read_variable->( DEFAULT_ENVDIR(), $appname, 'APPLDIR' )
212             and $path = catdir( $path, 'lib', $classdir );
213              
214             return $path = assert_directory( $path ) ? $path : undef;
215             };
216              
217             # Construction
218             sub _exporter_fail {
219 4     4   6178 my ($class, $name, $value, $globals) = @_;
220              
221             exists $EXPORT_REFS{ $name }
222 4 50       43 and return ( $name => $EXPORT_REFS{ $name }->() );
223              
224 0         0 throw( 'Subroutine [_1] not found in package [_2]', [ $name, $class ] );
225             }
226              
227             # Public functions
228             sub abs_path ($) {
229 1094 100 100 1094 1 2508 my $v = shift; (defined $v and length $v) or return $v;
  1094         4663  
230              
231 962 50 33     2102 is_ntfs() and not -e $v and return untaint_path( $v ); # Hate
232              
233 962         2424 $v = Cwd::abs_path( untaint_path( $v ) );
234              
235 962 50 33     2919 is_win32() and defined $v and $v =~ s{ / }{\\}gmx; # More hate
236              
237 962         3632 return $v;
238             }
239              
240             sub app_prefix ($) {
241 97   100 97 1 570 (my $v = lc ($_[ 0 ] // q())) =~ s{ :: }{_}gmx; return $v;
  97         367  
242             }
243              
244             sub arg_list (;@) {
245 382 100 100 382 1 5003 return $_[ 0 ] && ref $_[ 0 ] eq 'HASH' ? { %{ $_[ 0 ] } }
  285 100       2546  
246             : $_[ 0 ] ? { @_ }
247             : {};
248             }
249              
250             sub assert_directory ($) {
251 222     222 1 1775 my $v = abs_path( $_[ 0 ] );
252              
253 222 100 100     1115 defined $v and length $v and -d "${v}" and return $v;
      100        
254              
255 221         1004 return;
256             }
257              
258             sub base64_decode_ns ($) {
259 2 50   2 1 8 my $x = shift; defined $x or return; my @x = split q(), $x;
  2         6  
  2         9  
260              
261 2         6 my $index = $_index64->(); my $j = 0; my $k = 0;
  2         4  
  2         5  
262              
263 2         3 my $len = length $x; my $pad = 64; my @y = ();
  2         4  
  2         4  
264              
265             ROUND: {
266 2         4 while ($j < $len) {
  2         6  
267 10         14 my @c = (); my $i = 0;
  10         28  
268              
269 10         20 while ($i < 4) {
270 40         61 my $uc = $index->[ ord $x[ $j++ ] ];
271              
272 40 50       85 $uc ne 'XX' and $c[ $i++ ] = 0 + $uc; $j == $len or next;
  40 100       101  
273              
274 2 50       5 if ($i < 4) {
275 0 0       0 $i < 2 and last ROUND; $i == 2 and $c[ 2 ] = $pad; $c[ 3 ] = $pad;
  0 0       0  
  0         0  
276             }
277              
278 2         4 last;
279             }
280              
281 10 50 33     40 ($c[ 0 ] == $pad || $c[ 1 ] == $pad) and last;
282 10         17 $y[ $k++ ] = ( $c[ 0 ] << 2) | (($c[ 1 ] & 0x30) >> 4);
283 10 50       22 $c[ 2 ] == $pad and last;
284 10         19 $y[ $k++ ] = (($c[ 1 ] & 0x0F) << 4) | (($c[ 2 ] & 0x3C) >> 2);
285 10 100       23 $c[ 3 ] == $pad and last;
286 9         21 $y[ $k++ ] = (($c[ 2 ] & 0x03) << 6) | $c[ 3 ];
287             }
288             }
289              
290 2         5 return join q(), map { chr $_ } @y;
  29         72  
291             }
292              
293             sub base64_encode_ns (;$) {
294 2 50   2 1 5 my $x = shift; defined $x or return; my @x = split q(), $x;
  2         7  
  2         11  
295              
296 2         7 my $basis = $_base64_char_set->(); my $len = length $x; my @y = ();
  2         3  
  2         5  
297              
298 2         8 for (my $i = 0, my $j = 0; $len > 0; $len -= 3, $i += 3) {
299 10 50       15 my $c1 = ord $x[ $i ]; my $c2 = $len > 1 ? ord $x[ $i + 1 ] : 0;
  10         24  
300              
301 10         18 $y[ $j++ ] = $basis->[ $c1 >> 2 ];
302 10         19 $y[ $j++ ] = $basis->[ (($c1 & 0x3) << 4) | (($c2 & 0xF0) >> 4) ];
303              
304 10 100       21 if ($len > 2) {
    50          
305 9         13 my $c3 = ord $x[ $i + 2 ];
306              
307 9         17 $y[ $j++ ] = $basis->[ (($c2 & 0xF) << 2) | (($c3 & 0xC0) >> 6) ];
308 9         25 $y[ $j++ ] = $basis->[ $c3 & 0x3F ];
309             }
310             elsif ($len == 2) {
311 1         4 $y[ $j++ ] = $basis->[ ($c2 & 0xF) << 2 ];
312 1         3 $y[ $j++ ] = $basis->[ 64 ];
313             }
314             else { # len == 1
315 0         0 $y[ $j++ ] = $basis->[ 64 ];
316 0         0 $y[ $j++ ] = $basis->[ 64 ];
317             }
318             }
319              
320 2         18 return join q(), @y;
321             }
322              
323             sub bsonid (;$) {
324 1     1 1 5 return unpack 'H*', $_bsonid->( $_[ 0 ] );
325             }
326              
327             sub bsonid_time ($) {
328 1     1 1 5 return unpack 'N', substr hex2str( $_[ 0 ] ), 0, 4;
329             }
330              
331             sub bson64id (;$) {
332 1     1 1 373 return base64_encode_ns( $_bsonid->( 2 ) );
333             }
334              
335             sub bson64id_time ($) {
336 1     1 1 5 return unpack 'N', substr base64_decode_ns( $_[ 0 ] ), 2, 4;
337             }
338              
339             sub canonicalise ($;$) {
340 58     58 1 28546 my ($base, $relpath) = @_;
341              
342 58 50       179 $base = is_arrayref( $base ) ? catdir( @{ $base } ) : $base;
  0         0  
343 58 100       3360 $relpath or return canonpath( untaint_path( $base ) );
344              
345 33 50       97 my @relpath = is_arrayref( $relpath ) ? @{ $relpath } : $relpath;
  0         0  
346 33         184 my $path = canonpath( untaint_path( catdir( $base, @relpath ) ) );
347              
348 33 50       1564 -d $path and return $path;
349              
350 33         231 return canonpath( untaint_path( catfile( $base, @relpath ) ) );
351             }
352              
353             sub class2appdir ($) {
354 51     51 1 1047 return lc distname( $_[ 0 ] );
355             }
356              
357             sub classdir ($) {
358 23   50 23 1 249 return catdir( split m{ :: }mx, $_[ 0 ] // q() );
359             }
360              
361             sub classfile ($) {
362 56     56 1 663 return catfile( split m{ :: }mx, $_[ 0 ].'.pm' );
363             }
364              
365             sub create_token (;$) {
366 10   66 10 1 57 return digest( $_[ 0 ] // urandom() )->hexdigest;
367             }
368              
369             sub create_token64 (;$) {
370 0   0 0 1 0 return digest( $_[ 0 ] // urandom() )->b64digest;
371             }
372              
373             sub cwdp () {
374 1     1 1 610 return abs_path( curdir );
375             }
376              
377             sub dash2under (;$) {
378 4   50 4 1 23 (my $v = $_[ 0 ] // q()) =~ s{ [\-] }{_}gmx; return $v;
  4         22  
379             }
380              
381             sub data_dumper (;@) {
382 2     2 1 17 _data_dumper( @_ ); return 1;
  2         22601  
383             }
384              
385             sub digest ($) {
386 10     10 1 325 my $seed = shift; my ($candidate, $digest);
  10         58  
387              
388 10 100       30 if ($digest_cache) { $digest = Digest->new( $digest_cache ) }
  7         60  
389             else {
390 3         21 for (DIGEST_ALGORITHMS) {
391 3 50       8 $candidate = $_; $digest = eval { Digest->new( $candidate ) } and last;
  3         7  
  3         29  
392             }
393              
394 3 50       8612 $digest or throw( 'Digest algorithm not found' );
395 3         8 $digest_cache = $candidate;
396             }
397              
398 10         471 $digest->add( $seed );
399              
400 10         168 return $digest;
401             }
402              
403             sub distname ($) {
404 52   50 52 1 325 (my $v = $_[ 0 ] // q()) =~ s{ :: }{-}gmx; return $v;
  52         268  
405             }
406              
407             #head2 downgrade
408             # $sv_pv = downgrade $sv_pvgv;
409             #Horrendous Perl bug is promoting C<PV> and C<PVMG> type scalars to
410             #C<PVGV>. Serializing these values with L<Storable> throws a can't
411             #store SCALAR items error. This functions copies the string value of
412             #the input scalar to the output scalar but resets the output scalar
413             #type to C<PV>
414             #sub downgrade (;$) {
415             # my $x = shift // q(); my ($y) = $x =~ m{ (.*) }msx; return $y;
416             #}
417              
418             sub elapsed () {
419 1     1 1 10 return time - $BASETIME;
420             }
421              
422             sub emit (;@) {
423 10   100 10 1 1876 my @args = @_; $args[ 0 ] //= q(); chomp( @args );
  10         41  
  10         25  
424              
425 10         45 local ($OFS, $ORS) = ("\n", "\n");
426              
427 10 50       59 return openhandle *STDOUT ? emit_to( *STDOUT, @args ) : undef;
428             }
429              
430             sub emit_err (;@) {
431 3   50 3 1 3660 my @args = @_; $args[ 0 ] //= q(); chomp( @args );
  3         17  
  3         11  
432              
433 3         22 local ($OFS, $ORS) = ("\n", "\n");
434              
435 3 50       27 return openhandle *STDERR ? emit_to( *STDERR, @args ) : undef;
436             }
437              
438             sub emit_to ($;@) {
439 29     29 1 227 my ($handle, @args) = @_; local $OS_ERROR;
  29         401  
440              
441 29   33     153 return (print {$handle} @args or throw( 'IO error: [_1]', [ $OS_ERROR ] ));
442             }
443              
444             sub ensure_class_loaded ($;$) {
445 17   50 17 1 2111 my ($class, $opts) = @_; $opts //= {};
  17         134  
446              
447 17 50       68 $class or throw( Unspecified, [ 'class name' ], level => 2 );
448              
449 17 50       98 is_module_name( $class )
450             or throw( 'String [_1] invalid classname', [ $class ], level => 2 );
451              
452 17 100 66     457 not $opts->{ignore_loaded} and is_class_loaded( $class ) and return 1;
453              
454 9         432 eval { require_module( $class ) }; throw_on_error( { level => 3 } );
  9         51  
  9         1073836  
455              
456 8 50       534 is_class_loaded( $class )
457             or throw( 'Class [_1] loaded but package undefined',
458             [ $class ], level => 2 );
459              
460 8         406 return 1;
461             }
462              
463             sub env_prefix ($) {
464 51     51 1 905 return uc app_prefix( $_[ 0 ] );
465             }
466              
467             sub escape_TT (;$$) {
468 1 50   1 1 6 my $v = defined $_[ 0 ] ? $_[ 0 ] : q();
469 1   50     9 my $fl = ($_[ 1 ] && $_[ 1 ]->[ 0 ]) || '<';
470 1   50     10 my $fr = ($_[ 1 ] && $_[ 1 ]->[ 1 ]) || '>';
471              
472 1         8 $v =~ s{ \[\% }{${fl}%}gmx; $v =~ s{ \%\] }{%${fr}}gmx;
  1         5  
473              
474 1         5 return $v;
475             }
476              
477             sub exception (;@) {
478 8     8 1 41650 return EXCEPTION_CLASS->caught( @_ );
479             }
480              
481             sub find_apphome ($;$$) {
482 22     22 1 70 my ($appclass, $default, $extns) = @_; my $path;
  22         45  
483              
484             # 0. Pass the directory in (short circuit the search)
485 22 50       105 $path = assert_directory $default and return $path;
486              
487 22         94 my $app_pref = app_prefix $appclass;
488 22         94 my $appdir = class2appdir $appclass;
489 22         78 my $classdir = classdir $appclass;
490 22         94 my $env_pref = env_prefix $appclass;
491 22         226 my $my_home = File::HomeDir->my_home;
492              
493             # 1a. Environment variable - for application directory
494 22 50       1101 $path = assert_directory $ENV{ "${env_pref}_HOME" } and return $path;
495             # 1b. Environment variable - for config file
496 22 50       140 $path = $_get_env_var_for_conf->( "${env_pref}_CONFIG" ) and return $path;
497             # 2a. Users XDG_DATA_HOME env variable or XDG default share directory
498 22   33     208 $path = $ENV{ 'XDG_DATA_HOME' } // catdir( $my_home, '.local', 'share' );
499 22 50       120 $path = assert_directory catdir( $path, $appdir ) and return $path;
500             # 2b. Users home directory - dot file containing shell env variable
501 22 50       107 $path = $_get_file_var->( $my_home, $app_pref, $classdir ) and return $path;
502 22 50       222 $path = $_get_file_var->( $my_home, $appdir, $classdir ) and return $path;
503             # 2c. Users home directory - dot directory is apphome
504 22         151 $path = catdir( $my_home, ".${app_pref}" );
505 22 50       1524 $path = assert_directory $path and return $path;
506 22         144 $path = catdir( $my_home, ".${appdir}" );
507 22 50       89 $path = assert_directory $path and return $path;
508             # 3. Well known path containing shell env file
509 22 50       175 $path = $_get_known_file_var->( $appdir, $classdir ) and return $path;
510             # 4. Default install prefix
511 22         64 $path = catdir( @{ PREFIX() }, $appdir, 'default', 'lib', $classdir );
  22         82  
512 22 50       129 $path = assert_directory $path and return $path;
513             # 5a. Config file found in @INC - underscore as separator
514 22 50       126 $path = $_find_cfg_in_inc->( $classdir, $app_pref, $extns ) and return $path;
515             # 5b. Config file found in @INC - dash as separator
516 22 50       90 $path = $_find_cfg_in_inc->( $classdir, $appdir, $extns ) and return $path;
517             # 6. Default to /tmp
518 22         137 return untaint_path( DEFAULT_CONFHOME );
519             }
520              
521             sub find_source ($) {
522 55     55 1 1607 my $class = shift; my $file = classfile( $class ); my $path;
  55         214  
  55         177  
523              
524 55         175 for (@INC) {
525 165 100 66     878 $path = abs_path( catfile( $_, $file ) ) and -f $path and return $path;
526             }
527              
528 0         0 return;
529             }
530              
531             sub first_char ($) {
532 1     1 1 6 return substr $_[ 0 ], 0, 1;
533             }
534              
535             sub fqdn (;$) {
536 0   0 0 1 0 my $x = shift // hostname; return (gethostbyname( $x ))[ 0 ];
  0         0  
537             }
538              
539             sub fullname () {
540 0   0 0 1 0 my $v = (split m{ \s* , \s * }msx, (get_user()->gecos // q()))[ 0 ];
541              
542 0   0     0 $v //= q(); $v =~ s{ [\&] }{}gmx; # Coz af25e158-d0c7-11e3-bdcb-31d9eda79835
  0         0  
543              
544 0         0 return untaint_cmdline( $v );
545             }
546              
547             sub get_cfgfiles ($;$$) {
548 22     22 1 76 my ($appclass, $dirs, $extns) = @_;
549              
550 22   33     91 $appclass // throw( Unspecified, [ 'application class' ], level => 2 );
551 22 50 50     89 is_arrayref( $dirs ) or $dirs = [ $dirs || curdir ];
552              
553 22         110 my $app_pref = app_prefix $appclass;
554 22         112 my $appdir = class2appdir $appclass;
555 22         109 my $env_pref = env_prefix $appclass;
556 22   50     167 my $suffix = $ENV{ "${env_pref}_CONFIG_LOCAL_SUFFIX" } // '_local';
557 22         71 my @paths = ();
558              
559 22         69 for my $dir (@{ $dirs }) {
  22         71  
560 22   50     47 for my $extn (@{ $extns // [ supported_extensions() ] }) {
  22         160  
561 26         18678 for my $path (map { $_catpath->( $dir, $_ ) } "${app_pref}${extn}",
  104         258  
562             "${appdir}${extn}", "${app_pref}${suffix}${extn}",
563             "${appdir}${suffix}${extn}") {
564 104 50       1168 -f $path and push @paths, $path;
565             }
566             }
567             }
568              
569 22         160 return \@paths;
570             }
571              
572             sub get_user (;$) {
573 6 50   6 1 23 my $user = shift; is_win32() and return Class::Null->new;
  6         25  
574              
575 6 50 33     35 defined $user and $user !~ m{ \A \d+ \z }mx and return getpwnam( $user );
576              
577 6   33     68 return getpwuid( $user // $UID );
578             }
579              
580             sub hex2str (;$) {
581 2   50 2 1 1784 my @a = split m{}mx, shift // q(); my $str = q();
  2         8  
582              
583 2         15 while (my ($x, $y) = splice @a, 0, 2) { $str .= pack 'C', hex "${x}${y}" }
  13         50  
584              
585 2         18 return $str;
586             }
587              
588             sub home2appldir ($) {
589 7 100   7 1 50 $_[ 0 ] or return; my $dir = io( $_[ 0 ] );
  6         280  
590              
591 6   100     26354 $dir = $dir->parent while ($dir ne $dir->parent and $dir !~ m{ lib \z }mx);
592              
593 6 100       16422 return $dir ne $dir->parent ? $dir->parent : undef;
594             }
595              
596             sub io (;@) {
597 211     211 1 44932 return File::DataClass::IO->new( @_ );
598             }
599              
600             sub is_arrayref (;$) {
601 2607 100 100 2607 1 39389 return $_[ 0 ] && ref $_[ 0 ] eq 'ARRAY' ? 1 : 0;
602             }
603              
604             sub is_coderef (;$) {
605 58 100 100 58 1 1127 return $_[ 0 ] && ref $_[ 0 ] eq 'CODE' ? 1 : 0;
606             }
607              
608             sub is_hashref (;$) {
609 437 100 100 437 1 6482 return $_[ 0 ] && ref $_[ 0 ] eq 'HASH' ? 1 : 0;
610             }
611              
612             sub is_member (;@) {
613 502 50   502 1 18538 my ($candidate, @args) = @_; $candidate or return;
  502         2042  
614              
615 502 100       1698 is_arrayref $args[ 0 ] and @args = @{ $args[ 0 ] };
  424         2001  
616              
617 502 100   1454   4848 return (first { $_ eq $candidate } @args) ? 1 : 0;
  1454         7680  
618             }
619              
620             sub is_ntfs () {
621 962 50 33 962 1 1772 return is_win32() || lc $OSNAME eq 'cygwin' ? 1 : 0;
622             }
623              
624             sub is_win32 () {
625 2501 50   2501 1 23765 return lc $OSNAME eq 'mswin32' ? 1 : 0;
626             }
627              
628             sub list_attr_of ($;@) {
629 1     1 1 1352 my ($obj, @except) = @_; my $class = blessed $obj;
  1         5  
630              
631 1         5 ensure_class_loaded( 'Pod::Eventual::Simple' );
632              
633 1 50       4 is_member 'new', @except or push @except, 'new';
634              
635 45         6607 return map { my $attr = $_->[0]; [ @{ $_ }, $obj->$attr ] }
  45         72  
  45         696  
636 45         159 map { [ $_->[1], $_->[0], $_get_pod_content_for_attr->( @{ $_ } ) ] }
  45         182  
637 50 100       144 grep { $_->[0] ne 'Moo::Object' and not is_member $_->[1], @except }
638 50         1564 map { m{ \A (.+) \:\: ([^:]+) \z }mx; [ $1, $2 ] }
  50         146  
639 1         5 @{ Class::Inspector->methods( $class, 'full', 'public' ) };
  1         25  
640             }
641              
642             sub loginid (;$) {
643 6   50 6 1 31 return untaint_cmdline( get_user( $_[ 0 ] )->name || 'unknown' );
644             }
645              
646             sub logname (;$) { # Deprecated use loginid
647 5   33 5 1 63 return untaint_cmdline( $ENV{USER} || $ENV{LOGNAME} || loginid( $_[ 0 ] ) );
648             }
649              
650             sub merge_attributes ($@) {
651 48     48 1 203 my ($dest, @args) = @_;
652              
653 48 50       242 my $attr = is_arrayref( $args[ -1 ] ) ? pop @args : [];
654              
655 48   66     143 for my $k (grep { not exists $dest->{ $_ } or not defined $dest->{ $_ } }
  177         700  
656 48         175 @{ $attr }) {
657 173         358 my $i = 0; my $v;
  173         328  
658              
659 173   100     979 while (not defined $v and defined( my $src = $args[ $i++ ] )) {
660 203         795 my $class = blessed $src;
661              
662 203 100       3342 $v = $class ? ($src->can( $k ) ? $src->$k() : undef) : $src->{ $k };
    100          
663             }
664              
665 173 100       15861 defined $v and $dest->{ $k } = $v;
666             }
667              
668 48         215 return $dest;
669             }
670              
671             sub my_prefix (;$) {
672 2   50 2 1 18 return split_on__( basename( $_[ 0 ] // q(), PERL_EXTNS ) );
673             }
674              
675             sub nonblocking_write_pipe_pair () {
676 384 50   384 1 1045 my ($r, $w); pipe $r, $w or throw( 'No pipe' );
  384         9678  
677              
678 384         1681 fcntl $w, F_SETFL, O_NONBLOCK; $w->autoflush( 1 );
  384         3121  
679              
680 384         32433 binmode $r; binmode $w;
  384         1042  
681              
682 384         2170 return [ $r, $w ];
683             }
684              
685             sub ns_environment ($$;$) {
686 6     6 1 107 my ($class, $k, $v) = @_; $k = (env_prefix $class).'_'.(uc $k);
  6         27  
687              
688 6 50       134 return defined $v ? $ENV{ $k } = $v : $ENV{ $k };
689             }
690              
691             sub pad ($$;$$) {
692 7     7 1 23 my ($v, $wanted, $str, $direction) = @_; my $len = $wanted - length $v;
  7         20  
693              
694 7 100 66     31 $len > 0 or return $v; (defined $str and length $str) or $str = q( );
  6 100       33  
695              
696 6         23 my $pad = substr( $str x $len, 0, $len );
697              
698 6 100 100     45 (not $direction or $direction eq 'right') and return $v.$pad;
699 2 100       40 $direction eq 'left' and return $pad.$v;
700              
701 1         14 return (substr $pad, 0, int( (length $pad) / 2 )).$v
702             .(substr $pad, 0, int( 0.99999999 + (length $pad) / 2 ));
703             }
704              
705             sub prefix2class (;$) {
706 1     1 1 5 return join '::', map { ucfirst } split m{ - }mx, my_prefix( $_[ 0 ] );
  2         17  
707             }
708              
709             sub socket_pair () {
710 1     1 1 8 my $rdr = gensym; my $wtr = gensym;
  1         25  
711              
712 1 50       59 socketpair( $rdr, $wtr, AF_UNIX, SOCK_STREAM, PF_UNSPEC )
713             or throw( $EXTENDED_OS_ERROR );
714 1         10 shutdown ( $rdr, 1 ); # No more writing for reader
715 1         5 shutdown ( $wtr, 0 ); # No more reading for writer
716              
717 1         6 return [ $rdr, $wtr ];
718             }
719              
720             sub split_on__ (;$$) {
721 11   50 11 1 747 return (split m{ _ }mx, $_[ 0 ] // q())[ $_[ 1 ] // 0 ];
      100        
722             }
723              
724             sub split_on_dash (;$$) {
725 9   50 9 1 264 return (split m{ \- }mx, $_[ 0 ] // q())[ $_[ 1 ] // 0 ];
      50        
726             }
727              
728             sub squeeze (;$) {
729 46   50 46 1 675 (my $v = $_[ 0 ] // q()) =~ s{ \s+ }{ }gmx; return $v;
  46         158  
730             }
731              
732             sub strip_leader (;$) {
733 647   50 647 1 3235 (my $v = $_[ 0 ] // q()) =~ s{ \A [^:]+ [:] \s+ }{}msx; return $v;
  647         4222  
734             }
735              
736             sub sub_name (;$) {
737 1   50 1 1 10 my $frame = 1 + ($_[ 0 ] // 0);
738              
739 1   50     19 return (split m{ :: }mx, ((caller $frame)[ 3 ]) // 'main')[ -1 ];
740             }
741              
742             sub symlink (;$$$) {
743 3     3 1 11 my ($from, $to, $base) = @_;
744              
745 3 50 66     19 defined $base and not CORE::length $base and $base = File::Spec->rootdir;
746 3 50       10 $from or throw( Unspecified, [ 'path from' ] );
747 3         12 $from = io( $from )->absolute( $base );
748 3 50       2697 $from->exists or throw( PathNotFound, [ "${from}" ] );
749 3 50       213 $to or throw( Unspecified, [ 'path to' ] );
750 3 50       9 $to = io( $to )->absolute( $base ); $to->is_link and $to->unlink;
  3         2722  
751 3 50       228 $to->exists and throw( PathAlreadyExists, [ "${to}" ] );
752 3 50       150 CORE::symlink "${from}", "${to}"
753             or throw( 'Symlink from [_1] to [_2] failed: [_3]',
754             [ "${from}", "${to}", $OS_ERROR ] );
755 3         352 return "Symlinked ${from} to ${to}";
756             }
757              
758             sub thread_id () {
759 1 50   1 1 9 return exists $INC{ 'threads.pm' } ? threads->tid() : 0;
760             }
761              
762             sub throw (;@) {
763 165     165 1 3868 EXCEPTION_CLASS->throw( @_ );
764             }
765              
766             sub throw_on_error (;@) {
767 9     9 1 62 EXCEPTION_CLASS->throw_on_error( @_ );
768             }
769              
770             sub trim (;$$) {
771 2   100 2 1 2185 my $chs = $_[ 1 ] // " \t"; (my $v = $_[ 0 ] // q()) =~ s{ \A [$chs]+ }{}mx;
  2   50     61  
772              
773 2         13 chomp $v; $v =~ s{ [$chs]+ \z }{}mx; return $v;
  2         40  
  2         19  
774             }
775              
776             sub unescape_TT (;$$) {
777 1 50   1 1 4 my $v = defined $_[ 0 ] ? $_[ 0 ] : q();
778 1   50     6 my $fl = ($_[ 1 ] && $_[ 1 ]->[ 0 ]) || '<';
779 1   50     7 my $fr = ($_[ 1 ] && $_[ 1 ]->[ 1 ]) || '>';
780              
781 1         12 $v =~ s{ ${fl}\% }{[%}gmx; $v =~ s{ \%${fr} }{%]}gmx;
  1         8  
782              
783 1         6 return $v;
784             }
785              
786             sub untaint_cmdline (;$) {
787 59     59 1 6680 return untaint_string( UNTAINT_CMDLINE, $_[ 0 ] );
788             }
789              
790             sub untaint_identifier (;$) {
791 16     16 1 1414 return untaint_string( UNTAINT_IDENTIFIER, $_[ 0 ] );
792             }
793              
794             sub untaint_path (;$) {
795 1410     1410 1 11148 return untaint_string( UNTAINT_PATH, $_[ 0 ] );
796             }
797              
798             sub untaint_string ($;$) {
799 1485     1485 1 3465 my ($regex, $string) = @_;
800              
801 1485 100       3566 defined $string or return; length $string or return q();
  1472 50       3236  
802              
803 1472         9101 my ($untainted) = $string =~ $regex;
804              
805 1472 100 66     8201 (defined $untainted and $untainted eq $string)
806             or throw( Tainted, [ $string ], level => 3 );
807              
808 1468         40567 return $untainted;
809             }
810              
811             sub urandom (;$$) {
812 2   50 2 1 1127 my ($wanted, $opts) = @_; $wanted //= 64; $opts //= {};
  2   50     20  
  2         17  
813              
814 2 50       16 my $default = [ q(), 'dev', $OSNAME eq 'freebsd' ? 'random' : 'urandom' ];
815 2   33     21 my $io = io( $opts->{source} // $default )->block_size( $wanted );
816              
817 2         1799 my $red; $io->exists and $io->is_readable and $red = $io->read
818 2 50 33     12 and $red == $wanted and return ${ $io->buffer };
  2   33     4786  
      33        
819              
820 0         0 my $res = q(); while (length $res < $wanted) { $res .= $_pseudo_random->() }
  0         0  
  0         0  
821              
822 0         0 return substr $res, 0, $wanted;
823             }
824              
825             sub uuid (;$) {
826 0   0 0 1 0 return io( $_[ 0 ] // UUID_PATH )->chomp->getline;
827             }
828              
829             sub whiten ($) {
830 1     1 1 2327 my $v = unpack "b*", pop; my $pad = " \t" x 8;
  1         5  
831              
832 1         4 $v =~ tr{01}{ \t}; $v =~ s{ (.{9}) }{$1\n}gmx;
  1         27  
833              
834 1         9 return "${pad}\n${v}";
835             }
836              
837             sub zip (@) {
838 1     1 1 2670 my $p = @_ / 2; return @_[ map { $_, $_ + $p } 0 .. $p - 1 ];
  1         7  
  3         21  
839             }
840              
841             # Function composition
842             sub chain (;@) {
843 3     3 1 20 return (fold( sub { my ($x, $y) = @_; $x->$y } )->( shift ))->( @_ );
  3     1   12  
  1         11  
844             }
845              
846             sub compose (&;$) { # Was called build
847 1   100 1 1 6 my ($f, $g) = @_; $g //= sub { @_ }; return sub { $f->( $g->( @_ ) ) };
  3     3   13  
  3         2059  
  3         23  
  3         24  
848             }
849              
850             sub curry (&$;@) {
851 1     1 1 6 my ($f, @args) = @_; return sub { $f->( @args, @_ ) };
  1     1   1312  
  1         9  
852             }
853              
854             sub fold (&) {
855 3     3 1 10 my $f = shift;
856              
857             return sub (;$) {
858 3     3   10 my $x = shift;
859              
860             return sub (;@) {
861 3         9 my $y = $x; $y = $f->( $y, shift ) while (@_); return $y;
  3         18  
  3         37  
862             }
863 3         18 }
864 3         21 }
865              
866             sub Y (&) {
867 116     116 1 243 my $f = shift; return sub { $f->( Y( $f ) )->( @_ ) };
  116     114   508  
  114         322  
868             }
869              
870             sub factorial ($) {
871             return Y( sub (&) {
872 5     5   14 my $fac = shift;
873              
874             return sub ($) {
875 5         11 my $n = shift;
876              
877 5 100   1 1 25 return $n < 2 ? 1 : $n * $fac->( $n - 1 ) } } )->( @_ );
  5         36  
  1         950  
878             }
879              
880             sub fibonacci ($) {
881             return Y( sub {
882 109     109   277 my $fib = shift;
883              
884             return sub {
885 109         248 my $n = shift;
886              
887 109 100       654 return $n == 0 ? 0
    100          
888             : $n == 1 ? 1
889 109     1 1 437 : $fib->( $n - 1 ) + $fib->( $n - 2 ) } } )->( @_ );
  1         9  
890             }
891              
892             sub product (;@) {
893 4     4 1 18 return ((fold { $_[ 0 ] * $_[ 1 ] })->( 1 ))->( @_ );
  1     1   12  
894             }
895              
896             sub sum (;@) {
897 4     4 1 16 return ((fold { $_[ 0 ] + $_[ 1 ] })->( 0 ))->( @_ );
  1     1   10  
898             }
899              
900             1;
901              
902             __END__
903              
904             =pod
905              
906             =head1 Name
907              
908             Class::Usul::Functions - Globally accessible functions
909              
910             =head1 Synopsis
911              
912             package MyBaseClass;
913              
914             use Class::Usul::Functions qw( functions to import );
915              
916             =head1 Description
917              
918             Provides globally accessible functions
919              
920             =head1 Subroutines/Methods
921              
922             =head2 C<abs_path>
923              
924             $absolute_untainted_path = abs_path $some_path;
925              
926             Untaints path. Makes it an absolute path and returns it. Returns undef
927             otherwise. Traverses the filesystem
928              
929             =head2 C<app_prefix>
930              
931             $prefix = app_prefix __PACKAGE__;
932              
933             Takes a class name and returns it lower cased with B<::> changed to
934             B<_>, e.g. C<App::Munchies> becomes C<app_munchies>
935              
936             =head2 C<arg_list>
937              
938             $args = arg_list @rest;
939              
940             Returns a hash ref containing the passed parameter list. Enables
941             methods to be called with either a list or a hash ref as it's input
942             parameters
943              
944             =head2 C<assert>
945              
946             assert $ioc_object, $condition, $message;
947              
948             By default does nothing. Does not evaluate the passed parameters. The
949             L<assert|Classs::Usul::Constants/ASSERT> constant can be set via
950             an inherited class attribute to do something useful with whatever parameters
951             are passed to it
952              
953             =head2 C<assert_directory>
954              
955             $untainted_path = assert_directory $path_to_directory;
956              
957             Untaints directory path. Makes it an absolute path and returns it if it
958             exists. Returns undef otherwise
959              
960             =head2 C<base64_decode_ns>
961              
962             $decoded_value = base64_decode_ns $encoded_value;
963              
964             Decode a scalar value encode using L</base64_encode_ns>
965              
966             =head2 C<base64_encode_ns>
967              
968             $encoded_value = base64_encode_ns $encoded_value;
969              
970             Base 64 encode a scalar value using an output character set that preserves
971             the input values sort order (natural sort)
972              
973             =head2 C<bsonid>
974              
975             $bson_id = bsonid;
976              
977             Generate a new C<BSON> id. Returns a 24 character string of hex digits that
978             are reasonably unique across hosts and are in ascending order. Use this
979             to create unique ids for data streams like message queues and file feeds
980              
981             =head2 C<bsonid_time>
982              
983             $seconds_elapsed_since_the_epoch = bsonid_time $bson_id;
984              
985             Returns the time the C<BSON> id was generated as Unix time
986              
987             =head2 C<bson64id>
988              
989             $base64_encoded_extended_bson64_id = bson64id;
990              
991             Like L</bsonid> but better thread long running process support. A custom
992             Base64 encoding is used to reduce the id length
993              
994             =head2 C<bson64id_time>
995              
996             $seconds_elapsed_since_the_epoch = bson64id_time $bson64_id;
997              
998             Returns the time the C<BSON64> id was generated as Unix time
999              
1000             =head2 C<canonicalise>
1001              
1002             $untainted_canonpath = canonicalise $base, $relpath;
1003              
1004             Appends C<$relpath> to C<$base> using L<File::Spec::Functions>. The C<$base>
1005             and C<$relpath> arguments can be an array reference or a scalar. The return
1006             path is untainted and canonicalised
1007              
1008             =head2 C<class2appdir>
1009              
1010             $appdir = class2appdir __PACKAGE__;
1011              
1012             Returns lower cased L</distname>, e.g. C<App::Munchies> becomes
1013             C<app-munchies>
1014              
1015             =head2 C<classdir>
1016              
1017             $dir_path = classdir __PACKAGE__;
1018              
1019             Returns the path (directory) of a given class. Like L</classfile> but
1020             without the I<.pm> extension
1021              
1022             =head2 C<classfile>
1023              
1024             $file_path = classfile __PACKAGE__ ;
1025              
1026             Returns the path (file name plus extension) of a given class. Uses
1027             L<File::Spec> for portability, e.g. C<App::Munchies> becomes
1028             C<App/Munchies.pm>
1029              
1030             =head2 C<create_token>
1031              
1032             $random_hex = create_token $optional_seed;
1033              
1034             Create a random string token using L</digest>. If C<$seed> is defined then add
1035             that to the digest, otherwise add some random data provided by a call to
1036             L</urandom>. Returns a hexadecimal string
1037              
1038             =head2 C<create_token64>
1039              
1040             $random_base64 = create_token64 $optional_seed;
1041              
1042             Like L</create_token> but the output is C<base64> encoded
1043              
1044             =head2 C<cwdp>
1045              
1046             $current_working_directory = cwdp;
1047              
1048             Returns the current working directory, physical location
1049              
1050             =head2 C<dash2under>
1051              
1052             $string_with_underscores = dash2under 'a-string-with-dashes';
1053              
1054             Substitutes underscores for dashes
1055              
1056             =head2 C<data_dumper>
1057              
1058             data_dumper $thing;
1059              
1060             Uses L<Data::Printer> to dump C<$thing> in colour to I<stderr>
1061              
1062             =head2 C<digest>
1063              
1064             $digest_object = digest $seed;
1065              
1066             Creates an instance of the first available L<Digest> class and adds the seed.
1067             The constant C<DIGEST_ALGORITHMS> is consulted for the list of algorithms to
1068             search for. Returns the digest object reference
1069              
1070             =head2 C<distname>
1071              
1072             $distname = distname __PACKAGE__;
1073              
1074             Takes a class name and returns it with B<::> changed to
1075             B<->, e.g. C<App::Munchies> becomes C<App-Munchies>
1076              
1077             =head2 C<elapsed>
1078              
1079             $elapsed_seconds = elapsed;
1080              
1081             Returns the number of seconds elapsed since the process started
1082              
1083             =head2 C<emit>
1084              
1085             emit @lines_of_text;
1086              
1087             Prints to I<STDOUT> the lines of text passed to it. Lines are C<chomp>ed
1088             and then have newlines appended. Throws on IO errors
1089              
1090             =head2 C<emit_err>
1091              
1092             emit_err @lines_of_text;
1093              
1094             Like L</emit> but output to C<STDERR>
1095              
1096             =head2 C<emit_to>
1097              
1098             emit_to $filehandle, @lines_of_text;
1099              
1100             Prints to the specified file handle
1101              
1102             =head2 C<ensure_class_loaded>
1103              
1104             ensure_class_loaded $some_class, $options_ref;
1105              
1106             Require the requested class, throw an error if it doesn't load
1107              
1108             =head2 C<env_prefix>
1109              
1110             $prefix = env_prefix $class;
1111              
1112             Returns upper cased C<app_prefix>. Suitable as prefix for environment
1113             variables
1114              
1115             =head2 C<escape_TT>
1116              
1117             $text = escape_TT '[% some_stash_key %]';
1118              
1119             The left square bracket causes problems in some contexts. Substitute a
1120             less than symbol instead. Also replaces the right square bracket with
1121             greater than for balance. L<Template::Toolkit> will work with these
1122             sequences too, so unescaping isn't absolutely necessary
1123              
1124             =head2 C<exception>
1125              
1126             $e = exception $error;
1127              
1128             Expose the C<catch> method in the exception
1129             class L<Class::Usul::Exception>. Returns a new error object
1130              
1131             =head2 C<find_apphome>
1132              
1133             $directory_path = find_apphome $appclass, $homedir, $extns
1134              
1135             Returns the path to the applications home directory. Searches the following:
1136              
1137             # 0. Pass the directory in (short circuit the search)
1138             # 1a. Environment variable - for application directory
1139             # 1b. Environment variable - for config file
1140             # 2a. Users XDG_DATA_HOME env variable or XDG default share directory
1141             # 2b. Users home directory - dot file containing shell env variable
1142             # 2c. Users home directory - dot directory is apphome
1143             # 3. Well known path containing shell env file
1144             # 4. Default install prefix
1145             # 5a. Config file found in @INC - underscore as separator
1146             # 5b. Config file found in @INC - dash as separator
1147             # 6. Default to /tmp
1148              
1149             =head2 C<find_source>
1150              
1151             $path = find_source $module_name;
1152              
1153             Find absolute path to the source code for the given module
1154              
1155             =head2 C<first_char>
1156              
1157             $single_char = first_char $some_string;
1158              
1159             Returns the first character of C<$string>
1160              
1161             =head2 C<fqdn>
1162              
1163             $domain_name = fqdn $hostname;
1164              
1165             Call C<gethostbyname> on the supplied hostname whist defaults to this host
1166              
1167             =head2 C<fullname>
1168              
1169             $fullname = fullname;
1170              
1171             Returns the untainted first sub field from the gecos attribute of the
1172             object returned by a call to L</get_user>. Returns the null string if
1173             the gecos attribute value is false
1174              
1175             =head2 C<get_cfgfiles>
1176              
1177             $paths = get_cfgfiles $appclass, $dirs, $extns
1178              
1179             Returns an array ref of configurations file paths for the application
1180              
1181             =head2 C<get_user>
1182              
1183             $user_object = get_user $optional_uid_or_name;
1184              
1185             Returns the user object from a call to either C<getpwuid> or C<getpwnam>
1186             depending on whether an integer or a string was passed. The L<User::pwent>
1187             package is loaded so objects are returned. On MSWin32 systems returns an
1188             instance of L<Class::Null>. Defaults to the current uid but will lookup the
1189             supplied uid if provided
1190              
1191             =head2 C<hex2str>
1192              
1193             $string = hex2str $pairs_of_hex_digits;
1194              
1195             Converts the pairs of hex digits into a string of characters
1196              
1197             =head2 C<home2appldir>
1198              
1199             $appldir = home2appldir $home_dir;
1200              
1201             Strips the trailing C<lib/my_package> from the supplied directory path
1202              
1203             =head2 C<io>
1204              
1205             $io_object_ref = io $path_to_file_or_directory;
1206              
1207             Returns a L<File::DataClass::IO> object reference
1208              
1209             =head2 C<is_arrayref>
1210              
1211             $bool = is_arrayref $scalar_variable
1212              
1213             Tests to see if the scalar variable is an array ref
1214              
1215             =head2 C<is_coderef>
1216              
1217             $bool = is_coderef $scalar_variable
1218              
1219             Tests to see if the scalar variable is a code ref
1220              
1221             =head2 C<is_hashref>
1222              
1223             $bool = is_hashref $scalar_variable
1224              
1225             Tests to see if the scalar variable is a hash ref
1226              
1227             =head2 C<is_member>
1228              
1229             $bool = is_member 'test_value', qw( a_value test_value b_value );
1230              
1231             Tests to see if the first parameter is present in the list of
1232             remaining parameters
1233              
1234             =head2 C<is_ntfs>
1235              
1236             $bool = is_ntfs;
1237              
1238             Returns true if L</is_win32> is true or the C<$OSNAME> is
1239             L<cygwin|File::DataClass::Constants/CYGWIN>
1240              
1241             =head2 C<is_win32>
1242              
1243             $bool = is_win32;
1244              
1245             Returns true if the C<$OSNAME> is
1246             L<unfortunate|File::DataClass::Constants/MSOFT>
1247              
1248             =head2 C<list_attr_of>
1249              
1250             $attribute_list = list_attr_of $object_ref, @exception_list;
1251              
1252             Lists the attributes of the object reference, including defining class name,
1253             documentation, and current value
1254              
1255             =head2 C<loginid>
1256              
1257             $loginid = loginid;
1258              
1259             Returns the untainted name attribute of the object returned by a call
1260             to L</get_user> or 'unknown' if the name attribute value is false
1261              
1262             =head2 C<logname>
1263              
1264             $logname = logname;
1265              
1266             Deprecated. Returns untainted the first true value returned by; the environment
1267             variable C<USER>, the environment variable C<LOGNAME>, and the function
1268             L</loginid>
1269              
1270             =head2 C<merge_attributes>
1271              
1272             $dest = merge_attributes $dest, $src, $defaults, $attr_list_ref;
1273              
1274             Merges attribute hashes. The C<$dest> hash is updated and returned. The
1275             C<$dest> hash values take precedence over the C<$src> hash values which
1276             take precedence over the C<$defaults> hash values. The C<$src> hash
1277             may be an object in which case its accessor methods are called
1278              
1279             =head2 C<nonblocking_write_pipe_pair>
1280              
1281             $array_ref = non_blocking_write_pipe;
1282              
1283             Returns a pair of file handles, read then write. The write file handle is
1284             non blocking, binmode is set on both
1285              
1286             =head2 C<my_prefix>
1287              
1288             $prefix = my_prefix $PROGRAM_NAME;
1289              
1290             Takes the basename of the supplied argument and returns the first _
1291             (underscore) separated field. Supplies basename with
1292             L<extensions|Class::Usul::Constants/PERL_EXTNS>
1293              
1294             =head2 C<ns_environment>
1295              
1296             $value = ns_environment $class, $key, $value;
1297              
1298             An accessor / mutator for the environment variables prefixed by the supplied
1299             class name. Providing a value is optional, always returns the current value
1300              
1301             =head2 C<pad>
1302              
1303             $padded_str = pad $unpadded_str, $wanted_length, $pad_char, $direction;
1304              
1305             Pad a string out to the wanted length with the C<$pad_char> which
1306             defaults to a space. Direction can be; I<both>, I<left>, or I<right>
1307             and defaults to I<right>
1308              
1309             =head2 C<prefix2class>
1310              
1311             $class = prefix2class $PROGRAM_NAME;
1312              
1313             Calls L</my_prefix> with the supplied argument, splits the result on dash,
1314             C<ucfirst>s the list and then C<join>s that with I<::>
1315              
1316             =head2 C<socket_pair>
1317              
1318             ($reader, $writer) = @{ socket_pair };
1319              
1320             Return a C<socketpair> reader then writer. The writer has been closed on the
1321             reader and the reader has been closed on the writer
1322              
1323             =head2 C<split_on__>
1324              
1325             $field = split_on__ $string, $field_no;
1326              
1327             Splits string by _ (underscore) and returns the requested field. Defaults
1328             to field zero
1329              
1330             =head2 C<split_on_dash>
1331              
1332             $field = split_on_dash $string, $field_no;
1333              
1334             Splits string by - (dash) and returns the requested field. Defaults
1335             to field zero
1336              
1337             =head2 C<squeeze>
1338              
1339             $string = squeeze $string_containing_muliple_spacesd;
1340              
1341             Squeezes multiple whitespace down to a single space
1342              
1343             =head2 C<strip_leader>
1344              
1345             $stripped = strip_leader 'my_program: Error message';
1346              
1347             Strips the leading "program_name: whitespace" from the passed argument
1348              
1349             =head2 C<sub_name>
1350              
1351             $sub_name = sub_name $level;
1352              
1353             Returns the name of the method that calls it
1354              
1355             =head2 C<symlink>
1356              
1357             $message = symlink $from, $to, $base;
1358              
1359             It creates a symlink. If either C<$from> or C<$to> is a relative path
1360             then C<$base> is prepended to make it absolute. Returns a message
1361             indicating success or throws an exception on failure
1362              
1363             =head2 C<thread_id>
1364              
1365             $tid = thread_id;
1366              
1367             Returns the id of this thread. Returns zero if threads are not loaded
1368              
1369             =head2 C<throw>
1370              
1371             throw 'error_message', [ 'error_arg' ];
1372              
1373             Expose L<Class::Usul::Exception/throw>. L<Class::Usul::Constants> has a
1374             class attribute I<Exception_Class> which can be set change the class
1375             of the thrown exception
1376              
1377             =head2 C<throw_on_error>
1378              
1379             throw_on_error @args;
1380              
1381             Passes it's optional arguments to L</exception> and if an exception object is
1382             returned it throws it. Returns undefined otherwise. If no arguments are
1383             passed L</exception> will use the value of the global C<$EVAL_ERROR>
1384              
1385             =head2 C<trim>
1386              
1387             $trimmed_string = trim $string_with_leading_and_trailing_whitespace;
1388              
1389             Remove leading and trailing whitespace including trailing newlines. Takes
1390             an additional string used as the character class to remove. Defaults to
1391             space and tab
1392              
1393             =head2 C<unescape_TT>
1394              
1395             $text = unescape_TT '<% some_stash_key %>';
1396              
1397             Do the reverse of C<escape_TT>
1398              
1399             =head2 C<untaint_cmdline>
1400              
1401             $untainted_cmdline = untaint_cmdline $maybe_tainted_cmdline;
1402              
1403             Returns an untainted command line string. Calls L</untaint_string> with the
1404             matching regex from L<Class::Usul::Constants>
1405              
1406             =head2 C<untaint_identifier>
1407              
1408             $untainted_identifier = untaint_identifier $maybe_tainted_identifier;
1409              
1410             Returns an untainted identifier string. Calls L</untaint_string> with the
1411             matching regex from L<Class::Usul::Constants>
1412              
1413             =head2 C<untaint_path>
1414              
1415             $untainted_path = untaint_path $maybe_tainted_path;
1416              
1417             Returns an untainted file path. Calls L</untaint_string> with the
1418             matching regex from L<Class::Usul::Constants>
1419              
1420             =head2 C<untaint_string>
1421              
1422             $untainted_string = untaint_string $regex, $maybe_tainted_string;
1423              
1424             Returns an untainted string or throws
1425              
1426             =head2 C<urandom>
1427              
1428             $bytes = urandom $optional_length, $optional_provider;
1429              
1430             Returns random bytes. Length defaults to 64. The provider defaults to
1431             F</dev/urandom> and can be any type accepted by L</io>. If the provider exists
1432             and is readable, length bytes are read from it and returned. Otherwise some
1433             bytes from the second best generator are returned
1434              
1435             =head2 C<uuid>
1436              
1437             $uuid = uuid $optional_uuid_proc_filesystem_path;
1438              
1439             Return the contents of F</proc/sys/kernel/random/uuid>
1440              
1441             =head2 C<whiten>
1442              
1443             $encoded = whiten 'plain_text_to_be_obfuscated';
1444              
1445             Lifted from L<Acme::Bleach> this function encodes the passed scalar as spaces,
1446             tabs, and newlines. The L<encrypt> and L<decrypt> functions take a seed
1447             attribute in their options hash reference. A whitened line of Perl code
1448             would be a suitable value
1449              
1450             =head2 C<zip>
1451              
1452             %hash = zip @list_of_keys, @list_of_values;
1453              
1454             Zips two list of equal size together to form a hash
1455              
1456             =head2 C<chain>
1457              
1458             $result = chain $sub1, $sub2, $sub3
1459              
1460             Call each sub in turn passing the returned value as the first argument to
1461             the next function call
1462              
1463             =head2 C<compose>
1464              
1465             $code_ref = compose { }, $code_ref;
1466              
1467             Returns a code reference which when called returns the result of calling the
1468             block passing in the result of calling the optional code reference. Delays the
1469             calling of the input code reference until the output code reference is called
1470              
1471             =head2 C<curry>
1472              
1473             $curried_code_ref = curry $code_ref, @args;
1474             $result = $curried_code_ref->( @more_args );
1475              
1476             Returns a subroutine reference which when called, calls and returns the
1477             initial code reference passing in the original argument list and the
1478             arguments from the curried call. Must be called with a code reference and
1479             at least one argument
1480              
1481             =head2 C<fold>
1482              
1483             *sum = fold { $a + $b } 0;
1484              
1485             Classic reduce function with optional base value
1486              
1487             =head2 C<Y>
1488              
1489             $code_ref = Y( $code_ref );
1490              
1491             The Y-combinator function
1492              
1493             =head2 C<factorial>
1494              
1495             $result = factorial $n;
1496              
1497             Calculates the factorial for the supplied integer
1498              
1499             =head2 C<fibonacci>
1500              
1501             $result = fibonacci $n;
1502              
1503             Calculates the Fibonacci number for the supplied integer
1504              
1505             =head2 C<product>
1506              
1507             $product = product 1, 2, 3, 4;
1508              
1509             Returns the product of the list of numbers
1510              
1511             =head2 C<sum>
1512              
1513             $total = sum 1, 2, 3, 4;
1514              
1515             Adds the list of values
1516              
1517             =head1 Diagnostics
1518              
1519             None
1520              
1521             =head1 Configuration and Environment
1522              
1523             None
1524              
1525             =head1 Dependencies
1526              
1527             =over 3
1528              
1529             =item L<Class::Usul::Constants>
1530              
1531             =item L<Data::Printer>
1532              
1533             =item L<Digest>
1534              
1535             =item L<File::HomeDir>
1536              
1537             =item L<List::Util>
1538              
1539             =back
1540              
1541             =head1 Incompatibilities
1542              
1543             The L</home2appldir> method is dependent on the installation path
1544             containing a B<lib>
1545              
1546             The L</uuid> method with only work on a OS with a F</proc> filesystem
1547              
1548             =head1 Bugs and Limitations
1549              
1550             There are no known bugs in this module.
1551             Please report problems to the address below.
1552             Patches are welcome
1553              
1554             =head1 Author
1555              
1556             Peter Flanigan, C<< <pjfl@cpan.org> >>
1557              
1558             =head1 License and Copyright
1559              
1560             Copyright (c) 2017 Peter Flanigan. All rights reserved
1561              
1562             This program is free software; you can redistribute it and/or modify it
1563             under the same terms as Perl itself. See L<perlartistic>
1564              
1565             This program is distributed in the hope that it will be useful,
1566             but WITHOUT WARRANTY; without even the implied warranty of
1567             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
1568              
1569             =cut
1570              
1571             # Local Variables:
1572             # mode: perl
1573             # tab-width: 3
1574             # End: