PrePAN

Sign in to PrePAN

State::Tracker Manage states and execute callbacks for predefined states

Author
Sadrak@github
Date
URL
Status
In Review
Good

Synopsis

# some own flags ; using constants are not necessary
{
    my $progress_flag;
    use constant PROGRESS_DONE     => $progress_flag = 0;
    use constant PROGRESS_FIND     => 2**$progress_flag++;
    use constant PROGRESS_MD6SUM   => 2**$progress_flag++;
    use constant PROGRESS_SIGNAL   => 2**$progress_flag++;
}

# changing & checking states

my $progress = State::Tracker->new;

$progress << PROGRESS_FIND # activate
$progress >> PROGRESS_FIND # deactivate
$progress <> PROGRESS_FIND # toggle

$progress += PROGRESS_FIND # increment counter; activate if > 0
$progress -= PROGRESS_FIND # decrement counter, deactivate if < 0

$progress += {PROGRESS_FIND => 10} # increment counter by 10; activate if > 0
$progress -= {PROGRESS_FIND => 20} # decrement counter by 20, deactivate if < 0

$progress == PROGRESS_DONE # check all flag
$progress != PROGRESS_DONE # check all flag
$progress <= PROGRESS_FIND # check only flag active
$progress >= PROGRESS_FIND # check only flag inactive

# register callbacks

$progress->callback(              
    active   => PROGRESS_FIND,
    sub {
        my ($self, $event) = @_;
        ...
        return 0 if $disable_event;
        return 1; # event stay active
    },
);
$progress->callback(              
    active   => PROGRESS_FIND|PROGRESS_SIGNAL,
    sub { ... },
);
$progress->callback(
    active   => PROGRESS_FIND,
    inactive => PROGRESS_MD6SUM,
    sub { ... },
);
$progress->callback(              
    counter   => {PROGRESS_FIND => 30},  
    sub { ... },
);
$progress->callback(
    counter   => [{PROGRESS_FIND => 30}, {PROGRESS_MD6SUM => -20}],
    sub { ... },
);
$progress->callback(
    counter   => [{PROGRESS_FIND => {'>=' => 30}}, {PROGRESS_MD6SUM => {'!=' => -20}}],
    sub { ... },
);

# using in a loop

while ($progress != PROGRESS_DONE) { ... }

Description

= ROADMAP =

  • first oop, then overload

  • allow non binary states (dispatch internally)

= Description =

This Modul should help to manager many states in an complex environment.

You can define callbacks for all states & combinations at one place and later change only the $progress.

I use a very simple approach in an old project and i want to make a easy implementation for a new project.

The events only get fired if a related flag/counter is changed.

I am absolute unsure if there are problems with such a interface (overloading) or should i better use simple function calls ($progress->activate(PROGRESS_FIND);)?

Also the signes for setting & checks flags are my first thought, perhaps i should stick with the default binary operators (but i am most of the time confused with them).

Also i guess i need some modes:

  • What if someone registered a event with an exact counter like 10 and later increment this counter by 2 from 9 to 11. Should the event fired or not? (mode => 'singlestep')

    • Otherwise if he use exact counters, he should avoid incrementing by more than 1
  • What is someone want to disallow setting states again? Like PROGRESS_FIND is off and he set PROGRESS_FIND again to off? (mode => 'strict')

  • ggf. einen speed-mode, der davon ausgeht, das bereit nur bitfields genutzt werden?

Comments

The overloaded interface (<< >>, +=) is confusing.

It's not clear what a counter is and what incrementing it does.

Please provide a more detailed description of the problem that you are trying to solve and how this module helps solve it.
It looks like you're implementing a state machine as a cross-cutting feature. This seems like a useful thing. For instance, as various far-flung data structures in your application change, you could have callbacks update user-facing representations of execution status.

However, I agree with djerius: The overloaded operators, while conveniently terse, are too cryptic and thus will pose a maintenance obstacle. You should provide simple functions for these operations. Perhaps: st_on(PROGRESS_FIND), st_off(PROGRESS_FIND), st_toggle(PROGRESS_FIND).

I wouldn't worry about the case of someone incrementing by two beyond the state's callback threshold. Users might want to do this deliberately if, for instance, the integer represents a noncontinuous state in a FSM rather than a simple counter. If the user skips a trigger state, they should be trusted to know what they are doing. If they really want to trigger on such an operation, they can do so by storing the previous value of the counter and testing with < on any change.

Regarding someone turning on a state that is already on, or off a state that is already off, this should be allowed.

Whether you want to implement activation counts to make nested activations/deactivations work properly is up to you. I could see a case for it either way.

Either the programmer is being sloppy and is issuing spurious on/off commands, or they expect to be able to locally activate/deactivate states in nested scopes. In the former case, disallowing it would just make a bad situation worse by giving the programmer yet another thing to debug.

Again, I like this idea, and hope you publish it to CPAN.

Please sign up to post a review.