Acme-FSM
view release on metacpan or search on metacpan
=item I<$rule>
A scalar identifing a I<[turn]>.
One of opaque scalars returned by B<switch()> callback
(the other is processed (modified or not) I<$item>).
L<B<process()> method|/process()> description has more.
=item B<source()>
Special piece of user-code
(it's required at construction (L<B<connect()> method|/connect()>),
L<B<query_source()> method|/query_source()> describes how FSM reaches it).
Whatever it returns (in explicit scalar context) becomes I<$item>.
=item I<$state>
A scalar identifing a I<{state}> (see below) or parameter of I<{fsm}> (see
above).
=item state flow
=head1 B<connect()>
$bb1 = Acme::FSM->connect( { %options1 }, %fst1 );
$bb2 = Acme::FSM->connect( { %options2 }, { %fst2 } );
$bb3 = $bb2->connect( { %options3 } );
Creates a blackboard object.
Blackboard isa HASH, it's free to use except special C<_> key;
that key is for I<{fsm}> exclusively.
First parameter isa I<%$options>, it's required
(pass empty HASH if nothing to say).
Defined keys are:
=over
=item I<diag_level>
(positive integer)
Sets a diagnostic threshold.
It's meaning is covered in L<B<diag()> method|/diag()> documentation.
(scalar or C<CODE>)
Sets a source of items to process to be queried.
Required.
Refer to L<B<query_source()> method|/query_source()> documentation for
details.
=back
Second is FST (Finite State Table).
It's required for class construction and ignored (if any) for object
construction.
Difference between list and HASH is the former is copied into HASH internally;
the latter HASH is just saved as reference.
The FST is just borrowed from source object during object construction.
Thus, in the synopsis, I<$bb3> and I<$bb2> use references to HASH
I<%$fst2>.
An idea behind this is to minimize memory footprint.
OTOH, maninpulations with one HASH are effectevely manipulations with FST of
any other copy-object that borrowed that FST.
IOW, anything behind FST HASH of class construction or options HASH of object
construction isa trailer and B<carp>ed.
Obviously, there's no trailer in class construction with list FST.
=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;
=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.
B<(deep trace)>, L<B<query()> method|/query()>.
C<ref $what> (the former I<%s>) is I<%s> (the latter).
=item C<[query]: [query_dumper]: {namespace} !isa defined>
=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>.
t/base/connect.t view on Meta::CPAN
use version 0.77; our $VERSION = version->declare( v2.3.1 );
use t::TestSuite qw| :diag :wraps |;
use Test::More tests => 54;
use Acme::FSM;
our( $bb, $bback, $stderr );
$bb = eval { AFSMTS_class_wrap; 1 };
ok !$bb && $@ =~ m<{options} HASH is required>,
AFSMTS_croakson q|class, no {options}|;
my %common =
( fst => { },
state => q|START|,
action => q|VOID|,
diag_level => 10,
namespace => undef,
source => undef,
dumper => undef );
t/base/connect.t view on Meta::CPAN
is_deeply $bb->{_}, { %common, diag_level => 1 }, qq|$tag init done|;
like $stderr, qr<(?m)^\Q[connect]: FST has no {START} state>,
qq|$tag no {START} noted|;
like $stderr, qr<(?m)^\Q[connect]: FST has no {STOP} state>,
qq|$tag no {STOP} noted|;
$bback = $bb;
undef $bb;
$bb = eval { AFSMTS_object_wrap $bback; 1 };
ok !$bb && $@ =~ m<{options} HASH is required>,
AFSMTS_croakson q|object, no {options}|;
$tag = q|object, empty {options},|;
AFSMTS_object_wrap $bback, { };
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, diag_level => 1 }, qq|$tag init done|;
like $stderr, qr<(?m)^\Q[connect]: FST has no {START} state>,
qq|$tag no {START} noted|;
like $stderr, qr<(?m)^\Q[connect]: FST has no {STOP} state>,
qq|$tag no {STOP} noted|;
( run in 0.634 second using v1.01-cache-2.11-cpan-0a6323c29d9 )