File Coverage

blib/lib/App/hopen/Phases.pm
Criterion Covered Total %
statement 30 71 42.2
branch 0 24 0.0
condition 0 9 0.0
subroutine 10 20 50.0
pod 6 6 100.0
total 46 130 35.3


line stmt bran cond sub pod time code
1             # App::hopen::Phases - definitions of phases
2             package App::hopen::Phases;
3 1     1   8 use Data::Hopen;
  1         2  
  1         73  
4 1     1   7 use Data::Hopen::Base;
  1         2  
  1         6  
5              
6             our $VERSION = '0.000010';
7              
8 1     1   1312 use parent 'Exporter';
  1         3  
  1         9  
9             our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS);
10             BEGIN {
11 1     1   151 my @normal_export_ok = qw(is_phase is_last_phase phase_idx
12             curr_phase_idx next_phase);
13 1         3 my @hopenfile_export = qw(on);
14              
15 1         3 @EXPORT = qw(@PHASES);
16 1         3 @EXPORT_OK = (@normal_export_ok, @hopenfile_export);
17 1         36 %EXPORT_TAGS = (
18             default => [@EXPORT],
19             all => [@EXPORT, @normal_export_ok],
20             hopenfile => [@hopenfile_export], # Not included in :all!
21             );
22             }
23              
24 1     1   7 use App::hopen::BuildSystemGlobals;
  1         9  
  1         121  
25 1     1   8 use Getargs::Mixed;
  1         2  
  1         62  
26 1     1   573 use List::MoreUtils qw(first_index);
  1         12514  
  1         7  
27              
28             # Docs {{{1
29              
30             =head1 NAME
31              
32             Data::Hopen::Phases - Definitions and routines for hopen phases
33              
34             =head1 SYNOPSIS
35              
36             Definition of hopen phases. Phase names are case-insensitive. The canonical
37             form has only the first letter capitalized.
38              
39             Phase names may only contain ASCII letters, digits, or underscore. The first
40             character of a phase may not be a digit. This is so they can be used as
41             identifiers if necessary.
42              
43             This package also defines a special export tag, C<:hopenfile>, for use when
44             running hopen files. The wrapper code in L<Data::Hopen::App> uses this
45             tag. Hopen files themselves do not need to use this tag.
46              
47             The names C<first>, C<start>, C<last>, and C<end> are reserved.
48              
49             =head1 VARIABLES
50              
51             =head2 @PHASES
52              
53             The phases we know about, in order.
54              
55             =head1 FUNCTIONS
56              
57             =cut
58              
59             # }}}1
60              
61             # Phases are case-insensitive.
62 1     1   1675 our @PHASES; BEGIN { @PHASES = ('Check', 'Gen'); }
63             # *** This is where the default phase ($PHASES[0]) is set ***
64             # TODO? be more sophisticated about this :)
65              
66             # Internal function to regularize phase names.
67             sub _clean {
68 0 0   0     my $test_phase = shift or croak 'Need a phase name';
69 0           $test_phase = lc $test_phase;
70 0 0 0       $test_phase = $PHASES[0]
71             if $test_phase eq 'first' or $test_phase eq 'start';
72 0 0 0       $test_phase = $PHASES[$#PHASES]
73             if $test_phase eq 'last' or $test_phase eq 'end';
74 0           return lc($test_phase);
75             } #_clean()
76              
77             =head2 is_phase
78              
79             Return truthy if the given argument is the name of a phase we know about.
80              
81             =cut
82              
83             sub is_phase {
84 0 0   0 1   my $test_phase = shift or croak 'Need a phase name';
85 0           $test_phase = _clean($test_phase);
86 0     0     my $curr_idx = first_index { lc($_) eq $test_phase } @PHASES;
  0            
87 0           return $curr_idx+1; # -1 => falsy; all others => truthy
88             } #is_phase()
89              
90             =head2 is_last_phase
91              
92             Return truthy if the argument is the name of the last phase.
93             If no argument is given, checks the current phase
94             (L<App::hopen::BuildSystemGlobals/$Phase>).
95              
96             =cut
97              
98 0   0 0 1   sub is_last_phase { _clean(shift // $Phase) eq lc($PHASES[$#PHASES]) }
99              
100             =head2 phase_idx
101              
102             Get the index of the phase given as a parameter.
103             Returns undef if none. Phases are case-insensitive.
104              
105             =cut
106              
107             sub phase_idx {
108 0 0   0 1   my $test_phase = shift or croak 'Need a phase name';
109 0           $test_phase = _clean($test_phase);
110 0     0     my $curr_idx = first_index { lc($_) eq $test_phase } @PHASES;
  0            
111 0 0         return $curr_idx<0 ? undef : $curr_idx;
112             } #phase_idx()
113              
114             =head2 curr_phase_idx
115              
116             Get the index of the current phase.
117              
118             =cut
119              
120 0     0 1   sub curr_phase_idx { phase_idx $Phase }
121              
122             =head2 next_phase
123              
124             Get the phase after the given on. Returns undef if the argument
125             is the last phase. Dies if the argument is not a phase.
126              
127             =cut
128              
129             sub next_phase {
130 0 0   0 1   my $test_phase = shift or croak 'Need a phase name';
131 0           $test_phase = _clean($test_phase);
132 0           my $curr_idx = phase_idx $test_phase;
133 0 0         die "$test_phase is not a phase I know about" unless defined($curr_idx);
134 0 0         return undef if $curr_idx == $#PHASES; # Last one
135              
136 0           return $PHASES[$curr_idx+1];
137             } #next_phase()
138              
139             =head1 ROUTINES FOR USE IN HOPEN FILES
140              
141             These are exported if the tag C<:hopenfile> is given on the C<use> line.
142              
143             =head2 on
144              
145             Take a given action only in a specified phase. Usage examples:
146              
147             on check => { foo => 42 }; # Just return the given hashref
148             on gen => 1337; # Returns { Gen => 1337 }
149             on check => sub { return { foo => 1337 } };
150             # Call the given sub and return its return value.
151              
152             This is designed for use within a hopen file.
153             See L<Data::Hopen::App/_run_phase> for the execution environment C<on()> is
154             designed to run in.
155              
156             When run as part of a hopen file, C<on()> will skip the rest of the file if it
157             runs. For example:
158              
159             say "Hello, world!"; # This always runs
160             on check => { answer => $answer }; # This runs during the Check phase
161             on gen => { done => true }; # This runs during the Gen phase
162             say "Phase was neither Check nor Gen"; # Doesn't run in Check or Gen
163              
164             TODO support C<< on '!last' => ... >> or similar to take action when not in
165             the given phase.
166              
167             =cut
168              
169             sub on {
170 0     0 1   my $caller = caller;
171 0           my (%args) = parameters([qw(phase value)], @_);
172              
173 0           my $which_phase = _clean($args{phase});
174 0           my $val = $args{value};
175              
176 0           my $which_idx = phase_idx($which_phase);
177 0 0         return if $which_idx != curr_phase_idx;
178              
179             # We are in the correct phase. Take appropriate action.
180             # However, don't change our own return value.
181 0           my $result;
182 0 0         if(ref $val eq 'CODE') {
    0          
183 0           $result = &$val;
184             } elsif(ref $val eq 'HASH') {
185 0           $result = $val; # TODO? clone?
186             } else {
187 0           $result = {$PHASES[$which_idx] => $val};
188             }
189              
190             # Stash the value for the caller.
191             {
192 1     1   9 no strict 'refs';
  1         3  
  1         108  
  0            
193 0           ${ $caller . "::__R_on_result" } = $result;
  0            
194             }
195              
196             # Done --- skip the rest of the hopen file if we're in one.
197 0     0     hlog { 'Done with script for phase ``' . $args{phase} . "''" } 3;
  0            
198 0           eval {
199 1     1   7 no warnings 'exiting';
  1         2  
  1         150  
200 0           last __R_DO;
201             };
202             } #on()
203              
204             1;
205             __END__
206             # vi: set fdm=marker: #