Acme-FSM
    
    
  
  
  
view release on metacpan or search on metacpan
Except when in special I<{state}> (see below) it's supplied with one argument:
I<$item>.
In special states I<$item> is missing.
B<switch()> returns two controls:  I<$rule> and processed I<$item>.
What to do with returned I<$item> is determined by I<$action> (see below).
=item various I<[turn]>s
Each I<[turn]> spec is an ARRAY with two elements (trailing elements are
ignored).
First element is I<$state> to change to.
Second is I<$action> that sets what to do with I<$item> upon changing to named
I<$state>.
Here are known I<[turn]>s in order of logical treating decreasing.
=over
=item C<eturn>
That I<[turn]> will be choosen by FSM itself when I<$item> at hands is
C<undef>
(as returned by B<source()>).
I<switch()> isn't invoked -- I<$item> is void, there's nothing to call
I<switch()> with.
However, I<$action> may change I<$item>.
=item C<uturn>
That I<[turn]> will be choosen by FSM if I<$rule> is C<undef>
(as returned by B<switch()>).
Idea behind this is to give an FST an option to bailout.
In original B<DMA::FSM> that's not possible (except B<croak>ing, you know).
Also, see L<B<BUGS AND CAVEATS>|/Perl FALSE, undef, and uturn>.
=item C<tturn> and/or C<fturn>
I<$state> is treated as a key in FST hash, except when that's a special state.
Special states (in alphabetical order):
=over
=item C<BREAK>
Basically, it's just like C<STOP> I<$state>.
All comments there (see below) apply.
It's added to enable break out from loop, do something, then get back in loop.
I<$state> is changed to C<CONTINUE> just before returning to caller
(I<switch()> is called in C<BREAK> state).
The choice where to implicitly change state to C<CONTINUE> has been completely
arbitrary;
probably wrong.
=item C<CONTINUE>
Just like C<START> state (see below, all comments apply).
While C<BREAK> is turned to C<CONTINUE> implicitly no other handling is made.
=item C<START>
=item anything else
No arguments or more then two is an non-fatal error.
Returns C<undef> (with B<carp>).
=back
=cut
# TODO:202202172011:whynot: As soon as supported perl is young enough change it to smartmatch, plz.
my %special_turns = map { $_ => 1 } qw| eturn uturn tturn fturn |;
# TODO:202202162030:whynot: Consider more elaborate (informative) returns.
sub turn {
    my $self = shift @_;
    unless( @_                                       ) {
        $self->carp( q|no args| );         return undef }
    elsif( 1 == @_ && !exists $self->{_}{fst}{$_[0]} ) {
        $self->carp( qq|($_[0]): no such {fst} record| );
                                           return undef }
    elsif( 1 == @_                                   ) {
    return $item, $self->query_dumper( $item ) }
=item B<query_dumper()>
    $dump = $self->query_dumper( $item );
Seeks I<dumper> callback (L<configured at construction time|/dumper>).
If the callback wasn't configured uses simple hopefully informative and
C<undef> proof substitution.
Whatever the callback returns is checked to be B<defined>
(C<undef> is changed to C<"(unclear)">)
and then returned.
=cut
sub query_dumper                             {
    my $self = shift @_;
    return $self->verify(
      $self->query(
# TODO:202202210258:whynot: This is inefficient, defaulting should happen in B<connect()> instead.
        $self->{_}{dumper} // sub { sprintf q|(%s)|, $_[1] // q|undef| },
=cut
=head1 DIAGNOSTICS
=over
=item C<[action]: changing action: (%s) (%s)>
B<(deep trace)>, L<B<action()> method|/action()>.
Exposes change of I<$action> from previous (former I<%s>)
to current (latter I<%s>).
=item C<[action]: too many args (%i)>
B<(warning)>, L<B<action()> method|/action()>.
Obvious.
None or one argument is supposed.
=item C<[connect]: (%s): unknow option, ignored>
Named record (I<%s>) has been updated.
=item C<[fst]: updating {%s}{%s} entry>
B<(basic trace)>, L<B<fst()> method|/fst()>.
Named entry (I<%s>, the latter) in record I<%s> (the former) has been updated.
=item C<[process]: {%s}(%s): changing state: (CONTINUE)>
B<(basic trace)>, L<B<process()> method|/process()>.
Implicit change of state from C<BREAK> to C<CONTINUE> is about to happen.
=item C<[process]: {%s}(%s): entering>
B<(basic trace)>, L<B<process()> method|/process()>.
Entering state flow from I<$state> I<%s> (the former) with I<$action> I<%s>
(the latter).
=item C<[process]: {%s}(%s): going for>
B<(deep trace)>, L<B<process()> method|/process()>.
=item C<< [query]: [query_source]: object of <%s> can't [%s] method >>
=item C<< [query]: [query_switch]: object of <%s> can't [%s] method >>
B<(croak)>, L<B<query()> method|/query()>.
The object of I<%s> (the former) class can't do I<%s> (the latter) method.
=item C<[state]: changing state: (%s) (%s)>
B<(deep trace)>, L<B<state()> method|/state()>.
Exposes change of state from previous (former I<%s>)
to current (latter I<%s>).
=item C<[state]: too many args (%i)>
B<(warning)>, L<B<state()> method|/state()>.
Obvious.
None or one argument is supposed.
B<state()> has returned C<undef> in this case,
most probably will bring havoc in a moment.
t/base/query_switch.t view on Meta::CPAN
 [                      q|{switch} isa (CODE), {namespace} isa set|,
  [qw|                                                pass noise |],
  sub {                push_push( !0 ), { namespace => q|vesta| } },
  [                                                               ],
  sub {                       $bb->{RCS}, exists $bb->{CSSC}, $rc },
  [                                         q|dcvs|, '', q|tturn| ],
  { noted => qr.(?m)\Q[query_switch]: {START}{switch} isa (CODE). }   ],
 [    q|{switch} isa (CODE), {namespace} isa set, argument isa set|,
  [qw|                                                pass noise |],
  sub {                          undef, { namespace => q|vesta| } },
  [qw|                          allfusion_harvest_change_manager |],
  sub {                                @$bb{qw| RCS CSSC |}, @$rc },
  [qw|        so6 allfusion_harvest_change_manager tturn fastcst |],
  { noted => qr.(?m)\Q[query_switch]: {START}{switch} isa (CODE). }   ],
 [                 q|{switch} isa (), {namespace} !isa defined|,
  [qw|                                            fail noise |],
  sub {                                    q|snapshotcm|, { } },
  [                                                           ],
                                qr.\Q {namespace} !isa defined.,
  { noted => qr.(?m)\Q[query_switch]: {START}{switch} isa (). },      ],
 [    q|{switch} !isa defined method, {namespace} eq ()|,
  [qw|                               fail noise noise |],
  sub                                                 {
( run in 0.292 second using v1.01-cache-2.11-cpan-5dc5da66d9d )