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