use Class::Workflow;
# ***** NOTE *****
#
# This is a pretty long and boring example
#
# you probably want to see some flashy flash videos, so look in SEE ALSO
# first ;-)
#
# ****************
# a workflow object assists you in creating state/transition objects
# it lets you assign symbolic names to the various objects to ease construction
my $wf = Class::Workflow->new;
# ( you can still create the state, transition and instance objects manually. )
# create a state, and set the transitions it can perform
$wf->state(
name => "new",
transitions => [qw/accept reject/],
);
# set it as the initial state
$wf->initial_state("new");
# create a few more states
$wf->state(
name => "open",
transitions => [qw/claim_fixed reassign/],
);
$wf->state(
name => "rejected",
);
# transitions move instances from state to state
# create the transition named "reject"
# the state "new" refers to this transition
# the state "rejected" is the target state
$wf->transition(
name => "reject",
to_state => "rejected",
);
# create a transition named "accept",
# this transition takes a value from the context (which contains the current acting user)
# the context is used to set the current owner for the bug
$wf->transition(
name => "accept",
to_state => "opened",
body => sub {
my ( $transition, $instance, $context ) = @_;
return (
owner => $context->user, # assign to the use who accepted it
);
},
);
# hooks are triggerred whenever a state is entered. They cannot change the instance
# this hook calls a hypothetical method on the submitter object
$wf->state( "reject" )->add_hook(sub {
my ( $state, $instance ) = @_;
$instance->submitter->notify("Your item has been rejected");
});
# the rest of the workflow definition is omitted for brevity
# finally, use this workflow in the action that handles bug creation
sub new_bug {
my ( $submitter, %params ) = @_;
return $wf->new_instance(
submitter => $submitter,
%params,
);
}

On the implementation side, the core idea is that every "item" in the system (in our example, a bug) has a workflow instance. This instance represents the current position of the item in the workflow, along with history data (how did it get here).

In order to implement these restrictions cleanly you normally use a context object (a default one is provided in Class::Workflow::Context but you can use anything).

This is typically the first (and sometimes only) argument to all transition applications, and it describes the context that the transition is being applied in, that is who is applying the transition, what are they applying it with, etc etc.

In our bug system we typically care about the user, and not much else.

These two methods create update or retrieve state or transition objects.

They have autovivification semantics for ease of use, and are pretty lax in terms of what they accept.

More formal methods are presented below.

They have several forms:

$wf->state("foo"); # get (and maybe create) a new state with the name "foo"
$wf->state( foo => $object ); # set $object as the state by the name "foo"
$wf->state( $object ); # register $object ($object must support the ->name method )
# create or update the state named "foo" with the following attributes:
$wf->state(
name => "foo",
validators => [ sub { ... } ],
);
# also works with implicit name:
$wf->state( foo =>
validators => [ sub { ... } ],
);

This is used by create_or_set_state and create_or_set_transition, and will expand the attrs by the names to_state, transition and transitions to be objects instead of string names, hash or array references, by calling autovivify_transitions or autovivify_states.

In the future this method might be more aggressive, expanding suspect attrs.