Acme-FSM
view release on metacpan or search on metacpan
=cut
my @options = qw| diag_level namespace source dumper |;
sub connect {
my( $self, $opts ) = ( shift @_, shift @_ );
ref $opts eq q|HASH| or croak sprintf
q|[connect]: {options} HASH is required, got (%s) instead|, ref $opts;
my( $trailer, $leader );
if( ref $self eq '' ) {
$trailer = ref $_[0] eq q|HASH|;
$self = bless { _ => { fst => $trailer ? shift @_ : { @_ }}}, $self;
$leader = q|clean init with| }
else {
$trailer = @_;
$self = bless { _ => { %{$self->{_}} }}, ref $self;
$leader = q|stealing| }
$self->{_}{$_} = delete $opts->{$_} // $self->{_}{$_} foreach @options;
$self->{_}{diag_level} //= 1;
$self->diag( 3, q|%s (%i) items in FST|,
$leader, scalar keys %{$self->{_}{fst}} );
$self->carp( qq|($_): unknown option, ignored| ) foreach keys %$opts;
$self->carp( q|(source): unset| ) unless defined $self->{_}{source};
$trailer = ref $_[0] eq q|HASH| ? keys %{$_[0]} : @_ if $trailer;
$self->carp( qq|ignoring ($trailer) FST items in trailer| ) if $trailer;
$self->carp( q|FST has no {START} state| ) unless
exists $self->{_}{fst}{START};
$self->carp( q|FST has no {STOP} state| ) unless
exists $self->{_}{fst}{STOP};
@{$self->{_}}{qw| state action |} = qw| START VOID |;
return $self }
=head1 B<process()>
$bb = Acme::FSM->connect( { }, \%fst );
$rc = $bb->process;
This is heart, brains, liver, and so on of B<Acme::FSM>.
B<process()> is what does actual state flow.
That state flow is steered by records in I<%fst>.
Each record consists of:
=over
=item B<switch()> callback
This is some user supplied code that
always consumes whatever B<source()> callback returns (at some point in past),
(optionally) regurgitates said input,
and decides what I<[turn]> to go next.
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>
If any (hence 'or') is present then I<$rule> returned by B<switch()> is
treated as Perl boolean, except C<undef> it's handled by C<uturn>.
That's how it is in original B<DMA::FSM>.
If B<switch()> always returns TRUE (or FALSE) then C<fturn> (or C<tturn>) can
be missing.
Also, see L<B<BUGS AND CAVEATS>|/tturn, fturn, and switch()>.
=item C<turns>
If neither C<tturn> nor C<fturn> is present then whatever I<$rule> would be
returned by B<switch()> is treated as string.
That string is supposed to be a key in I<%$turns>.
I<$rule> returned is forced to be string (so if you dare to return objects,
such objects should have C<""> overloaded).
Also, see L<B<BUGS AND CAEATS>|/Default For Turn Map>.
=back
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>
It's I<$state> set by B<connect()>.
I<$action> (it's also set by B<connect()>) is ignored
(if I<$action> is C<undef> then silently replaces with empty string),
B<switch()> is invoked with no arguments
(there's not anything to process yet).
Whatever I<$item> could be returned is ignored.
I<$rule> is followed.
Thus C<eturn> can't be followed.
See also L<B<BUGS AND CAVEATS>|/Special handling of START and CONTINUE>.
=item C<STOP>
It's last state in the state flow.
I<$action> is retained
(that's what B<process()> will return)
(however, because it's processed before C<STOP> state is acknowledged it must
not be C<undef>)
but otherwise ignored.
B<switch()> is invoked with no arguments
(B<switch()> of previous I<{state}> should have took care).
Whatever I<$item> could be returned is ignored.
Whatever I<$rule> could be returned is reported (at I<(basic trace)> level)
but otherwise ignored.
Then I<$action> is returned and state flow terminates.
=back
Supported I<$action>s (in alphabetical order):
=over
=item C<NEXT>
Drop whatever I<$item> at hands.
Request another.
If FST has such record:
somestate => { eturn => [ somestate => 'NEXT' ] }
then FSM will stay in C<somestate> as long as I<source()> callback returns
C<undef>.
Thus consuming all resources available.
No options provided to limit that consumption.
=item C<SAME>
Retains I<$item> uncoditionally.
That is, even if I<$item> isn't B<defined> it's kept anyway.
(also B<carp>s).
Also see below.
=item *
Empty string is returned if there're I<tturn> and/or I<fturn> turns.
I<turns> hash is ignored in that case.
=item *
C<HASH> is returned if there's turn map
(and neither I<tturn> nor I<fturn> is present).
B<(note)> In that case, B<turn()> checks for I<turns> is indeed a HASH,
nothing more
(however B<croaks> if that's not the case);
It may as well be empty;
Design legacy.
=item *
Returns C<HASH> for C<STOP> and C<BREAK> I<$state>s without any further
processing
(For those I<$state>s any I<$rule> is ignored and C<HASH> enables I<switch()>
callbacks to give more informative logs
(while that information is mangled anyway);
Probably bad idea).
=item *
C<undef> is returned if there's nothing to say --
neither I<tturn>, nor I<fturn>, nor turn map --
this record is kind of void.
The record should be studied to find out why.
B<carp>s in that case.
=back
=item query specific I<[turn]>
Two scalars are I<$state> and specially encoded I<$rule>
(refer to L<B<query_switch()> method|/query_switch()> about encoding).
If I<$rule> can't be decoded then B<croak>s.
Returns (after verification) requested I<$rule> as ARRAY.
While straightforward I<[turn]>s (such as C<tturn>, C<fturn>, and such) could
be in fact queried through L<B<fst()> method|/fst()> turn map needs bit more
sophisticated handling;
and that's what B<turn()> does;
in fact asking for C<turns> will result in B<croak>.
I<$action> of C<START> and C<CONTINUE> special states suffer implicit
defaulting to empty string.
=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 == @_ ) {
my $state = shift @_;
my $entry = $self->verify(
$self->{_}{fst}{$state}, $state, '', q|entry|, q|HASH| );
# WORKAROUND:201305070051:whynot: Otherwise there will be spurious B<carp>s about anyway useless turns in those entries.
$state eq q|STOP| || $state eq q|BREAK| and return q|HASH|;
exists $entry->{tturn} || exists $entry->{fturn} and return '';
unless( exists $entry->{turns} ) {
# XXX:201305071531:whynot: Should just B<croak> instead, probably.
$self->carp( qq|{$state}: none supported turn| );
return undef }
$self->verify( $entry->{turns}, $state, q|turns|, q|turn|, q|HASH| );
return q|HASH| }
elsif( 2 == @_ ) {
my( $state, $turn ) = @_;
my $entry;
$self->verify( $turn, $state, $turn, q|turn|, '' );
if( exists $special_turns{$turn} ) {
$entry = $self->{_}{fst}{$state}{$turn} }
elsif( !index $turn, q|turn%| ) {
$entry = $self->{_}{fst}{$state}{turns}{substr $turn, 5} }
else {
croak sprintf q|[turn]: {%s}(%s): unknown turn|, $state, $turn }
$self->verify( $entry, $state, $turn, q|turn|, q|ARRAY| );
$self->verify( $entry->[0], $state, $turn, q|state|, '' );
# XXX:20121230140241:whynot: {START}{turn}{action} is ignored anyway.
# XXX:201305072006:whynot: {CONTINUE}{turn}{action} is ignored too.
$entry->[1] //= '' if $state eq q|START| || $state eq q|CONTINUE|;
$self->verify( $entry->[1], $state, $turn, q|action|, '' );
return $entry }
else {
$self->carp( sprintf q|too many args (%i)|, scalar @_ );
return undef }
}
=item B<action()>
$bb->action eq $action and die;
$action = $bb->action( $new_action );
Queries and sets I<$action> of B<A::F> instance.
Modes:
=over
=item query I<$action>
No arguments -- returns current I<$action> of the instance.
Note, Perl FALSE B<isa> parameter.
=item set I<$action>
Don't verify if turn map exists.
B<(note)>
Don't verify if C<"turn%$rule"> exists in turn map.
=back
B<switch()> is always invoked in list context even if I<$item> would be
ignored.
If I<$rule> shouldn't be paired with I<$item> it won't be
(it's safe to call B<query_switch()> in scalar context then and
there won't be any trailing C<undef>s).
=cut
sub query_switch {
my $self = shift @_;
my @turn;
# WORKAROUND:20121229000801:whynot: No B<verify()>, B<query()> does its checks by itself.
@turn = $self->query(
$self->fst( $self->state, q|switch| ),
sprintf( q|{%s}{switch}|, $self->state ),
@_ ) if !@_ || defined $_[0];
my $kind = $self->turn( $self->state );
$turn[0] =
@_ && !defined $_[0] ? q|eturn| :
# TODO:202201071700:whynot: Make C<undef> special only when C<uturn> is present, plz.
!defined $turn[0] ? q|uturn| :
# FIXME:201304230145:whynot: Defaulting to basics here looks as bad as B<croak>ing.
# TODO:202212202039:whynot: L<Default For Turn Map>.
$kind ? qq|turn%$turn[0]| :
$turn[0] ? q|tturn| : q|fturn|;
return @_ ? @turn : $turn[0] }
=item B<query_source()>
( $item, $dump ) = $self->query_source;
Seeks B<source()> callback and acquires whatever it returns.
The callback is called in scalar context.
As useful feature, also feeds I<$item> to L<dumper callback|/query_dumper()>.
L<B<query()> method|/query()> has detailed description how B<source()>
callback is acquired.
Returns I<$item> and result of L<I<dumper> callback|/dumper>.
=cut
sub query_source {
my $self = shift @_;
# WORKAROUND:20121229001530:whynot: No B<verify()>, I<{source}> can return anything.
my $item = $self->query( $self->{_}{source}, q|{source}|, @_ );
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| },
q|{dumper}|, @_ ) // q|(unclear)|,
# XXX:202202210304:whynot: 'source' looks like remnants of refactoring. Should investigate it deeper.
$self->state, qw| source source |, '' ) }
=item B<diag()>
$bb->diag( 3, 'going to die at %i.', __LINE__ );
Internal.
Provides unified and single-point-of-failure way to output diagnostics.
Intensity is under control of
L<I<diag_level> configuration parameter|/diag_level>.
Each object has it's own,
however it's inherited when objects are copied.
Defined levels are:
=over
=item C<0>
Nothing at all.
Even error reporting is suppressed.
=item C<1>
Default.
Errors of here-be-dragons type.
=item C<2>
Basic diagnostics for callbacks.
=item C<3>
Basic trace.
Construction, starting and leaving runs.
=item C<4>
Extended diagnostics for callbacks.
=item C<5>
Deep trace.
By the way diagnostics of I<switch> entry resolving.
=back
=cut
Implementation wise that leads to B<source()> that on the fly figures out
current I<$state> and then somehow branches (sometimes branches out).
Such B<source()>s look horrible because they are.
=item Special handling of C<START> and C<CONTINUE>
B<(bug)>
In contrary with C<STOP> and C<BREAK> special states,
special treatement of C<START> and C<CONTINUE> is a side effect of
L<B<process()> method|/process()> entry sequence.
That's why routinely enter C<START> or C<CONTINUE> by state flow is of
there-be-dragons type.
Totally should be rewritten.
=item B<switch()> and I<$item>
B<(bug)>
It might be surprising, but, from experience,
the state flow mostly doesn't care about I<$item>.
Mostly, I<{state}> entered with I<$action> C<NEXT> processes input
and just wonders ahead.
Still those pesky I<$item>s *must* be passed around (by design).
Still every B<switch()> *must* return I<$item> too
(even if it's just a placeholder).
Something must be done about it.
=item C<tturn>, C<fturn>, and B<switch()>
B<(misdesign)>
First, by design and legacy B<switch()> must report what turn the control flow
must make
(that's why it *must* return I<$rule>).
OTOH, there's some value in keeping B<switch()>es as simple as possible what
results in I<{state}> with alone C<tturn> I<{turn}> and B<switch()> that
always returns TRUE.
Changes are coming.
=item B<turn()> and B<fst()>
B<(misdesign)>
Encoding (so to speak) in use by B<turn()> (in prediction mode) is plain
stupid.
C<undef> signals two distinct conditions
(granted, both are manifest of broken I<{fst}>).
Empty string doesn't distinguish safe (both C<tturn> and C<fturn> are present)
and risky (C<tturn> or C<fturn> is missing) I<{state}>.
C<HASH> doesn't say if there's anything in turn map.
All that needs loads of workout.
=back
=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>
B<(warning)>, L<B<connect()> method|/connect()>.
Each option that's not known to B<A::F> is ignored and B<carp>ed.
There're no means for child class to force B<A::F> (as a parent class) to
accept something.
Child classes should process their options by itself.
=item C<[connect]: clean init with (%i) items in FST>
B<(basic trace)>, L<B<connect()> method|/connect()>.
During class construction FST has been found.
That FST consists of I<%i> items.
Those items are number of I<$state>s and not I<$state>s plus I<{state}>s.
=item C<[connect]: FST has no {START} state>
B<(warning)>, L<B<connect()> method|/connect()>.
I<START> I<{state}> is required.
It's missing.
This situation will definetely result in devastating failure later.
=item C<[connect]: FST has no {STOP} state>
B<(warning)>, L<B<connect()> method|/connect()>.
I<STOP> I<{state}> is required.
It's missing.
If FST is such that I<STOP> I<$state> can't be reached
(for whatever reasons)
this may be cosmetic.
=item C<[connect]: ignoring (%i) FST items in trailer>
B<(warning)>, L<B<connect()> method|/connect()>.
A trailer has been found and was ignored.
Beware, I<%i> could be misleading.
For HASH in trailer that HASH is considered and key number is reported.
For list in trailer an B<item> number in list is reported, even if it's twice
as many in FST (what isa hash).
Just because.
=item C<[connect]: (source): unset>
B<(warning)>, L<B<connect()> method|/connect()>.
I<$source> option isa unset, it should be.
There's no other way to feed items in.
This situation will definetely result in devastating failure later.
=item C<[connect]: stealing (%i) items in FST>
B<(basic trace)>, L<B<connect()> method|/connect()>.
During object construction FST has been found.
That FST consists of I<%i> items.
Those items are I<{state}>s, not I<$state>s plus I<{state}>s.
=item C<[fst]: creating {%s} record>
B<(basic trace)>, L<B<fst()> method|/fst()>.
New state record has been created, named I<%s>.
=item C<[fst]: creating {%s}{%s} entry>
B<(basic trace)>, L<B<fst()> method|/fst()>.
New entry named I<%s> (the latter) has been created in record named I<%s> (the
former).
=item C<[fst]: no args>
B<(warning)>, L<B<fst()> method|/fst()>.
No arguments, it's an error.
However, instead of promptly dieing, C<undef> is returned
in blind hope this will be devastating enough.
=item C<[fst]: (%s): no such {fst} record>
B<(warning)>, L<B<fst()> method|/fst()>.
Requested entry I<%s> is missing.
State record ought to exist beforehand for any usecase except
L<record creation|/set {state} of specific $state>.
=item C<[fst]: too many args (%i)>
B<(warning)>, L<B<fst()> method|/fst()>.
Too many args have been passed in.
And again, instead of prompt dieing C<undef> is returned.
=item C<[fst]: updating {%s} record>
B<(basic trace)>, L<B<fst()> method|/fst()>.
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()>.
Trying to enter I<$state> I<%s> (the former) with I<$action> I<%s> (the
latter).
=item C<[process]: {%s}(%s): %s: going with>
B<(basic trace)>, L<B<process()> method|/process()>.
While in I<$state> I<%s> (1st) doing I<$action> I<%s> (2nd) the I<$item>
happens to be I<%s> (3rd).
C<undef> will look like this: C<((undef))>.
=item C<[process]: {%s}(%s): %s: turning with>
B<(basic trace)>, L<B<process()> method|/process()>.
B<switch()> (of I<$state> I<%s>, the 1st) has returned I<$rule> I<%s> (2nd)
and I<$item> I<%s>.
Now dealing with this.
=item C<[process]: {%s}(%s): leaving>
B<(basic trace)>, L<B<process()> method|/process()>.
Leaving state flow with I<$state> I<%s> (the former) and I<$action> I<%s> (the
latter).
=item C<[process]: {%s}(%s): switch returned: (%s)>
B<(basic trace)>, L<B<process()> method|/process()>.
B<switch()> (of I<$state> I<%s>, the 1st, with I<$action> I<%s>, the 2nd) has
been invoked and returned something that was interpreted as I<$rule> I<%s>,
the 3rd.
=item C<[process]: {%s}(%s): unknown action>
B<(croak)>, L<B<process()> method|/process()>.
Attempt to enter I<$state> I<%s> (the former) has been made.
However, it has failed because I<$action> I<%s> (the latter) isn't known.
=item C<< [query]: [$caller]: <%s> package can't [%s] subroutine >>
B<(croak)>, L<B<query()> method|/query()>.
I<$namespace> and I<$what> has been resolved as package I<%s> (the former)
and subroutine I<%s> (the latter).
However, the package can't do such subroutine.
=item C<[query]: [query_dumper]: %s !isa defined>
=item C<[query]: [query_source]: %s !isa defined>
=item C<[query]: [query_switch]: %s !isa defined>
B<(croak)>, L<B<query()> method|/query()>.
=item C<[query]: [query_source]: {namespace} !isa defined>
=item C<[query]: [query_switch]: {namespace} !isa defined>
B<(croak)>, L<B<query()> method|/query()>.
I<$what> isn't CODE, now to resolve it I<$namespace> is required.
Apparently, it was missing all along.
=item C<[query]: [query_dumper]: {namespace} isa (%s)>
=item C<[query]: [query_source]: {namespace} isa (%s)>
=item C<[query]: [query_switch]: {namespace} isa (%s)>
B<(deep trace)>, L<B<query()> method|/query()>.
C<ref $namespace> is I<%s>.
=item C<[query]: [query_dumper]: defaulting %s to $self>
=item C<[query]: [query_source]: defaulting %s to $self>
=item C<[query]: [query_switch]: defaulting %s to $self>
B<(deep trace)>, L<B<query()> method|/query()>.
I<$namespace> is an empty string.
The blackboard object will be used to resolve the callback.
=item C<< [query]: [query_dumper]: going for <%s>->[%s] >>
=item C<< [query]: [query_source]: going for <%s>->[%s] >>
=item C<< [query]: [query_switch]: going for <%s>->[%s] >>
B<(deep trace)>, L<B<query()> method|/query()>.
Attempting to call I<%s> (the latter) method on object of I<%s> (the former)
class.
=item C<< [query]: [query_dumper]: going for <%s>::[%s] >>
=item C<< [query]: [query_source]: going for <%s>::[%s] >>
=item C<< [query]: [query_switch]: going for <%s>::[%s] >>
B<(deep trace)>, L<B<query()> method|/query()>.
Attempting to call I<%s> (the latter) subrouting of package I<%s> (the
former).
=item C<< [query]: [query_dumper]: object of <%s> can't [%s] method >>
=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.
=item C<[turn]: (%s): no such {fst} record>
B<(warning)>, L<B<turn()> method|/turn()>.
Peeking for I<[turn]>s of I<%s> I<$state> yeilds nothing, there's no such
state.
=item C<[turn]: {%s}: none supported turn>
B<(warning)>, L<B<turn()> method|/turn()>.
Whatever content of I<%s> entry is FSM doesn't know how to handle it.
=item C<[turn]: {%s}(%s): unknown turn>
B<(croak)>, L<B<turn()> method|/turn()>.
There was request for I<[turn]> I<%s> (the latter) of I<$state> I<%s> (the
former).
While I<{state}> record has been found and is OK,
there is no such I<$rule>.
=item C<[turn]: no args>
B<(warning)>, L<B<turn()> method|/turn()>.
No argumets, it's an error.
=item C<[turn]: too many args (%i)>
B<(warning)>, L<B<turn()> method|/turn()>.
There's no way to handle that many (namely: I<%i>) arguments.
=item C<[verify]: {%s}{%s}: %s !isa defined>
B<(croak)>, L<B<verify()> method|/verify()>.
I<$rc> queried
from something in I<{fst}> related to I<%s> (3rd)
(value of which is I<%s> (2nd))
while in I<$state> I<%s> (1st)
isn't defined.
=item C<[verify]: {%s}{%s}: %s isa (%s), should be (%s)>
B<(croak)>, L<B<verify()> method|/verify()>.
B<ref> of I<$rc> queried
from something in I<{fst}> related to I<%s> (3rd)
(value of which is I<%s> (2nd))
while in I<$state> I<%s> (1st) is I<%s> (4th).
While it should be I<%s> (5th)
(the last one is literally I<$test>).
=back
( run in 2.282 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )