Jim Thomason > Basset-1.04 > Basset::Machine



Annotate this POD


New  1
Open  0
View/Report Bugs
Module Version: 1.01   Source  


Basset::Machine - used to state machines


Jim Thomason, jim@jimandkoka.com


Basset::Machine implements a state machine. This is useful for any thing that requires a process flow. web applications, shell scripts, tk apps, you name it. Anything that you want that requires user interaction and a flow of control.


An example is best. Let's try a simple one.

 package My::Machine;
 use Basset::Object;
 Basset::Object->inherits(__PACKAGE__, 'machine');
 sub start {
        return shift->state('login');
 sub login {
        my $self = shift;
        my $heap = $self->heap;
        if ($heap->{'loggedin'}) {
                return $self->state('success');
        } else {
                return $self->state('prompt');
 sub prompt {
        my $self = shift;
        print "Please enter your username (must be 'bob'): ";
        chomp(my $name = <STDIN>);
        $self->{'heap'}->{'loggedin'} = 1 if $name eq 'bob';
        return $self->state('login');
 sub success {
        my $self = shift;
        print "You are logged in\n";
        return $self->terminate;
 use My::Machine;

Look at the http://www.bassetsoftware.com/perl/basset/tutorial for more info.



This is the current state of the machine. This is how you move around in the flow of your machine. The default start state is 'start'. If you don't provide a state when your machine starts executing, it will try to enter the start state. You may always provide the state that you want. It is traditional to return the next state from your current state.

 sub current_state {
        my $self = shift;
        return $self->state('next_state');

States are usually methods in your machine module. But, if you have a complicated state, you may put it into its own class, in a subdirectory of the Machine class. methods in the machine class take precedence over external states. External states are entered via their 'main' method.

 package My::Machine;
 sub some_state {
        return shift->state('login');
 package My::Machine::Login;
 sub main {
        my $self = shift;
        my $machine = $self->m;
        return $self->m->state('jump_pt');

All machines implicitly start in a setup state when they begin running, and end with a terminate state, if those are defined. Note that these states will be entered any time the machine starts or stops running, respectively, so you may need to explicitly check the current state as appropriate.

When entering a state, you receive 1 argument - the state you came from. You may receive additional arguments that the prior state handed in to you.

See Basset::Machine::State for more information.


The heap is a hashref that contains useful information that's local to the machine. You can think of it as a global namespace as far as the states are concerned, but local to the machine.

This is how data is passed from state to state.

 sub state1 {
        my $self = shift;
        $self->heap->{'value1'} = 'foo';
        return $self->state('state2');
 sub state2 {
        my $self = shift;
        print 'value1 is ', $self->heap->{'value1'}, "\n";
        return $self->terminate;

transitions provides a layer of insulation for you. Instead of explicitly specifying your machine's states in code (for example, in a web app where every html page needs to return the machine's state), you can instead define a transition. This allows you to hide the actual states from the external world. So you can re-define states as desired, but the transitions will always remain the same.

        'login' => 'login_prompt',
        'analyze' => 'analyze_2',       #changed from old 'analyze' method

You then invoke it via a transition call, instead of a state call.

 sub state {
        my $self = shift;
        return $self->transition('analyze');

object attribute, which defaults to true. Normally, this will prevent you from re-entering a state from itself. Most of the time, this means that you forgot to transition out of it at the end of the state.

Nonetheless, there are times when you may want to stay where you are. If you have a machine that functions that way, then make this attribute false, and best of luck to you.


Most machines tend to need extractors. So you have one for free here. Wrappered by the extract method, below.



convenience method which allows you to create and run a machine in one step.


is the same as:

 my $m = My::Machine->new();

Will return undef if the machine aborts or is not constructed, and the machine itself upon its termination.


Actually runs the machine, transitions states, does all the magic.


implicit state that executes when the machine starts running. Does not actually affect the current state of the machine (that is, you can check $self->state and it won't return 'setup'). By default, it just returns success and the machine then begins running.

This is a good place to do things like setup database connections, look up frequently used classes, cache data, etc. By default, you get your extractor attribute set to whatever's in your conf file.

If setup aborts, it will teardown the machine and nothing will run.


implicit state that executes when the machine stops running. Will receive no arguments if the machine terminates normally (terminate or interrupt), will receive the single word "aborted" if the machine is stopping due to an abort. Does not actually affect the last run state (that is, you can check $self->state and it won't return 'teardown'). By default, it just returns success and the machine is done running.

This is a good place to do things like close database connections, write things to disk, log messages, etc.


start is the only state that must be defined within the machine class itself. This super method is abstract and aborts the machine. You must override it.


terminate stops the machine normally and clears out the current state.


interrupt expects to be given a state. It will stop the machine from running, and advance it to the state that was provided. This is useful to temporarily suspend the machine and return to it later. Note that re-running the machine will cause setup to be re-run, and that you will still run teardown after the interrupt.


aborts the machine immediately, tears it down, and returns the error passed in. This should be used to report machine errors in place of ->error.


simply returns self. This is a convenience method to make states more readily interchangeable between methods and explicit state modules


transitions the machine to the next state, as per the transitions table.


Convenience method. Simply calls extract on your extractor attribute, if you have one.

syntax highlighting: