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   470692 use strict;
  28         60  
  28         701  
4 28     28   145 use warnings;
  28         77  
  28         601  
5 28     28   905 use parent 'Exporter::Tiny';
  28         536  
  28         125  
6              
7 28     28   18678 use Class::Inspector;
  28         70193  
  28         771  
8 28     28   10347 use Class::Null;
  28         8638  
  28         874  
9 28         181 use Class::Usul::Constants qw( ASSERT DEFAULT_CONFHOME DEFAULT_ENVDIR
10             DIGEST_ALGORITHMS EXCEPTION_CLASS
11             PERL_EXTNS PREFIX UNTAINT_CMDLINE
12 28     28   1069 UNTAINT_IDENTIFIER UNTAINT_PATH UUID_PATH );
  28         60  
13 28     28   29324 use Cwd qw( );
  28         55  
  28         3175  
14             use Data::Printer alias => q(_data_dumper), colored => 1, indent => 3,
15 0         0 filters => { 'DateTime' => sub { $_[ 0 ].q() },
16 8         14667 '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   15444 'Type::Tiny::Union' => sub { $_[ 0 ]->display_name }, };
  28         645690  
  28         570  
  0         0  
21 28     28   49041 use Digest qw( );
  28         11659  
  28         608  
22 28     28   148 use Digest::MD5 qw( md5 );
  28         72  
  28         1414  
23 28     28   174 use English qw( -no_match_vars );
  28         96  
  28         216  
24 28     28   8716 use Fcntl qw( F_SETFL O_NONBLOCK );
  28         96  
  28         1133  
25 28     28   160 use File::Basename qw( basename dirname );
  28         56  
  28         1443  
26 28     28   11789 use File::DataClass::Functions qw( supported_extensions );
  28         240912  
  28         1502  
27 28     28   14258 use File::DataClass::IO qw( );
  28         1738989  
  28         1013  
28 28     28   272 use File::HomeDir qw( );
  28         84  
  28         704  
29 28     28   148 use File::Spec::Functions qw( canonpath catdir catfile curdir );
  28         57  
  28         1713  
30 28     28   176 use List::Util qw( first );
  28         55  
  28         1385  
31 28     28   164 use Module::Runtime qw( is_module_name require_module );
  28         61  
  28         248  
32 28     28   1533 use Scalar::Util qw( blessed openhandle );
  28         61  
  28         1178  
33 28     28   14514 use Socket qw( AF_UNIX SOCK_STREAM PF_UNSPEC );
  28         78663  
  28         3739  
34 28     28   249 use Symbol;
  28         59  
  28         1415  
35 28     28   160 use Sys::Hostname qw( hostname );
  28         57  
  28         1376  
36 28         281 use Unexpected::Functions qw( is_class_loaded PathAlreadyExists
37 28     28   150 PathNotFound Tainted Unspecified );
  28         59  
38 28     28   27399 use User::pwent;
  28         82377  
  28         114  
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 28     28   16712 my $bson_id_count : shared = 0;
  28         28925  
  28         156596  
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   5489 my ($class, $name, $value, $globals) = @_;
220              
221             exists $EXPORT_REFS{ $name }
222 4 50       29 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 4058 my $v = shift; (defined $v and length $v) or return $v;
  1094         4528  
230              
231 962 50 33     1858 is_ntfs() and not -e $v and return untaint_path( $v ); # Hate
232              
233 962         1996 $v = Cwd::abs_path( untaint_path( $v ) );
234              
235 962 50 33     2791 is_win32() and defined $v and $v =~ s{ / }{\\}gmx; # More hate
236              
237 962         3479 return $v;
238             }
239              
240             sub app_prefix ($) {
241 97   100 97 1 526 (my $v = lc ($_[ 0 ] // q())) =~ s{ :: }{_}gmx; return $v;
  97         301  
242             }
243              
244             sub arg_list (;@) {
245 382 100 100 382 1 3986 return $_[ 0 ] && ref $_[ 0 ] eq 'HASH' ? { %{ $_[ 0 ] } }
  285 100       1749  
246             : $_[ 0 ] ? { @_ }
247             : {};
248             }
249              
250             sub assert_directory ($) {
251 222     222 1 1559 my $v = abs_path( $_[ 0 ] );
252              
253 222 100 100     1051 defined $v and length $v and -d "${v}" and return $v;
      100        
254              
255 221         952 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         5  
  2         15  
260              
261 2         7 my $index = $_index64->(); my $j = 0; my $k = 0;
  2         4  
  2         3  
262              
263 2         4 my $len = length $x; my $pad = 64; my @y = ();
  2         3  
  2         5  
264              
265             ROUND: {
266 2         3 while ($j < $len) {
  2         5  
267 10         19 my @c = (); my $i = 0;
  10         15  
268              
269 10         19 while ($i < 4) {
270 40         55 my $uc = $index->[ ord $x[ $j++ ] ];
271              
272 40 50       86 $uc ne 'XX' and $c[ $i++ ] = 0 + $uc; $j == $len or next;
  40 100       104  
273              
274 2 50       10 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     49 ($c[ 0 ] == $pad || $c[ 1 ] == $pad) and last;
282 10         20 $y[ $k++ ] = ( $c[ 0 ] << 2) | (($c[ 1 ] & 0x30) >> 4);
283 10 50       21 $c[ 2 ] == $pad and last;
284 10         21 $y[ $k++ ] = (($c[ 1 ] & 0x0F) << 4) | (($c[ 2 ] & 0x3C) >> 2);
285 10 100       21 $c[ 3 ] == $pad and last;
286 9         24 $y[ $k++ ] = (($c[ 2 ] & 0x03) << 6) | $c[ 3 ];
287             }
288             }
289              
290 2         6 return join q(), map { chr $_ } @y;
  29         74  
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         12  
295              
296 2         7 my $basis = $_base64_char_set->(); my $len = length $x; my @y = ();
  2         6  
  2         5  
297              
298 2         8 for (my $i = 0, my $j = 0; $len > 0; $len -= 3, $i += 3) {
299 10 50       16 my $c1 = ord $x[ $i ]; my $c2 = $len > 1 ? ord $x[ $i + 1 ] : 0;
  10         20  
300              
301 10         20 $y[ $j++ ] = $basis->[ $c1 >> 2 ];
302 10         21 $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         4 $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         20 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 4 return unpack 'N', substr hex2str( $_[ 0 ] ), 0, 4;
329             }
330              
331             sub bson64id (;$) {
332 1     1 1 337 return base64_encode_ns( $_bsonid->( 2 ) );
333             }
334              
335             sub bson64id_time ($) {
336 1     1 1 3 return unpack 'N', substr base64_decode_ns( $_[ 0 ] ), 2, 4;
337             }
338              
339             sub canonicalise ($;$) {
340 58     58 1 23488 my ($base, $relpath) = @_;
341              
342 58 50       163 $base = is_arrayref( $base ) ? catdir( @{ $base } ) : $base;
  0         0  
343 58 100       2720 $relpath or return canonpath( untaint_path( $base ) );
344              
345 33 50       79 my @relpath = is_arrayref( $relpath ) ? @{ $relpath } : $relpath;
  0         0  
346 33         152 my $path = canonpath( untaint_path( catdir( $base, @relpath ) ) );
347              
348 33 50       863 -d $path and return $path;
349              
350 33         175 return canonpath( untaint_path( catfile( $base, @relpath ) ) );
351             }
352              
353             sub class2appdir ($) {
354 51     51 1 973 return lc distname( $_[ 0 ] );
355             }
356              
357             sub classdir ($) {
358 23   50 23 1 242 return catdir( split m{ :: }mx, $_[ 0 ] // q() );
359             }
360              
361             sub classfile ($) {
362 56     56 1 686 return catfile( split m{ :: }mx, $_[ 0 ].'.pm' );
363             }
364              
365             sub create_token (;$) {
366 10   66 10 1 48 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 338 return abs_path( curdir );
375             }
376              
377             sub dash2under (;$) {
378 4   50 4 1 19 (my $v = $_[ 0 ] // q()) =~ s{ [\-] }{_}gmx; return $v;
  4         17  
379             }
380              
381             sub data_dumper (;@) {
382 2     2 1 10 _data_dumper( @_ ); return 1;
  2         12967  
383             }
384              
385             sub digest ($) {
386 10     10 1 344 my $seed = shift; my ($candidate, $digest);
  10         19  
387              
388 10 100       29 if ($digest_cache) { $digest = Digest->new( $digest_cache ) }
  7         41  
389             else {
390 3         18 for (DIGEST_ALGORITHMS) {
391 3 50       8 $candidate = $_; $digest = eval { Digest->new( $candidate ) } and last;
  3         11  
  3         34  
392             }
393              
394 3 50       9050 $digest or throw( 'Digest algorithm not found' );
395 3         8 $digest_cache = $candidate;
396             }
397              
398 10         287 $digest->add( $seed );
399              
400 10         135 return $digest;
401             }
402              
403             sub distname ($) {
404 52   50 52 1 265 (my $v = $_[ 0 ] // q()) =~ s{ :: }{-}gmx; return $v;
  52         246  
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 7 return time - $BASETIME;
420             }
421              
422             sub emit (;@) {
423 10   100 10 1 1358 my @args = @_; $args[ 0 ] //= q(); chomp( @args );
  10         32  
  10         25  
424              
425 10         39 local ($OFS, $ORS) = ("\n", "\n");
426              
427 10 50       57 return openhandle *STDOUT ? emit_to( *STDOUT, @args ) : undef;
428             }
429              
430             sub emit_err (;@) {
431 3   50 3 1 2069 my @args = @_; $args[ 0 ] //= q(); chomp( @args );
  3         13  
  3         9  
432              
433 3         14 local ($OFS, $ORS) = ("\n", "\n");
434              
435 3 50       24 return openhandle *STDERR ? emit_to( *STDERR, @args ) : undef;
436             }
437              
438             sub emit_to ($;@) {
439 29     29 1 129 my ($handle, @args) = @_; local $OS_ERROR;
  29         258  
440              
441 29   33     114 return (print {$handle} @args or throw( 'IO error: [_1]', [ $OS_ERROR ] ));
442             }
443              
444             sub ensure_class_loaded ($;$) {
445 17   50 17 1 1127 my ($class, $opts) = @_; $opts //= {};
  17         107  
446              
447 17 50       53 $class or throw( Unspecified, [ 'class name' ], level => 2 );
448              
449 17 50       73 is_module_name( $class )
450             or throw( 'String [_1] invalid classname', [ $class ], level => 2 );
451              
452 17 100 66     339 not $opts->{ignore_loaded} and is_class_loaded( $class ) and return 1;
453              
454 9         357 eval { require_module( $class ) }; throw_on_error( { level => 3 } );
  9         40  
  9         869618  
455              
456 8 50       428 is_class_loaded( $class )
457             or throw( 'Class [_1] loaded but package undefined',
458             [ $class ], level => 2 );
459              
460 8         320 return 1;
461             }
462              
463             sub env_prefix ($) {
464 51     51 1 616 return uc app_prefix( $_[ 0 ] );
465             }
466              
467             sub escape_TT (;$$) {
468 1 50   1 1 5 my $v = defined $_[ 0 ] ? $_[ 0 ] : q();
469 1   50     11 my $fl = ($_[ 1 ] && $_[ 1 ]->[ 0 ]) || '<';
470 1   50     8 my $fr = ($_[ 1 ] && $_[ 1 ]->[ 1 ]) || '>';
471              
472 1         8 $v =~ s{ \[\% }{${fl}%}gmx; $v =~ s{ \%\] }{%${fr}}gmx;
  1         5  
473              
474 1         6 return $v;
475             }
476              
477             sub exception (;@) {
478 8     8 1 16761 return EXCEPTION_CLASS->caught( @_ );
479             }
480              
481             sub find_apphome ($;$$) {
482 22     22 1 68 my ($appclass, $default, $extns) = @_; my $path;
  22         42  
483              
484             # 0. Pass the directory in (short circuit the search)
485 22 50       121 $path = assert_directory $default and return $path;
486              
487 22         98 my $app_pref = app_prefix $appclass;
488 22         97 my $appdir = class2appdir $appclass;
489 22         84 my $classdir = classdir $appclass;
490 22         86 my $env_pref = env_prefix $appclass;
491 22         230 my $my_home = File::HomeDir->my_home;
492              
493             # 1a. Environment variable - for application directory
494 22 50       1157 $path = assert_directory $ENV{ "${env_pref}_HOME" } and return $path;
495             # 1b. Environment variable - for config file
496 22 50       118 $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     205 $path = $ENV{ 'XDG_DATA_HOME' } // catdir( $my_home, '.local', 'share' );
499 22 50       112 $path = assert_directory catdir( $path, $appdir ) and return $path;
500             # 2b. Users home directory - dot file containing shell env variable
501 22 50       105 $path = $_get_file_var->( $my_home, $app_pref, $classdir ) and return $path;
502 22 50       80 $path = $_get_file_var->( $my_home, $appdir, $classdir ) and return $path;
503             # 2c. Users home directory - dot directory is apphome
504 22         145 $path = catdir( $my_home, ".${app_pref}" );
505 22 50       66 $path = assert_directory $path and return $path;
506 22         111 $path = catdir( $my_home, ".${appdir}" );
507 22 50       84 $path = assert_directory $path and return $path;
508             # 3. Well known path containing shell env file
509 22 50       92 $path = $_get_known_file_var->( $appdir, $classdir ) and return $path;
510             # 4. Default install prefix
511 22         48 $path = catdir( @{ PREFIX() }, $appdir, 'default', 'lib', $classdir );
  22         115  
512 22 50       82 $path = assert_directory $path and return $path;
513             # 5a. Config file found in @INC - underscore as separator
514 22 50       104 $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       81 $path = $_find_cfg_in_inc->( $classdir, $appdir, $extns ) and return $path;
517             # 6. Default to /tmp
518 22         132 return untaint_path( DEFAULT_CONFHOME );
519             }
520              
521             sub find_source ($) {
522 55     55 1 874 my $class = shift; my $file = classfile( $class ); my $path;
  55         248  
  55         124  
523              
524 55         199 for (@INC) {
525 165 100 66     844 $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 83 my ($appclass, $dirs, $extns) = @_;
549              
550 22   33     86 $appclass // throw( Unspecified, [ 'application class' ], level => 2 );
551 22 50 50     78 is_arrayref( $dirs ) or $dirs = [ $dirs || curdir ];
552              
553 22         97 my $app_pref = app_prefix $appclass;
554 22         98 my $appdir = class2appdir $appclass;
555 22         83 my $env_pref = env_prefix $appclass;
556 22   50     171 my $suffix = $ENV{ "${env_pref}_CONFIG_LOCAL_SUFFIX" } // '_local';
557 22         64 my @paths = ();
558              
559 22         45 for my $dir (@{ $dirs }) {
  22         83  
560 22   50     50 for my $extn (@{ $extns // [ supported_extensions() ] }) {
  22         144  
561 26         18081 for my $path (map { $_catpath->( $dir, $_ ) } "${app_pref}${extn}",
  104         235  
562             "${appdir}${extn}", "${app_pref}${suffix}${extn}",
563             "${appdir}${suffix}${extn}") {
564 104 50       1067 -f $path and push @paths, $path;
565             }
566             }
567             }
568              
569 22         146 return \@paths;
570             }
571              
572             sub get_user (;$) {
573 6 50   6 1 16 my $user = shift; is_win32() and return Class::Null->new;
  6         18  
574              
575 6 50 33     22 defined $user and $user !~ m{ \A \d+ \z }mx and return getpwnam( $user );
576              
577 6   33     39 return getpwuid( $user // $UID );
578             }
579              
580             sub hex2str (;$) {
581 2   50 2 1 827 my @a = split m{}mx, shift // q(); my $str = q();
  2         6  
582              
583 2         11 while (my ($x, $y) = splice @a, 0, 2) { $str .= pack 'C', hex "${x}${y}" }
  13         46  
584              
585 2         16 return $str;
586             }
587              
588             sub home2appldir ($) {
589 7 100   7 1 34 $_[ 0 ] or return; my $dir = io( $_[ 0 ] );
  6         252  
590              
591 6   100     16345 $dir = $dir->parent while ($dir ne $dir->parent and $dir !~ m{ lib \z }mx);
592              
593 6 100       13924 return $dir ne $dir->parent ? $dir->parent : undef;
594             }
595              
596             sub io (;@) {
597 211     211 1 32063 return File::DataClass::IO->new( @_ );
598             }
599              
600             sub is_arrayref (;$) {
601 2607 100 100 2607 1 27696 return $_[ 0 ] && ref $_[ 0 ] eq 'ARRAY' ? 1 : 0;
602             }
603              
604             sub is_coderef (;$) {
605 58 100 100 58 1 762 return $_[ 0 ] && ref $_[ 0 ] eq 'CODE' ? 1 : 0;
606             }
607              
608             sub is_hashref (;$) {
609 437 100 100 437 1 4261 return $_[ 0 ] && ref $_[ 0 ] eq 'HASH' ? 1 : 0;
610             }
611              
612             sub is_member (;@) {
613 502 50   502 1 16154 my ($candidate, @args) = @_; $candidate or return;
  502         1661  
614              
615 502 100       1289 is_arrayref $args[ 0 ] and @args = @{ $args[ 0 ] };
  424         1466  
616              
617 502 100   1454   3586 return (first { $_ eq $candidate } @args) ? 1 : 0;
  1454         5626  
618             }
619              
620             sub is_ntfs () {
621 962 50 33 962 1 1779 return is_win32() || lc $OSNAME eq 'cygwin' ? 1 : 0;
622             }
623              
624             sub is_win32 () {
625 2501 50   2501 1 19883 return lc $OSNAME eq 'mswin32' ? 1 : 0;
626             }
627              
628             sub list_attr_of ($;@) {
629 1     1 1 1233 my ($obj, @except) = @_; my $class = blessed $obj;
  1         6  
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         5474 return map { my $attr = $_->[0]; [ @{ $_ }, $obj->$attr ] }
  45         69  
  45         608  
636 45         154 map { [ $_->[1], $_->[0], $_get_pod_content_for_attr->( @{ $_ } ) ] }
  45         178  
637 50 100       129 grep { $_->[0] ne 'Moo::Object' and not is_member $_->[1], @except }
638 50         1455 map { m{ \A (.+) \:\: ([^:]+) \z }mx; [ $1, $2 ] }
  50         116  
639 1         4 @{ Class::Inspector->methods( $class, 'full', 'public' ) };
  1         22  
640             }
641              
642             sub loginid (;$) {
643 6   50 6 1 25 return untaint_cmdline( get_user( $_[ 0 ] )->name || 'unknown' );
644             }
645              
646             sub logname (;$) { # Deprecated use loginid
647 5   33 5 1 41 return untaint_cmdline( $ENV{USER} || $ENV{LOGNAME} || loginid( $_[ 0 ] ) );
648             }
649              
650             sub merge_attributes ($@) {
651 48     48 1 166 my ($dest, @args) = @_;
652              
653 48 50       233 my $attr = is_arrayref( $args[ -1 ] ) ? pop @args : [];
654              
655 48   66     152 for my $k (grep { not exists $dest->{ $_ } or not defined $dest->{ $_ } }
  177         637  
656 48         138 @{ $attr }) {
657 173         317 my $i = 0; my $v;
  173         270  
658              
659 173   100     805 while (not defined $v and defined( my $src = $args[ $i++ ] )) {
660 203         581 my $class = blessed $src;
661              
662 203 100       2683 $v = $class ? ($src->can( $k ) ? $src->$k() : undef) : $src->{ $k };
    100          
663             }
664              
665 173 100       13013 defined $v and $dest->{ $k } = $v;
666             }
667              
668 48         153 return $dest;
669             }
670              
671             sub my_prefix (;$) {
672 2   50 2 1 12 return split_on__( basename( $_[ 0 ] // q(), PERL_EXTNS ) );
673             }
674              
675             sub nonblocking_write_pipe_pair () {
676 384 50   384 1 673 my ($r, $w); pipe $r, $w or throw( 'No pipe' );
  384         4677  
677              
678 384         1077 fcntl $w, F_SETFL, O_NONBLOCK; $w->autoflush( 1 );
  384         1759  
679              
680 384         15137 binmode $r; binmode $w;
  384         608  
681              
682 384         1452 return [ $r, $w ];
683             }
684              
685             sub ns_environment ($$;$) {
686 6     6 1 90 my ($class, $k, $v) = @_; $k = (env_prefix $class).'_'.(uc $k);
  6         21  
687              
688 6 50       120 return defined $v ? $ENV{ $k } = $v : $ENV{ $k };
689             }
690              
691             sub pad ($$;$$) {
692 7     7 1 31 my ($v, $wanted, $str, $direction) = @_; my $len = $wanted - length $v;
  7         16  
693              
694 7 100 66     28 $len > 0 or return $v; (defined $str and length $str) or $str = q( );
  6 100       23  
695              
696 6         20 my $pad = substr( $str x $len, 0, $len );
697              
698 6 100 100     37 (not $direction or $direction eq 'right') and return $v.$pad;
699 2 100       8 $direction eq 'left' and return $pad.$v;
700              
701 1         10 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         11  
707             }
708              
709             sub socket_pair () {
710 1     1 1 5 my $rdr = gensym; my $wtr = gensym;
  1         17  
711              
712 1 50       37 socketpair( $rdr, $wtr, AF_UNIX, SOCK_STREAM, PF_UNSPEC )
713             or throw( $EXTENDED_OS_ERROR );
714 1         7 shutdown ( $rdr, 1 ); # No more writing for reader
715 1         2 shutdown ( $wtr, 0 ); # No more reading for writer
716              
717 1         4 return [ $rdr, $wtr ];
718             }
719              
720             sub split_on__ (;$$) {
721 11   50 11 1 416 return (split m{ _ }mx, $_[ 0 ] // q())[ $_[ 1 ] // 0 ];
      100        
722             }
723              
724             sub split_on_dash (;$$) {
725 9   50 9 1 249 return (split m{ \- }mx, $_[ 0 ] // q())[ $_[ 1 ] // 0 ];
      50        
726             }
727              
728             sub squeeze (;$) {
729 46   50 46 1 676 (my $v = $_[ 0 ] // q()) =~ s{ \s+ }{ }gmx; return $v;
  46         171  
730             }
731              
732             sub strip_leader (;$) {
733 647   50 647 1 2171 (my $v = $_[ 0 ] // q()) =~ s{ \A [^:]+ [:] \s+ }{}msx; return $v;
  647         3133  
734             }
735              
736             sub sub_name (;$) {
737 1   50 1 1 10 my $frame = 1 + ($_[ 0 ] // 0);
738              
739 1   50     10 return (split m{ :: }mx, ((caller $frame)[ 3 ]) // 'main')[ -1 ];
740             }
741              
742             sub symlink (;$$$) {
743 3     3 1 8 my ($from, $to, $base) = @_;
744              
745 3 50 66     17 defined $base and not CORE::length $base and $base = File::Spec->rootdir;
746 3 50       9 $from or throw( Unspecified, [ 'path from' ] );
747 3         8 $from = io( $from )->absolute( $base );
748 3 50       2022 $from->exists or throw( PathNotFound, [ "${from}" ] );
749 3 50       157 $to or throw( Unspecified, [ 'path to' ] );
750 3 50       8 $to = io( $to )->absolute( $base ); $to->is_link and $to->unlink;
  3         2045  
751 3 50       181 $to->exists and throw( PathAlreadyExists, [ "${to}" ] );
752 3 50       141 CORE::symlink "${from}", "${to}"
753             or throw( 'Symlink from [_1] to [_2] failed: [_3]',
754             [ "${from}", "${to}", $OS_ERROR ] );
755 3         338 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 3393 EXCEPTION_CLASS->throw( @_ );
764             }
765              
766             sub throw_on_error (;@) {
767 9     9 1 50 EXCEPTION_CLASS->throw_on_error( @_ );
768             }
769              
770             sub trim (;$$) {
771 2   100 2 1 686 my $chs = $_[ 1 ] // " \t"; (my $v = $_[ 0 ] // q()) =~ s{ \A [$chs]+ }{}mx;
  2   50     38  
772              
773 2         6 chomp $v; $v =~ s{ [$chs]+ \z }{}mx; return $v;
  2         20  
  2         11  
774             }
775              
776             sub unescape_TT (;$$) {
777 1 50   1 1 4 my $v = defined $_[ 0 ] ? $_[ 0 ] : q();
778 1   50     7 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         10  
782              
783 1         6 return $v;
784             }
785              
786             sub untaint_cmdline (;$) {
787 59     59 1 5055 return untaint_string( UNTAINT_CMDLINE, $_[ 0 ] );
788             }
789              
790             sub untaint_identifier (;$) {
791 16     16 1 615 return untaint_string( UNTAINT_IDENTIFIER, $_[ 0 ] );
792             }
793              
794             sub untaint_path (;$) {
795 1410     1410 1 8746 return untaint_string( UNTAINT_PATH, $_[ 0 ] );
796             }
797              
798             sub untaint_string ($;$) {
799 1485     1485 1 3056 my ($regex, $string) = @_;
800              
801 1485 100       3293 defined $string or return; length $string or return q();
  1472 50       3524  
802              
803 1472         8498 my ($untainted) = $string =~ $regex;
804              
805 1472 100 66     7323 (defined $untainted and $untainted eq $string)
806             or throw( Tainted, [ $string ], level => 3 );
807              
808 1468         37658 return $untainted;
809             }
810              
811             sub urandom (;$$) {
812 2   50 2 1 573 my ($wanted, $opts) = @_; $wanted //= 64; $opts //= {};
  2   50     16  
  2         13  
813              
814 2 50       18 my $default = [ q(), 'dev', $OSNAME eq 'freebsd' ? 'random' : 'urandom' ];
815 2   33     23 my $io = io( $opts->{source} // $default )->block_size( $wanted );
816              
817 2         1439 my $red; $io->exists and $io->is_readable and $red = $io->read
818 2 50 33     11 and $red == $wanted and return ${ $io->buffer };
  2   33     3863  
      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 1284 my $v = unpack "b*", pop; my $pad = " \t" x 8;
  1         3  
831              
832 1         3 $v =~ tr{01}{ \t}; $v =~ s{ (.{9}) }{$1\n}gmx;
  1         16  
833              
834 1         5 return "${pad}\n${v}";
835             }
836              
837             sub zip (@) {
838 1     1 1 808 my $p = @_ / 2; return @_[ map { $_, $_ + $p } 0 .. $p - 1 ];
  1         4  
  3         15  
839             }
840              
841             # Function composition
842             sub chain (;@) {
843 3     3 1 13 return (fold( sub { my ($x, $y) = @_; $x->$y } )->( shift ))->( @_ );
  3     1   7  
  1         7  
844             }
845              
846             sub compose (&;$) { # Was called build
847 3   100 3 1 8 my ($f, $g) = @_; $g //= sub { @_ }; return sub { $f->( $g->( @_ ) ) };
  1     3   4  
  3         969  
  3         13  
  3         15  
848             }
849              
850             sub curry (&$;@) {
851 1     1 1 4 my ($f, @args) = @_; return sub { $f->( @args, @_ ) };
  1     1   482  
  1         6  
852             }
853              
854             sub fold (&) {
855 3     3 1 7 my $f = shift;
856              
857             return sub (;$) {
858 3     3   7 my $x = shift;
859              
860             return sub (;@) {
861 3         5 my $y = $x; $y = $f->( $y, shift ) while (@_); return $y;
  3         13  
  3         20  
862             }
863 3         11 }
864 3         12 }
865              
866             sub Y (&) {
867 116     116 1 161 my $f = shift; return sub { $f->( Y( $f ) )->( @_ ) };
  116     114   283  
  114         187  
868             }
869              
870             sub factorial ($) {
871             return Y( sub (&) {
872 5     5   7 my $fac = shift;
873              
874             return sub ($) {
875 5         8 my $n = shift;
876              
877 5 100   1 1 18 return $n < 2 ? 1 : $n * $fac->( $n - 1 ) } } )->( @_ );
  5         24  
  1         489  
878             }
879              
880             sub fibonacci ($) {
881             return Y( sub {
882 109     109   157 my $fib = shift;
883              
884             return sub {
885 109         156 my $n = shift;
886              
887 109 100       422 return $n == 0 ? 0
    100          
888             : $n == 1 ? 1
889 109     1 1 257 : $fib->( $n - 1 ) + $fib->( $n - 2 ) } } )->( @_ );
  1         6  
890             }
891              
892             sub product (;@) {
893 4     4 1 11 return ((fold { $_[ 0 ] * $_[ 1 ] })->( 1 ))->( @_ );
  1     1   6  
894             }
895              
896             sub sum (;@) {
897 4     4 1 10 return ((fold { $_[ 0 ] + $_[ 1 ] })->( 0 ))->( @_ );
  1     1   6  
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: