File Coverage

blib/lib/Earth.pm
Criterion Covered Total %
statement 92 103 89.3
branch 34 52 65.3
condition 23 43 53.4
subroutine 23 23 100.0
pod 11 13 84.6
total 183 234 78.2


line stmt bran cond sub pod time code
1             package Earth;
2              
3 1     1   1555 use 5.018;
  1         3  
4              
5 1     1   4 use strict;
  1         2  
  1         15  
6 1     1   4 use warnings;
  1         1  
  1         21  
7              
8 1     1   3 use Exporter 'import';
  1         2  
  1         305  
9              
10             our @EXPORT = qw(
11             args
12             call
13             can
14             chain
15             error
16             false
17             make
18             roll
19             then
20             true
21             wrap
22             );
23              
24             our $TRACE_LIMIT = $ENV{EARTH_TRACE_LIMIT};
25             our $TRACE_OFFSET = $ENV{EARTH_TRACE_OFFSET} ||= 0;
26              
27             require Scalar::Util;
28              
29             # STATE
30              
31             state $cached = {
32             'Earth' => 1,
33             };
34              
35             # VERSION
36              
37             our $VERSION = '0.03';
38              
39             # AUTHORITY
40              
41             our $AUTHORITY = 'cpan:AWNCORP';
42              
43             # FUNCTIONS
44              
45             sub args {
46             return (!@_)
47             ? ({})
48             : ((@_ == 1 && ref($_[0]) eq 'HASH')
49 4 50 100 4 1 8969 ? (!%{$_[0]} ? {} : {%{$_[0]}})
  1 100       4  
  1 100       10  
    50          
50             : (@_ % 2 ? {@_, undef} : {@_}));
51             }
52              
53             sub call {
54 21     21 1 1928 my ($invocant, $routine, @arguments) = @_;
55 21         27 my $next = !!$routine;
56 21 50 33     103 if ($next && UNIVERSAL::isa($invocant, 'CODE')) {
57 0         0 return $invocant->(@arguments);
58             }
59 21 100 66     69 if ($next && Scalar::Util::blessed($invocant)) {
60 8 50       64 return $invocant->$routine(@arguments) if UNIVERSAL::can($invocant, $routine);
61 0         0 $next = 0;
62             }
63 13 100 66     48 if ($next && ref($invocant) eq 'SCALAR') {
64 3 50       18 return $$invocant->$routine(@arguments) if UNIVERSAL::can($$invocant, $routine);
65 0         0 $next = 0;
66             }
67 10 50 33     24 if ($next && UNIVERSAL::can(load($invocant), $routine)) {
68 1     1   6 no strict 'refs';
  1         2  
  1         62  
69 10         13 return &{"${invocant}::${routine}"}(@arguments);
  10         77  
70             }
71 0 0 0     0 if ($next && UNIVERSAL::can($invocant, 'AUTOLOAD')) {
72 1     1   6 no strict 'refs';
  1         1  
  1         462  
73 0         0 return &{"${invocant}::${routine}"}(@arguments);
  0         0  
74             }
75 0         0 error("Exception! call(@{[join(', ', map qq('$_'), @_)]}) failed.");
  0         0  
76             }
77              
78             sub can {
79 1 50   1 1 17 return if !@_;
80 1 50       5 return call((ref($_[0]) ? $_[0] : \$_[0]), 'can', $_[1]);
81             }
82              
83              
84             sub chain {
85 3     3 1 35 my ($invocant, @routines) = @_;
86 3 50       6 return if !$invocant;
87 3 100       17 for my $next (map +(ref($_) eq 'ARRAY' ? $_ : [$_]), @routines) {
88 7         31 $invocant = call($invocant, @$next);
89             }
90 3         27 return $invocant;
91             }
92              
93             sub error {
94 3     3 1 6176 my ($message, $offset, $limit) = @_;
95 3   100     11 my @stacktrace = ($message || 'Exception!');
96 3         8 my $frames = trace($offset, $limit);
97 3 100       7 if (@$frames > 1) {
98 2         3 push @stacktrace, "\nTraceback (reverse chronological order):\n";
99             }
100 3         7 for (my $i = 1; $i < @$frames; $i++) {
101 12         44 push @stacktrace,
102             "$$frames[$i][3]\n in $$frames[$i][1] at line $$frames[$i][2]";
103             }
104 3         36 die(join("\n", @stacktrace, ""));
105             }
106              
107             sub false {
108 2     2 1 4161 require Scalar::Util;
109 2         17 state $false = Scalar::Util::dualvar(0, "0");
110             }
111              
112             sub load {
113 10     10 0 15 my ($package) = @_;
114              
115 10 100       22 if ($$cached{$package}) {
116 8         39 return $package;
117             }
118              
119 2 50       4 if ($package eq 'main') {
120 0         0 return do {$$cached{$package} = 1; $package};
  0         0  
  0         0  
121             }
122              
123 2   33     13 my $failed = !$package || $package !~ /^\w(?:[\w:']*\w)?$/;
124 2         2 my $loaded;
125              
126 2 50       5 my $error = do {
127 2         3 local $@;
128 1     1   6 no strict 'refs';
  1         2  
  1         348  
129 2         10 $loaded = !!UNIVERSAL::can($package, 'new');
130 2 50       11 $loaded = !!UNIVERSAL::can($package, 'import') if !$loaded;
131 2 50       110 $loaded = eval "require $package; 1" if !$loaded;
132 2         9 $@;
133             }
134             if !$failed;
135              
136 2 50 33     16 error("Exception! Error loading package \"$package\".")
      33        
137             if $error
138             or $failed
139             or not $loaded;
140              
141 2         4 $$cached{$package} = 1;
142              
143 2         12 return $package;
144             }
145              
146             sub make {
147 6 50   6 1 18 return if !@_;
148 6         12 return call($_[0], 'new', @_);
149             }
150              
151             sub roll {
152 2     2 1 28 return (@_[1,0,2..$#_]);
153             }
154              
155             sub then {
156 1     1 1 3 return ($_[0], call(@_));
157             }
158              
159             sub trace {
160 3     3 0 5 my ($offset, $limit) = (@_);
161              
162 3   50     12 $offset //= $TRACE_OFFSET // 1;
      66        
163 3   66     9 $limit //= $TRACE_LIMIT;
164              
165 3         4 my $frames = [];
166 3         20 for (my $i = $offset; my @caller = caller($i); $i++) {
167 15         36 push @$frames, [@caller];
168 15 100 66     69 last if defined($limit) && $i + 1 == $offset + $limit;
169             }
170              
171 3         6 return $frames;
172             }
173              
174             sub true {
175 2     2 1 4060 require Scalar::Util;
176 2         17 state $true = Scalar::Util::dualvar(1, "1");
177             }
178              
179             sub wrap {
180 7     7 1 11112 my ($package, $alias) = @_;
181 7 50       16 return if !$package;
182 7   66     28 my $moniker = $alias // $package =~ s/\W//gr;
183 7         15 my $caller = caller(0);
184 1     1   7 no strict 'refs';
  1         1  
  1         32  
185 1     1   5 no warnings 'redefine';
  1         7  
  1         189  
186 7 100   17   29 return *{"${caller}::${moniker}"} = sub {@_ ? make($package, @_) : $package};
  7         102  
  17         19786  
187             }
188              
189             1;
190              
191              
192             =head1 NAME
193              
194             Earth - FP Framework
195              
196             =cut
197              
198             =head1 ABSTRACT
199              
200             FP Framework for Perl 5
201              
202             =cut
203              
204             =head1 VERSION
205              
206             0.03
207              
208             =cut
209              
210             =head1 SYNOPSIS
211              
212             package main;
213              
214             use Earth;
215              
216             wrap 'Digest::SHA', 'SHA';
217              
218             call(SHA(), 'sha1_hex');
219              
220             # "da39a3ee5e6b4b0d3255bfef95601890afd80709"
221              
222             =cut
223              
224             =head1 DESCRIPTION
225              
226             Earth is a functional-programming framework for Perl 5. Perl is a
227             multi-paradigm programming language that also supports functional programming,
228             but, Perl has an intentionally limited standard library with an emphasis on
229             providing library support via the CPAN which is overwhelmingly object-oriented.
230             This makes developing in a functional style difficult as you'll eventually need
231             to rely on a CPAN library that requires you to switch over to object-oriented
232             programming. Earth facilitates functional programming for Perl 5 by providing
233             functions which enable indirect routine dispatching, allowing the execution of
234             both functional and object-oriented code.
235              
236             =cut
237              
238             =head1 FUNCTIONS
239              
240             This package provides the following functions:
241              
242             =cut
243              
244             =head2 args
245              
246             args(Any @args) (HashRef)
247              
248             The args function takes a list of arguments and returns a hashref.
249              
250             I>
251              
252             =over 4
253              
254             =item args example 1
255              
256             # given: synopsis
257              
258             args(content => 'example');
259              
260             # {content => "example"}
261              
262             =back
263              
264             =over 4
265              
266             =item args example 2
267              
268             # given: synopsis
269              
270             args({content => 'example'});
271              
272             # {content => "example"}
273              
274             =back
275              
276             =over 4
277              
278             =item args example 3
279              
280             # given: synopsis
281              
282             args('content');
283              
284             # {content => undef}
285              
286             =back
287              
288             =over 4
289              
290             =item args example 4
291              
292             # given: synopsis
293              
294             args('content', 'example', 'algorithm');
295              
296             # {content => "example", algorithm => undef}
297              
298             =back
299              
300             =cut
301              
302             =head2 call
303              
304             call(Str | Object | CodeRef $self, Any @args) (Any)
305              
306             The call function dispatches function and method calls to a package and returns
307             the result.
308              
309             I>
310              
311             =over 4
312              
313             =item call example 1
314              
315             # given: synopsis
316              
317             call(SHA, 'sha1_hex');
318              
319             # "da39a3ee5e6b4b0d3255bfef95601890afd80709"
320              
321             =back
322              
323             =over 4
324              
325             =item call example 2
326              
327             # given: synopsis
328              
329             call('Digest::SHA', 'sha1_hex');
330              
331             # "da39a3ee5e6b4b0d3255bfef95601890afd80709"
332              
333             =back
334              
335             =over 4
336              
337             =item call example 3
338              
339             # given: synopsis
340              
341             call(\SHA, 'new');
342              
343             # bless(do{\(my $o = '...')}, 'Digest::SHA')
344              
345             =back
346              
347             =over 4
348              
349             =item call example 4
350              
351             # given: synopsis
352              
353             wrap 'Digest';
354              
355             call(Digest('SHA'), 'reset');
356              
357             # "da39a3ee5e6b4b0d3255bfef95601890afd80709"
358              
359             =back
360              
361             =cut
362              
363             =head2 can
364              
365             can(Str | Object | CodeRef $self, Str $name) (CodeRef)
366              
367             The can function checks if the object or class has a routine matching the name
368             provided, and if so returns a coderef for that routine.
369              
370             I>
371              
372             =over 4
373              
374             =item can example 1
375              
376             # given: synopsis
377              
378             my $coderef = can(SHA(1), 'sha1_hex');
379              
380             # sub { ... }
381              
382             =back
383              
384             =cut
385              
386             =head2 chain
387              
388             chain(Str | Object | CodeRef $self, Str | ArrayRef[Str] @args) (Any)
389              
390             The chain function chains function and method calls to a package (and return
391             values) and returns the result.
392              
393             I>
394              
395             =over 4
396              
397             =item chain example 1
398              
399             # given: synopsis
400              
401             my $hex = chain(\SHA, 'new', 'sha1_hex');
402              
403             # "d3aed913fdc7f277dddcbde47d50a8b5259cb4bc"
404              
405             =back
406              
407             =over 4
408              
409             =item chain example 2
410              
411             # given: synopsis
412              
413             my $hex = chain(\SHA, 'new', ['add', 'hello'], 'sha1_hex');
414              
415             # "f47b0cd4b6336d07ab117d7ee3f47566c9799f23"
416              
417             =back
418              
419             =over 4
420              
421             =item chain example 3
422              
423             # given: synopsis
424              
425             wrap 'Digest';
426              
427             my $hex = chain(Digest('SHA'), ['add', 'hello'], 'sha1_hex');
428              
429             # "8575ce82b266fdb5bc98eb43488c3b420577c24c"
430              
431             =back
432              
433             =cut
434              
435             =head2 error
436              
437             error(Str $message, Int $offset, Int $limit) (Any)
438              
439             The error function dies with the error message provided and prints a
440             stacktrace. If C<$limit> or C<$offset> are provided, those options will
441             constrain the output of the stacktrace.
442              
443             I>
444              
445             =over 4
446              
447             =item error example 1
448              
449             # given: synopsis
450              
451             error;
452              
453             # "Exception!"
454              
455             =back
456              
457             =over 4
458              
459             =item error example 2
460              
461             # given: synopsis
462              
463             error('Exception!');
464              
465             # "Exception!"
466              
467             =back
468              
469             =over 4
470              
471             =item error example 3
472              
473             # given: synopsis
474              
475             error('Exception!', 0, 1);
476              
477             # "Exception!"
478              
479             =back
480              
481             =cut
482              
483             =head2 false
484              
485             false() (Bool)
486              
487             The false function returns a falsy boolean value which is designed to be
488             practically indistinguishable from the conventional numerical C<0> value.
489              
490             I>
491              
492             =over 4
493              
494             =item false example 1
495              
496             package main;
497              
498             use Earth;
499              
500             my $false = false;
501              
502             # 0
503              
504             =back
505              
506             =over 4
507              
508             =item false example 2
509              
510             package main;
511              
512             use Earth;
513              
514             my $true = !false;
515              
516             # 1
517              
518             =back
519              
520             =cut
521              
522             =head2 make
523              
524             make(Str $package, Any @args) (Any)
525              
526             The make function L<"calls"|Earth/call> the C routine on the invocant and
527             returns the result which should be a package string or an object.
528              
529             I>
530              
531             =over 4
532              
533             =item make example 1
534              
535             # given: synopsis
536              
537             my $string = make(SHA);
538              
539             # bless(do{\(my $o = '...')}, 'Digest::SHA')
540              
541             =back
542              
543             =over 4
544              
545             =item make example 2
546              
547             # given: synopsis
548              
549             my $string = make(Digest, 'SHA');
550              
551             # bless(do{\(my $o = '...')}, 'Digest::SHA')
552              
553             =back
554              
555             =cut
556              
557             =head2 roll
558              
559             roll(Str $name, Any @args) (Any)
560              
561             The roll function takes a list of arguments, assuming the first argument is
562             invokable, and reorders the list such that the routine name provided comes
563             after the invocant (i.e. the 1st argument), creating a list acceptable to the
564             L function.
565              
566             I>
567              
568             =over 4
569              
570             =item roll example 1
571              
572             package main;
573              
574             use Earth;
575              
576             my @list = roll('sha1_hex', SHA);
577              
578             # ("Digest::SHA", "sha1_hex")
579              
580             =back
581              
582             =over 4
583              
584             =item roll example 2
585              
586             package main;
587              
588             use Earth;
589              
590             my @list = roll('sha1_hex', call(SHA(1), 'reset'));
591              
592             # (bless(do{\(my $o = '...')}, 'Digest::SHA'), "sha1_hex")
593              
594             =back
595              
596             =cut
597              
598             =head2 then
599              
600             then(Str | Object | CodeRef $self, Any @args) (Any)
601              
602             The then function proxies the call request to the L function and returns
603             the result as a list, prepended with the invocant.
604              
605             I>
606              
607             =over 4
608              
609             =item then example 1
610              
611             package main;
612              
613             use Earth;
614              
615             my @list = then(SHA, 'sha1_hex');
616              
617             # ("Digest::SHA", "da39a3ee5e6b4b0d3255bfef95601890afd80709")
618              
619             =back
620              
621             =cut
622              
623             =head2 true
624              
625             true() (Bool)
626              
627             The true function returns a truthy boolean value which is designed to be
628             practically indistinguishable from the conventional numerical C<1> value.
629              
630             I>
631              
632             =over 4
633              
634             =item true example 1
635              
636             package main;
637              
638             use Earth;
639              
640             my $true = true;
641              
642             # 1
643              
644             =back
645              
646             =over 4
647              
648             =item true example 2
649              
650             package main;
651              
652             use Earth;
653              
654             my $false = !true;
655              
656             # 0
657              
658             =back
659              
660             =cut
661              
662             =head2 wrap
663              
664             wrap(Str $package, Str $alias) (CodeRef)
665              
666             The wrap function installs a wrapper function in the calling package which when
667             called either returns the package string if no arguments are provided, or calls
668             L on the package with whatever arguments are provided and returns the
669             result. Unless an alias is provided as a second argument, special characters
670             are stripped from the package to create the function name.
671              
672             I>
673              
674             =over 4
675              
676             =item wrap example 1
677              
678             # given: synopsis
679              
680             my $coderef = wrap('Digest::SHA');
681              
682             # my $digest = DigestSHA();
683              
684             # "Digest::SHA"
685              
686             =back
687              
688             =over 4
689              
690             =item wrap example 2
691              
692             # given: synopsis
693              
694             my $coderef = wrap('Digest::SHA');
695              
696             # my $digest = DigestSHA(1);
697              
698             # bless(do{\(my $o = '...')}, 'Digest::SHA')
699              
700             =back
701              
702             =over 4
703              
704             =item wrap example 3
705              
706             # given: synopsis
707              
708             my $coderef = wrap('Digest::SHA', 'SHA');
709              
710             # my $digest = SHA;
711              
712             # "Digest::SHA"
713              
714             =back
715              
716             =over 4
717              
718             =item wrap example 4
719              
720             # given: synopsis
721              
722             my $coderef = wrap('Digest::SHA', 'SHA');
723              
724             # my $digest = SHA(1);
725              
726             # bless(do{\(my $o = '...')}, 'Digest::SHA')
727              
728             =back
729              
730             =cut
731              
732             =head1 AUTHORS
733              
734             Awncorp, C
735              
736             =cut