| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  |  | 
| 2 |  |  |  |  |  |  | use warnings; | 
| 3 | 20 |  |  | 20 |  | 2343 | use strict; | 
|  | 20 |  |  |  |  | 33 |  | 
|  | 20 |  |  |  |  | 671 |  | 
| 4 | 20 |  |  | 20 |  | 94 | use base qw( Workflow::Base ); | 
|  | 20 |  |  |  |  | 42 |  | 
|  | 20 |  |  |  |  | 511 |  | 
| 5 | 20 |  |  | 20 |  | 90 | use Log::Log4perl qw( get_logger ); | 
|  | 20 |  |  |  |  | 36 |  | 
|  | 20 |  |  |  |  | 2370 |  | 
| 6 | 20 |  |  | 20 |  | 120 | use Workflow::Condition; | 
|  | 20 |  |  |  |  | 36 |  | 
|  | 20 |  |  |  |  | 130 |  | 
| 7 | 20 |  |  | 20 |  | 1656 | use Workflow::Condition::Evaluate; | 
|  | 20 |  |  |  |  | 34 |  | 
|  | 20 |  |  |  |  | 189 |  | 
| 8 | 20 |  |  | 20 |  | 6560 | use Workflow::Exception qw( workflow_error condition_error ); | 
|  | 20 |  |  |  |  | 45 |  | 
|  | 20 |  |  |  |  | 102 |  | 
| 9 | 20 |  |  | 20 |  | 820 | use Exception::Class; | 
|  | 20 |  |  |  |  | 29 |  | 
|  | 20 |  |  |  |  | 792 |  | 
| 10 | 20 |  |  | 20 |  | 101 | use Workflow::Factory qw( FACTORY ); | 
|  | 20 |  |  |  |  | 32 |  | 
|  | 20 |  |  |  |  | 153 |  | 
| 11 | 20 |  |  | 20 |  | 1675 | use English qw( -no_match_vars ); | 
|  | 20 |  |  |  |  | 42 |  | 
|  | 20 |  |  |  |  | 116 |  | 
| 12 | 20 |  |  | 20 |  | 639 |  | 
|  | 20 |  |  |  |  | 47 |  | 
|  | 20 |  |  |  |  | 82 |  | 
| 13 |  |  |  |  |  |  | $Workflow::State::VERSION = '1.61'; | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | my @FIELDS   = qw( state description type ); | 
| 16 |  |  |  |  |  |  | my @INTERNAL = qw( _test_condition_count _factory _actions _conditions | 
| 17 |  |  |  |  |  |  | _next_state ); | 
| 18 |  |  |  |  |  |  | __PACKAGE__->mk_accessors( @FIELDS, @INTERNAL ); | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | ######################################## | 
| 22 |  |  |  |  |  |  | # PUBLIC | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | my ( $self, $action_name ) = @_; | 
| 25 |  |  |  |  |  |  | $self->_contains_action_check($action_name); | 
| 26 | 139 |  |  | 139 | 1 | 239 | return @{ $self->_conditions->{$action_name} }; | 
| 27 | 139 |  |  |  |  | 300 | } | 
| 28 | 139 |  |  |  |  | 1277 |  | 
|  | 139 |  |  |  |  | 241 |  | 
| 29 |  |  |  |  |  |  | my ( $self, $wf, $action_name ) = @_; | 
| 30 |  |  |  |  |  |  | my $common_config = | 
| 31 |  |  |  |  |  |  | $self->_factory->get_action_config($wf, $action_name); | 
| 32 | 60 |  |  | 60 | 1 | 139 | my $state_config  = $self->_actions->{$action_name}; | 
| 33 | 60 |  |  |  |  | 153 | my $config        = { %{$common_config}, %{$state_config} }; | 
| 34 |  |  |  |  |  |  | my $action_class  = $common_config->{class}; | 
| 35 | 60 |  |  |  |  | 120 |  | 
| 36 | 60 |  |  |  |  | 487 | return $action_class->new( $wf, $config ); | 
|  | 60 |  |  |  |  | 156 |  | 
|  | 60 |  |  |  |  | 292 |  | 
| 37 | 60 |  |  |  |  | 130 | } | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 60 |  |  |  |  | 462 | my ( $self, $action_name ) = @_; | 
| 40 |  |  |  |  |  |  | return $self->_actions->{$action_name}; | 
| 41 |  |  |  |  |  |  | } | 
| 42 |  |  |  |  |  |  |  | 
| 43 | 252 |  |  | 252 | 1 | 357 | my ($self) = @_; | 
| 44 | 252 |  |  |  |  | 480 | return keys %{ $self->_actions }; | 
| 45 |  |  |  |  |  |  | } | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | my ( $self, $wf, $group ) = @_; | 
| 48 | 29 |  |  | 29 | 1 | 42 | my @all_actions       = $self->get_all_action_names; | 
| 49 | 29 |  |  |  |  | 30 | my @available_actions = (); | 
|  | 29 |  |  |  |  | 70 |  | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | # assuming that the user wants the _fresh_ list of available actions, | 
| 52 |  |  |  |  |  |  | # we clear the condition cache before checking which ones are available | 
| 53 | 29 |  |  | 29 | 1 | 6857 | local $wf->{'_condition_result_cache'} = {}; | 
| 54 | 29 |  |  |  |  | 60 |  | 
| 55 | 29 |  |  |  |  | 348 | foreach my $action_name (@all_actions) { | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | if ( $group ) { | 
| 58 |  |  |  |  |  |  | my $action_config = | 
| 59 | 29 |  |  |  |  | 67 | $self->_factory()->get_action_config( $wf, $action_name ); | 
| 60 |  |  |  |  |  |  | if ( defined $action_config->{group} | 
| 61 | 29 |  |  |  |  | 53 | and $action_config->{group} ne $group ) { | 
| 62 |  |  |  |  |  |  | next; | 
| 63 | 76 | 50 |  |  |  | 399 | } | 
| 64 | 0 |  |  |  |  | 0 | } | 
| 65 |  |  |  |  |  |  |  | 
| 66 | 0 | 0 | 0 |  |  | 0 | if ( $self->is_action_available( $wf, $action_name ) ) { | 
| 67 |  |  |  |  |  |  | push @available_actions, $action_name; | 
| 68 | 0 |  |  |  |  | 0 | } | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  | return @available_actions; | 
| 71 |  |  |  |  |  |  | } | 
| 72 | 76 | 100 |  |  |  | 167 |  | 
| 73 | 43 |  |  |  |  | 94 | my ( $self, $wf, $action_name ) = @_; | 
| 74 |  |  |  |  |  |  | eval { $self->evaluate_action( $wf, $action_name ) }; | 
| 75 |  |  |  |  |  |  |  | 
| 76 | 29 |  |  |  |  | 290 | # Everything is fine | 
| 77 |  |  |  |  |  |  | return 1 unless( $EVAL_ERROR ); | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | # We got an exception, check if it is a Workflow::Exception | 
| 80 | 76 |  |  | 76 | 1 | 141 | return 0 if (Exception::Class->caught('Workflow::Exception')); | 
| 81 | 76 |  |  |  |  | 85 |  | 
|  | 76 |  |  |  |  | 439 |  | 
| 82 |  |  |  |  |  |  | $EVAL_ERROR->rethrow() if (ref $EVAL_ERROR); | 
| 83 |  |  |  |  |  |  |  | 
| 84 | 76 | 100 |  |  |  | 22827 | croak $EVAL_ERROR; | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 | 33 | 50 |  |  |  | 206 | my ($self) = @_; | 
| 88 |  |  |  |  |  |  | return; # left for backward compatibility with 1.49 | 
| 89 | 0 | 0 |  |  |  | 0 | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 0 |  |  |  |  | 0 | my ( $self, $wf, $action_name ) = @_; | 
| 92 |  |  |  |  |  |  | my $state = $self->state; | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | # NOTE: this will throw an exception if C<$action_name> is not | 
| 95 | 0 |  |  | 0 | 1 | 0 | # contained in this state, so there's no need to do it explicitly | 
| 96 | 0 |  |  |  |  | 0 |  | 
| 97 |  |  |  |  |  |  | my @conditions = $self->get_conditions($action_name); | 
| 98 |  |  |  |  |  |  | foreach my $condition (@conditions) { | 
| 99 |  |  |  |  |  |  | my $condition_name = $condition->name; | 
| 100 | 136 |  |  | 136 | 1 | 214 |  | 
| 101 | 136 |  |  |  |  | 289 | my $rv; | 
| 102 |  |  |  |  |  |  | eval { | 
| 103 |  |  |  |  |  |  | $rv = Workflow::Condition->evaluate_condition($wf, $condition_name); | 
| 104 |  |  |  |  |  |  | }; | 
| 105 |  |  |  |  |  |  | if ($EVAL_ERROR) { | 
| 106 | 136 |  |  |  |  | 1318 | if (Exception::Class->caught('Workflow::Exception::Condition')) { | 
| 107 | 136 |  |  |  |  | 1236 | condition_error "No access to action '$action_name' in ", | 
| 108 | 84 |  |  |  |  | 237 | "state '$state' because $EVAL_ERROR "; | 
| 109 |  |  |  |  |  |  | } | 
| 110 | 84 |  |  |  |  | 748 | else { | 
| 111 | 84 |  |  |  |  | 92 | $EVAL_ERROR->rethrow() if (ref $EVAL_ERROR ne ''); | 
| 112 | 84 |  |  |  |  | 192 | # For briefness, we just send back the first line of EVAL | 
| 113 |  |  |  |  |  |  | my @t = split /\n/, $EVAL_ERROR; | 
| 114 | 84 | 50 |  |  |  | 639 | my $ee = shift @t; | 
|  |  | 100 |  |  |  |  |  | 
| 115 | 0 | 0 |  |  |  | 0 | Exception::Class::Base->throw( | 
| 116 | 0 |  |  |  |  | 0 | error | 
| 117 |  |  |  |  |  |  | => "Got unknown exception while handling condition '$condition_name' / " . $ee ); | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  | } | 
| 120 | 0 | 0 |  |  |  | 0 | elsif (! $rv) { | 
| 121 |  |  |  |  |  |  | condition_error "No access to action '$action_name' in ", | 
| 122 | 0 |  |  |  |  | 0 | "state '$state' because condition '$condition_name' failed"; | 
| 123 | 0 |  |  |  |  | 0 | } | 
| 124 | 0 |  |  |  |  | 0 | } | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | my ( $self, $action_name, $action_return ) = @_; | 
| 128 |  |  |  |  |  |  | $self->_contains_action_check($action_name); | 
| 129 |  |  |  |  |  |  | my $resulting_state = $self->_next_state->{$action_name}; | 
| 130 | 33 |  |  |  |  | 153 | return $resulting_state unless ( ref($resulting_state) eq 'HASH' ); | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | if ( defined $action_return ) { | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | # TODO: Throw exception if $action_return not found and no '*' defined? | 
| 135 |  |  |  |  |  |  | return $resulting_state->{$action_return} || $resulting_state->{'*'}; | 
| 136 |  |  |  |  |  |  | } else { | 
| 137 | 53 |  |  | 53 | 1 | 98 | return %{$resulting_state}; | 
| 138 | 53 |  |  |  |  | 121 | } | 
| 139 | 53 |  |  |  |  | 567 | } | 
| 140 | 53 | 50 |  |  |  | 545 |  | 
| 141 |  |  |  |  |  |  | my ( $self, $wf ) = @_; | 
| 142 | 0 | 0 |  |  |  | 0 | my $state = $self->state; | 
| 143 |  |  |  |  |  |  | unless ( $self->autorun ) { | 
| 144 |  |  |  |  |  |  | workflow_error "State '$state' is not marked for automatic ", | 
| 145 | 0 |  | 0 |  |  | 0 | "execution. If you want it to be run automatically ", | 
| 146 |  |  |  |  |  |  | "set the 'autorun' property to 'yes'."; | 
| 147 | 0 |  |  |  |  | 0 | } | 
|  | 0 |  |  |  |  | 0 |  | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | my @actions   = $self->get_available_action_names($wf); | 
| 150 |  |  |  |  |  |  | my $pre_error = "State '$state' should be automatically executed but"; | 
| 151 |  |  |  |  |  |  | if ( scalar @actions > 1 ) { | 
| 152 | 9 |  |  | 9 | 1 | 21 | workflow_error "$pre_error there are multiple actions available ", | 
| 153 | 9 |  |  |  |  | 14 | "for execution. Actions are: ", join ', ', @actions; | 
| 154 | 9 | 50 |  |  |  | 77 | } | 
| 155 | 0 |  |  |  |  | 0 | if ( scalar @actions == 0 ) { | 
| 156 |  |  |  |  |  |  | workflow_error | 
| 157 |  |  |  |  |  |  | "$pre_error there are no actions available for execution."; | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  | $self->log->debug("Auto-running state '$state' with action '$actions[0]'"); | 
| 160 | 9 |  |  |  |  | 22 | return $actions[0]; | 
| 161 | 9 |  |  |  |  | 28 | } | 
| 162 | 9 | 50 |  |  |  | 19 |  | 
| 163 | 0 |  |  |  |  | 0 | my ( $self, $setting ) = @_; | 
| 164 |  |  |  |  |  |  | if ( defined $setting ) { | 
| 165 |  |  |  |  |  |  | if ( $setting =~ /^(true|1|yes)$/i ) { | 
| 166 | 9 | 50 |  |  |  | 17 | $self->{autorun} = 'yes'; | 
| 167 | 0 |  |  |  |  | 0 | } else { | 
| 168 |  |  |  |  |  |  | $self->{autorun} = 'no'; | 
| 169 |  |  |  |  |  |  | } | 
| 170 | 9 |  |  |  |  | 32 | } | 
| 171 | 9 |  |  |  |  | 70 | return ( $self->{autorun} eq 'yes' ); | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | my ( $self, $setting ) = @_; | 
| 175 | 221 |  |  | 221 | 1 | 345 | if ( defined $setting ) { | 
| 176 | 221 | 100 |  |  |  | 435 | if ( $setting =~ /^(true|1|yes)$/i ) { | 
| 177 | 138 | 100 |  |  |  | 590 | $self->{may_stop} = 'yes'; | 
| 178 | 9 |  |  |  |  | 12 | } else { | 
| 179 |  |  |  |  |  |  | $self->{may_stop} = 'no'; | 
| 180 | 129 |  |  |  |  | 207 | } | 
| 181 |  |  |  |  |  |  | } | 
| 182 |  |  |  |  |  |  | return ( $self->{may_stop} eq 'yes' ); | 
| 183 | 221 |  |  |  |  | 561 | } | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | ######################################## | 
| 186 |  |  |  |  |  |  | # INTERNAL | 
| 187 | 138 |  |  | 138 | 1 | 198 |  | 
| 188 | 138 | 50 |  |  |  | 212 | my ( $self, $config, $factory ) = @_; | 
| 189 | 138 | 50 |  |  |  | 317 |  | 
| 190 | 0 |  |  |  |  | 0 | # Fallback for old style | 
| 191 |  |  |  |  |  |  | $factory ||= FACTORY; | 
| 192 | 138 |  |  |  |  | 194 | my $name = $config->{name}; | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | my $class = ref $self; | 
| 195 | 138 |  |  |  |  | 166 |  | 
| 196 |  |  |  |  |  |  | $self->log->debug("Constructing '$class' object for state $name"); | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | $self->state($name); | 
| 199 |  |  |  |  |  |  | $self->_factory($factory); | 
| 200 |  |  |  |  |  |  | $self->_actions( {} ); | 
| 201 |  |  |  |  |  |  | $self->_conditions( {} ); | 
| 202 | 138 |  |  | 138 | 1 | 209 | $self->_next_state( {} ); | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | # Note this is the workflow type. | 
| 205 | 138 |  | 33 |  |  | 243 | $self->type( $config->{type} ); | 
| 206 | 138 |  |  |  |  | 189 | $self->description( $config->{description} ); | 
| 207 |  |  |  |  |  |  |  | 
| 208 | 138 |  |  |  |  | 183 | if ( $config->{autorun} ) { | 
| 209 |  |  |  |  |  |  | $self->autorun( $config->{autorun} ); | 
| 210 | 138 |  |  |  |  | 342 | } else { | 
| 211 |  |  |  |  |  |  | $self->autorun('no'); | 
| 212 | 138 |  |  |  |  | 44414 | } | 
| 213 | 138 |  |  |  |  | 1724 | if ( $config->{may_stop} ) { | 
| 214 | 138 |  |  |  |  | 1253 | $self->may_stop( $config->{may_stop} ); | 
| 215 | 138 |  |  |  |  | 1229 | } else { | 
| 216 | 138 |  |  |  |  | 1195 | $self->may_stop('no'); | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  | foreach my $state_action_config ( @{ $config->{action} } ) { | 
| 219 | 138 |  |  |  |  | 1176 | my $action_name = $state_action_config->{name}; | 
| 220 | 138 |  |  |  |  | 1366 | $self->log->debug("Adding action '$action_name' to '$class' '$name'"); | 
| 221 |  |  |  |  |  |  | $self->_add_action_config( $action_name, $state_action_config ); | 
| 222 | 138 | 100 |  |  |  | 1172 | } | 
| 223 | 9 |  |  |  |  | 13 | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 | 129 |  |  |  |  | 346 | my ( $self, $action_name, $resulting ) = @_; | 
| 226 |  |  |  |  |  |  | my $name          = $self->state; | 
| 227 | 138 | 50 |  |  |  | 232 | my @errors        = (); | 
| 228 | 0 |  |  |  |  | 0 | my %new_resulting = (); | 
| 229 |  |  |  |  |  |  | foreach my $map ( @{$resulting} ) { | 
| 230 | 138 |  |  |  |  | 227 | if ( not $map->{state} or not defined $map->{return} ) { | 
| 231 |  |  |  |  |  |  | push @errors, | 
| 232 | 138 |  |  |  |  | 162 | "Must have both 'state' ($map->{state}) and 'return' " | 
|  | 138 |  |  |  |  | 313 |  | 
| 233 | 153 |  |  |  |  | 481 | . "($map->{return}) keys defined."; | 
| 234 | 153 |  |  |  |  | 308 | } elsif ( $new_resulting{ $map->{return} } ) { | 
| 235 | 153 |  |  |  |  | 33540 | push @errors, "The 'return' value ($map->{return}) must be " | 
| 236 |  |  |  |  |  |  | . "unique among the resulting states."; | 
| 237 |  |  |  |  |  |  | } else { | 
| 238 |  |  |  |  |  |  | $new_resulting{ $map->{return} } = $map->{state}; | 
| 239 |  |  |  |  |  |  | } | 
| 240 | 10 |  |  | 10 |  | 28 | } | 
| 241 | 10 |  |  |  |  | 29 | if ( scalar @errors ) { | 
| 242 | 10 |  |  |  |  | 90 | workflow_error "Errors found assigning 'resulting_state' to ", | 
| 243 | 10 |  |  |  |  | 20 | "action '$action_name' in state '$name': ", join '; ', @errors; | 
| 244 | 10 |  |  |  |  | 17 | } | 
|  | 10 |  |  |  |  | 27 |  | 
| 245 | 20 | 50 | 33 |  |  | 124 | $self->log->debug( "Assigned multiple resulting states in '$name' and ", | 
|  |  | 50 |  |  |  |  |  | 
| 246 | 0 |  |  |  |  | 0 | "action '$action_name' from array ok" ); | 
| 247 |  |  |  |  |  |  | return \%new_resulting; | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 0 |  |  |  |  | 0 | my ( $self, $action_name, $resulting ) = @_; | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | if ( my $resulting_type = ref $resulting ) { | 
| 253 | 20 |  |  |  |  | 79 | if ( $resulting_type eq 'ARRAY' ) { | 
| 254 |  |  |  |  |  |  | $resulting | 
| 255 |  |  |  |  |  |  | = $self->_assign_next_state_from_array( $action_name, | 
| 256 | 10 | 50 |  |  |  | 35 | $resulting ); | 
| 257 | 0 |  |  |  |  | 0 | } | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  |  | 
| 260 | 10 |  |  |  |  | 36 | return $resulting; | 
| 261 |  |  |  |  |  |  | } | 
| 262 | 10 |  |  |  |  | 2978 |  | 
| 263 |  |  |  |  |  |  | my ( $self, $action_name, $action_config ) = @_; | 
| 264 |  |  |  |  |  |  | my $state = $self->state; | 
| 265 |  |  |  |  |  |  | unless ( $action_config->{resulting_state} ) { | 
| 266 | 153 |  |  | 153 |  | 228 | my $no_change_value = Workflow->NO_CHANGE_VALUE; | 
| 267 |  |  |  |  |  |  | workflow_error "Action '$action_name' in state '$state' does not ", | 
| 268 | 153 | 100 |  |  |  | 309 | "have the key 'resulting_state' defined. This key ", | 
| 269 | 10 | 50 |  |  |  | 91 | "is required -- if you do not want the state to ", | 
| 270 | 10 |  |  |  |  | 48 | "change, use the value '$no_change_value'."; | 
| 271 |  |  |  |  |  |  | } | 
| 272 |  |  |  |  |  |  | # Copy the action config, | 
| 273 |  |  |  |  |  |  | # so we can delete keys consumed by the state below | 
| 274 |  |  |  |  |  |  | my $copied_config   = { %$action_config }; | 
| 275 |  |  |  |  |  |  | my $resulting_state = delete $copied_config->{resulting_state}; | 
| 276 | 153 |  |  |  |  | 331 | my $condition       = delete $copied_config->{condition}; | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | # Removes 'resulting_state' key from action_config | 
| 279 |  |  |  |  |  |  | $self->_next_state->{$action_name} = | 
| 280 | 153 |  |  | 153 |  | 281 | $self->_create_next_state( $action_name, $resulting_state ); | 
| 281 | 153 |  |  |  |  | 313 |  | 
| 282 | 153 | 50 |  |  |  | 1672 | # Removes 'condition' key from action_config | 
| 283 | 0 |  |  |  |  | 0 | $self->_conditions->{$action_name} = [ | 
| 284 | 0 |  |  |  |  | 0 | $self->_create_condition_objects( $action_name, $condition ) | 
| 285 |  |  |  |  |  |  | ]; | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | $self->_actions->{$action_name} = $copied_config; | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | my ( $self, $action_name, $action_conditions ) = @_; | 
| 291 | 153 |  |  |  |  | 581 | my @conditions = $self->normalize_array( $action_conditions ); | 
| 292 | 153 |  |  |  |  | 257 | my @condition_objects = (); | 
| 293 | 153 |  |  |  |  | 189 | my $count             = 1; | 
| 294 |  |  |  |  |  |  | foreach my $condition_info (@conditions) { | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 153 |  |  |  |  | 299 | # Special case: a 'test' denotes our 'evaluate' condition | 
| 297 |  |  |  |  |  |  | if ( $condition_info->{test} ) { | 
| 298 |  |  |  |  |  |  | my $state  = $self->state(); | 
| 299 |  |  |  |  |  |  | push @condition_objects, | 
| 300 | 153 |  |  |  |  | 1384 | Workflow::Condition::Evaluate->new( | 
| 301 |  |  |  |  |  |  | {   name  => "_$state\_$action_name\_condition\_$count", | 
| 302 |  |  |  |  |  |  | class => 'Workflow::Condition::Evaluate', | 
| 303 |  |  |  |  |  |  | test  => $condition_info->{test}, | 
| 304 | 153 |  |  |  |  | 1339 | } | 
| 305 |  |  |  |  |  |  | ); | 
| 306 |  |  |  |  |  |  | $count++; | 
| 307 |  |  |  |  |  |  | } else { | 
| 308 | 153 |  |  | 153 |  | 208 | $self->log->info( | 
| 309 | 153 |  |  |  |  | 331 | "Fetching condition '$condition_info->{name}'"); | 
| 310 | 153 |  |  |  |  | 182 | push @condition_objects, | 
| 311 | 153 |  |  |  |  | 155 | $self->_factory() | 
| 312 | 153 |  |  |  |  | 217 | ->get_condition( $condition_info->{name}, $self->type() ); | 
| 313 |  |  |  |  |  |  | } | 
| 314 |  |  |  |  |  |  | } | 
| 315 | 88 | 100 |  |  |  | 182 | return @condition_objects; | 
| 316 | 16 |  |  |  |  | 64 | } | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | my ( $self, $action_name ) = @_; | 
| 319 |  |  |  |  |  |  | unless ( $self->contains_action($action_name) ) { | 
| 320 |  |  |  |  |  |  | workflow_error "State '", $self->state, "' does not contain ", | 
| 321 |  |  |  |  |  |  | "action '$action_name'"; | 
| 322 |  |  |  |  |  |  | } | 
| 323 | 16 |  |  |  |  | 365 | } | 
| 324 | 16 |  |  |  |  | 57 |  | 
| 325 |  |  |  |  |  |  | 1; | 
| 326 | 72 |  |  |  |  | 136 |  | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | =pod | 
| 329 |  |  |  |  |  |  |  | 
| 330 | 72 |  |  |  |  | 15532 | =head1 NAME | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | Workflow::State - Information about an individual state in a workflow | 
| 333 | 153 |  |  |  |  | 419 |  | 
| 334 |  |  |  |  |  |  | =head1 VERSION | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | This documentation describes version 1.61 of this package | 
| 337 | 192 |  |  | 192 |  | 276 |  | 
| 338 | 192 | 50 |  |  |  | 329 | =head1 SYNOPSIS | 
| 339 | 0 |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | # This is an internal object... | 
| 341 |  |  |  |  |  |  | <workflow...> | 
| 342 |  |  |  |  |  |  | <state name="Start"> | 
| 343 |  |  |  |  |  |  | <action ... resulting_state="Progress" /> | 
| 344 |  |  |  |  |  |  | </state> | 
| 345 |  |  |  |  |  |  | ... | 
| 346 |  |  |  |  |  |  | <state name="Progress" description="I am in progress"> | 
| 347 |  |  |  |  |  |  | <action ... > | 
| 348 |  |  |  |  |  |  | <resulting_state return="0" state="Needs Affirmation" /> | 
| 349 |  |  |  |  |  |  | <resulting_state return="1" state="Approved" /> | 
| 350 |  |  |  |  |  |  | <resulting_state return="*" state="Needs More Info" /> | 
| 351 |  |  |  |  |  |  | </action> | 
| 352 |  |  |  |  |  |  | </state> | 
| 353 |  |  |  |  |  |  | ... | 
| 354 |  |  |  |  |  |  | <state name="Approved" autorun="yes"> | 
| 355 |  |  |  |  |  |  | <action ... resulting_state="Completed" /> | 
| 356 |  |  |  |  |  |  | ... | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | Each L<Workflow::State> object represents a state in a workflow. Each | 
| 361 |  |  |  |  |  |  | state can report its name, description and all available | 
| 362 |  |  |  |  |  |  | actions. Given the name of an action it can also report what | 
| 363 |  |  |  |  |  |  | conditions are attached to the action and what state will result from | 
| 364 |  |  |  |  |  |  | the action (the 'resulting state'). | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | =head2 Resulting State | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | The resulting state is action-dependent. For instance, in the | 
| 369 |  |  |  |  |  |  | following example you can perform two actions from the state 'Ticket | 
| 370 |  |  |  |  |  |  | Created' -- 'add comment' and 'edit issue': | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | <state name="Ticket Created"> | 
| 373 |  |  |  |  |  |  | <action name="add comment" | 
| 374 |  |  |  |  |  |  | resulting_state="NOCHANGE" /> | 
| 375 |  |  |  |  |  |  | <action name="edit issue" | 
| 376 |  |  |  |  |  |  | resulting_state="Ticket In Progress" /> | 
| 377 |  |  |  |  |  |  | </state> | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | If you execute 'add comment' the new state of the workflow will be the | 
| 380 |  |  |  |  |  |  | same ('NOCHANGE' is a special state). But if you execute 'edit issue' | 
| 381 |  |  |  |  |  |  | the new state will be 'Ticket In Progress'. | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | You can also have multiple return states for a single action. The one | 
| 384 |  |  |  |  |  |  | chosen by the workflow system will depend on what the action | 
| 385 |  |  |  |  |  |  | returns. For instance we might have something like: | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | <state name="create user"> | 
| 388 |  |  |  |  |  |  | <action name="create"> | 
| 389 |  |  |  |  |  |  | <resulting_state return="admin"    state="Assign as Admin" /> | 
| 390 |  |  |  |  |  |  | <resulting_state return="helpdesk" state="Assign as Helpdesk" /> | 
| 391 |  |  |  |  |  |  | <resulting_state return="*"        state="Assign as Luser" /> | 
| 392 |  |  |  |  |  |  | </action> | 
| 393 |  |  |  |  |  |  | </state> | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | So if we execute 'create' the workflow will be in one of three states: | 
| 396 |  |  |  |  |  |  | 'Assign as Admin' if the return value of the 'create' action is | 
| 397 |  |  |  |  |  |  | 'admin', 'Assign as Helpdesk' if the return is 'helpdesk', and 'Assign | 
| 398 |  |  |  |  |  |  | as Luser' if the return is anything else. | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | =head2 Autorun State | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | You can also indicate that the state should be automatically executed | 
| 403 |  |  |  |  |  |  | when the workflow enters it using the 'autorun' property. Note the | 
| 404 |  |  |  |  |  |  | slight change in terminology -- typically we talk about executing an | 
| 405 |  |  |  |  |  |  | action, not a state. But we can use both here because an automatically | 
| 406 |  |  |  |  |  |  | run state requires that one and only one action is available for | 
| 407 |  |  |  |  |  |  | running. That doesn't mean a state contains only one action. It just | 
| 408 |  |  |  |  |  |  | means that only one action is available when the state is entered. For | 
| 409 |  |  |  |  |  |  | example, you might have two actions with mutually exclusive conditions | 
| 410 |  |  |  |  |  |  | within the autorun state. | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | If no action or more than one action is available at the time the | 
| 413 |  |  |  |  |  |  | workflow enters an autorun state, Workflow will throw an error. There | 
| 414 |  |  |  |  |  |  | are some conditions where this might not be what you want. For example | 
| 415 |  |  |  |  |  |  | when you have a state which contains an action that depends on some | 
| 416 |  |  |  |  |  |  | condition. If it is true, you might be happy to move on to the next | 
| 417 |  |  |  |  |  |  | state, but if it is not, you are fine to come back and try again later | 
| 418 |  |  |  |  |  |  | if the action is available. This behaviour can be achived by setting the | 
| 419 |  |  |  |  |  |  | 'may_stop' property to yes, which will cause Workflow to just quietly | 
| 420 |  |  |  |  |  |  | stop automatic execution if it does not have a single action to execute. | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | =head1 PUBLIC METHODS | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | =head3 get_conditions( $action_name ) | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | Returns a list of L<Workflow::Condition> objects for action | 
| 427 |  |  |  |  |  |  | C<$action_name>. Throws exception if object does not contain | 
| 428 |  |  |  |  |  |  | C<$action_name> at all. | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | =head3 get_action( $workflow, $action_name ) | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | Returns an L<Workflow::Action> instance initialized using both the | 
| 433 |  |  |  |  |  |  | global configuration provided to the named action in the "action | 
| 434 |  |  |  |  |  |  | configuration" provided to the factory as well as any configuration | 
| 435 |  |  |  |  |  |  | specified as part of the listing of actions in the state of the | 
| 436 |  |  |  |  |  |  | workflow declaration. | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | =head3 contains_action( $action_name ) | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | Returns true if this state contains action C<$action_name>, false if | 
| 441 |  |  |  |  |  |  | not. | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | =head3 is_action_available( $workflow, $action_name ) | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | Returns true if C<$action_name> is contained within this state B<and> | 
| 446 |  |  |  |  |  |  | it matches any conditions attached to it, using the data in the | 
| 447 |  |  |  |  |  |  | context of the C<$workflow> to do the checks. | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | =head3 evaluate_action( $workflow, $action_name ) | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | Throws exception if action C<$action_name> is either not contained in | 
| 452 |  |  |  |  |  |  | this state or if it does not pass any of the attached conditions, | 
| 453 |  |  |  |  |  |  | using the data in the context of C<$workflow> to do the checks. | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | =head3 get_all_action_names() | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | Returns list of all action names available in this state. | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | =head3 get_available_action_names( $workflow, $group ) | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | Returns all actions names that are available given the data in | 
| 462 |  |  |  |  |  |  | C<$workflow>. Each action name returned will return true from | 
| 463 |  |  |  |  |  |  | B<is_action_available()>. | 
| 464 |  |  |  |  |  |  | $group is optional parameter. If it is set, additional check for group | 
| 465 |  |  |  |  |  |  | membership will be performed. | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | =head3 get_next_state( $action_name, [ $action_return ] ) | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | Returns the state(s) that will result if action C<$action_name> | 
| 470 |  |  |  |  |  |  | is executed. If you've specified multiple return states in the | 
| 471 |  |  |  |  |  |  | configuration then you need to specify the C<$action_return>, | 
| 472 |  |  |  |  |  |  | otherwise we return a hash with action return values as the keys and | 
| 473 |  |  |  |  |  |  | the action names as the values. | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | =head3 get_autorun_action_name( $workflow ) | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | Retrieve the action name to be autorun for this state. If the state | 
| 478 |  |  |  |  |  |  | does not have the 'autorun' property enabled this throws an | 
| 479 |  |  |  |  |  |  | exception. It also throws an exception if there are multiple actions | 
| 480 |  |  |  |  |  |  | available or if there are no actions available. | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | Returns name of action to be used for autorunning the state. | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | =head3 clear_condition_cache ( ) | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | Deprecated, kept for 1.61 compatibility. | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | Used to empties the condition result cache for a given state. | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | =head1 PROPERTIES | 
| 491 |  |  |  |  |  |  |  | 
| 492 |  |  |  |  |  |  | All property methods act as a getter and setter. For example: | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | my $state_name = $state->state; | 
| 495 |  |  |  |  |  |  | $state->state( 'some name' ); | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | B<state> | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | Name of this state (required). | 
| 500 |  |  |  |  |  |  |  | 
| 501 |  |  |  |  |  |  | B<description> | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | Description of this state (optional). | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | =head3 autorun | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | Returns true if the state should be automatically run, false if | 
| 508 |  |  |  |  |  |  | not. To set to true the property value should be 'yes', 'true' or 1. | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | =head3 may_stop | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | Returns true if the state may stop automatic execution silently, false | 
| 513 |  |  |  |  |  |  | if not. To set to true the property value should be 'yes', 'true' or 1. | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | =head1 INTERNAL METHODS | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  | =head3 init( $config ) | 
| 518 |  |  |  |  |  |  |  | 
| 519 |  |  |  |  |  |  | Assigns 'state', 'description', 'autorun' and 'may_stop' properties from | 
| 520 |  |  |  |  |  |  | C<$config>. Also assigns configuration for all actions in the state, | 
| 521 |  |  |  |  |  |  | performing some sanity checks like ensuring every action has a | 
| 522 |  |  |  |  |  |  | 'resulting_state' key. | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | =over | 
| 527 |  |  |  |  |  |  |  | 
| 528 |  |  |  |  |  |  | =item * L<Workflow> | 
| 529 |  |  |  |  |  |  |  | 
| 530 |  |  |  |  |  |  | =item * L<Workflow::Condition> | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | =item * L<Workflow::Factory> | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | =back | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 537 |  |  |  |  |  |  |  | 
| 538 |  |  |  |  |  |  | Copyright (c) 2003-2022 Chris Winters. All rights reserved. | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify | 
| 541 |  |  |  |  |  |  | it under the same terms as Perl itself. | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | Please see the F<LICENSE> | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | =head1 AUTHORS | 
| 546 |  |  |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | Please see L<Workflow> | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | =cut |