File Coverage

blib/lib/Sub/Chain.pm
Criterion Covered Total %
statement 52 59 88.1
branch 13 24 54.1
condition 11 16 68.7
subroutine 13 13 100.0
pod 4 4 100.0
total 93 116 80.1


line stmt bran cond sub pod time code
1             # vim: set ts=2 sts=2 sw=2 expandtab smarttab:
2             #
3             # This file is part of Sub-Chain
4             #
5             # This software is copyright (c) 2010 by Randy Stauner.
6             #
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             #
10 2     2   1074 use strict;
  2         4  
  2         74  
11 2     2   11 use warnings;
  2         5  
  2         98  
12              
13             package Sub::Chain;
14             BEGIN {
15 2     2   41 $Sub::Chain::VERSION = '0.012';
16             }
17             BEGIN {
18 2     2   37 $Sub::Chain::AUTHORITY = 'cpan:RWSTAUNER';
19             }
20             # ABSTRACT: Chain subs together and call in succession
21              
22 2     2   10 use Carp qw(croak carp);
  2         4  
  2         158  
23              
24             # enable object to be called like a coderef
25             use overload
26 2         22 '&{}' => \&coderef,
27 2     2   14 fallback => 1;
  2         3  
28              
29 2     2   2052 use Object::Enum 0.072 ();
  2         42791  
  2         1369  
30              
31             our %Enums = (
32             result => Object::Enum->new({unset => 0, default => 'replace',
33             values => [qw(replace discard)]}),
34             on_undef => Object::Enum->new({unset => 0, default => 'proceed',
35             values => [qw(skip blank proceed)]}),
36             );
37              
38              
39             sub new {
40 6     6 1 520 my $class = shift;
41 6 50       22 my %opts = ref $_[0] ? %{$_[0]} : @_;
  0         0  
42              
43 6         17 my $self = {
44             chain => []
45             };
46 6         14 bless $self, $class;
47              
48 6         21 $self->_copy_enums(\%opts);
49              
50 6         265 return $self;
51             }
52              
53              
54             sub append {
55 10     10 1 1167 my ($self, $sub, $args, $opts) = @_;
56              
57             # TODO: normalize_spec (better than this):
58 10   100     43 $args ||= [];
59 10   50     54 $opts ||= {};
60 10         24 $self->_copy_enums($opts, $opts);
61              
62 10         940 CORE::push(@{ $self->{chain} }, [$sub, $args, $opts]);
  10         33  
63             # allow calls to be chained
64 10         28 return $self;
65             }
66              
67              
68             sub call {
69 13     13 1 76 my ($self, @args) = @_;
70             # cache function call
71 13         22 my $wantarray = wantarray;
72              
73 13         14 my @chain = @{ $self->{chain} };
  13         36  
74 13 50       33 carp("No subs appended to the chain")
75             unless @chain;
76              
77 13         20 foreach my $tr ( @chain ){
78 23         133 my ($sub, $extra, $opts) = @$tr;
79 23         51 my @all = (@args, @$extra);
80 23         22 my @result;
81              
82             # TODO: instead of duplicating enum objects do %opts = (%$self, %$opts)
83 23 50 66     93 if( @args && $opts->{on_undef} && !defined($args[0]) ){
      66        
84 0 0       0 next if $opts->{on_undef}->is_skip;
85 0 0       0 $args[0] = ''
86             if $opts->{on_undef}->is_blank;
87             }
88              
89             # call sub with same context as this
90 23 50       1036 if( !defined $wantarray ){
    50          
91 0         0 $sub->(@all);
92             }
93             elsif( $wantarray ){
94 0         0 @result = $sub->(@all);
95             }
96             else {
97 23         54 $result[0] = $sub->(@all);
98             }
99 23 100       139 @args = @result
100             if $opts->{result}->is_replace;
101             }
102              
103             # if 'result' isn't 'replace' what would be a good return value?
104             # would they expect one?
105              
106             # return value appropriate for context
107 13 50       187 if( !defined $wantarray ){
    50          
108 0         0 return;
109             }
110             elsif( $wantarray ){
111 0         0 return @args;
112             }
113             else {
114 13         66 return $args[0];
115             }
116             }
117              
118              
119             sub coderef {
120 5     5 1 15 my ($self) = @_;
121 5     5   16 return sub { $self->call(@_); }
122 5         26 }
123              
124             sub _copy_enums {
125 16     16   22 my ($self, $from, $to) = @_;
126 16   66     136 $to ||= $self;
127 16         57 while( my ($name, $enum) = each %Enums ){
128 32 100 66     1994 $to->{$name} = ($self->{$name} || $enum)->clone(
    100          
129             # use the string passed in
130             exists $from->{$name} ? $from->{$name} :
131             # clone from the default value saved on the instance
132             $self->{$name} ? $self->{$name}->value : ()
133             );
134             };
135             }
136              
137             1;
138              
139              
140             # TODO: link to questions on perlmonks and stackoverflow?
141              
142              
143             __END__