Jim Thomason > Basset-1.04 > Basset::Machine

Download:
Basset-1.04.tar.gz

Dependencies

Annotate this POD

CPAN RT

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

NAME ^

Basset::Machine - used to state machines

AUTHOR ^

Jim Thomason, jim@jimandkoka.com

DESCRIPTION ^

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.

SYNOPSIS ^

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;
 }
 
 1;
 
 ---
 
 #!/usr/bin/perl
 use My::Machine;
 
 My::Machine->execute;

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

ATTRIBUTES ^

state

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.

heap

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

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.

 My::Machine->transitions({
        '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');
 }
reentry_is_fatal

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.

extractor

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

METHODS ^

execute

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

 My::Machine->execute();

is the same as:

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

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

run

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

 $machine->run;
setup

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.

teardown

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

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

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

interrupt

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.

abort

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.

machine

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

transition

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

 $m->transition('login');
extract

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

syntax highlighting: