No description, website, or topics provided.
Pull request Compare This branch is 10 commits ahead of nothingmuch:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


    Class::Workflow - Light weight workflow system.

            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

                    name => "new",
                    transitions => [qw/accept reject/],

            # set it as the initial state


            # create a few more states

                    name => "open",
                    transitions => [qw/claim_fixed reassign/],

                    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

                    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

                    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,

    Workflow systems let you build a state machine, with transitions between

    There are several examples in the examples directory, worth looking over
    to help you understand and to learn some more advanced things.

    The most important example is probably how to store a workflow
    definition (the states and transitions) as well as the instances using
    DBIx::Class in a database.

  Bug Tracker Example
    One of the simplest examples of a workflow which you've probably used is
    a bug tracking application:

    The initial state is 'new'
    new New bugs arrive here.

        This bug is not valid.

        Target state: "rejected".

        This bug needs to be worked on.

        Target state: "open".

        This is the state where deleted bugs go, it has no transitions.

        The bug is being worked on right now.

        Pass the bug to someone else.

        Target state: "unassigned".

        The bug looks fixed, and needs verifification.

        Target state: "awaiting_approval".

        The bug is waiting for a developer to take it.

        Volunteer to handle the bug.

        Target state: "open".

        The submitter needs to verify the bug.

        The bug is resolved and can be closed.

        Target state: "closed"

        The bug needs more work.

        Target state: "open"

        This is, like rejected, an end state (it has no transitions).

    If you read through this very simple state machine you can see that it
    describes the steps and states a bug can go through in a bug tracking
    system. The core of every workflow is a state machine.

    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 this implementation, the instance is usually a consumer of
    Class::Workflow::Instance, typically Class::Workflow::Instance::Simple.

    So, when you write your MyBug class, it should look like this (if it
    were written in Moose):

            package MyBug;
            use Moose;

            has workflow_instance => (
                    does => "Class::Workflow::Instance", # or a more restrictive constraint
                    is   => "rw",

    Since this system is purely functional (at least if your transitions
    are), you need to always set the instance after applying a transition.

    For example, let's say you have a handler for the "accept" action, to
    change the instance's state it would do something like this:

            sub accept {
                    my $bug = shift;

                    my $wi = $bug->workflow_instance;
                    my $current_state = $wi->state;

                    # if your state supports named transitions      
                    my $accept = $current_state->get_transition( "accept" )
                            or die "There's no 'accept' transition in the current state";

                    my $wi_accepted = $accept->apply( $wi );

                    $bug->workflow_instance( $wi_accepted );

    Now let's decsribe some restrictions on this workflow.

    *   Only the submitter can approve the bug as resolved.

    *   Only the developer can claim the bug was fixed, and reassign the

    *   Any developer (but not the submitter) can accept a bug as valid,
        into the 'open' state.

    A workflow system will not only help in modelying the state machine, but
    also help you create restrictions on how states need to be changed, etc.

    The implementation of restrictions is explained after the next section.

    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.

    Imagine that we have a user class:

            package MyUser;

            has id => (
                    isa => "Num",
                    is  => "ro",
                    default => sub { next_unique_id() };

            has name => (

    We can create a context like this:

            package MyWorkflowContext;
            use Moose;

            extends "Class::Workflow::Context";

            has user => (
                    isa => "MyUser",
                    is  => "rw",

    to contain the "current" user.

    Then, when we apply the transition a bit differently:

            sub accept {
                    my ( $bug, $current_user ) = @_;

                    my $wi = $bug->workflow_instance;
                    my $current_state = $wi->state;

                    # if your state supports named transitions      
                    my $accept = $current_state->get_transition( "accept" )
                            or croak "There's no 'accept' transition in the current state";

                    my $c = MyWorkflowContext->new( user => $current_user );
                    my $wi_accepted = $accept->apply( $wi, $c );

                    $bug->workflow_instance( $wi_accepted );

    And the transition has access to our $c object, which references the
    current user.

    In order to implement the restrictions we specified above we need to
    know who the submitter and owner of the item are.

    For this we create our own instance class as well:

            package MyWorkflowInstance;
            use Moose;

            extends "Class::Workflow::Instance::Simple";

            has owner => (
                    isa => MyUser",
                    is  => "ro", # all instance fields should be read only

            has submitter => (
                    isa => MyUser",
                    is  => "ro", # all instance fields should be read only

    When the first instance is created the current user is set as the

    Then, as transitions are applied they can check for the restrictions.

    This is typically not done in the actual transition body, but rather in
    validation hooks. Class::Workflow::Transition::Validate provides a
    stanard hook, and Class::Workflow::Transition::Simple provides an even
    easier interface for this:

            my $fixed = Class::Workflow::Transition::Simple->new(
                    name          => 'fixed',
                    to_transition => $awaiting_approval,
                    validators    => [
                            sub {
                                    my ( $self, $instance, $c ) = @_;
                                    die "Not owner" unless $self->instance->owner->id == $c->user->id;
                    body => sub {
                            # ...

    Persistence in workflows involves saving the workflow instance as a
    relationship of the item whose state it represents, or even treating the
    instance as the actual item.

    In any case, right now there are no turnkey persistence layers

    A fully working DBIx::Class example can be found in the examples/dbic
    directory, but setup is manual. Serialization based persistence (with
    e.g. Storable) is trivial as well.

    See Class::Workflow::Cookbook for more details.

    Most of the Class::Workflow system is implemented using roles to specify
    interfaces with reusable behavior, and then ::Simple classes which mash
    up a bunch of useful roles.

    This means that you have a very large amount of flexibility in how you
    compose your state/transition objects, allowing good integration with
    most existing software.

    This is achieved using Moose, specifically Moose::Role.

    Class::Workflow objects are utility objects to help you create workflows
    and instances without worrying too much about the state and transition

    It's usage is overviewed in the "SYNOPSIS" section.

        These are the classes to instantiate with.

        They default to Class::Workflow::Instance::Simple,
        Class::Workflow::State::Simple and

        Instantiate the workflow

        Set the starting state of instances.

        Return all the registered states or transitions.

        Return all the registered state or transition names.

        These two methods create update or retrieve state or transition

        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:
                        name       => "foo",
                        validators => [ sub { ... } ],

                # also works with implicit name:
                $wf->state( foo =>
                        validators  => [ sub { ... } ],

        (wherever ->state is used ->transition can also be used).

        Additionally, whenever you construct a state like this:

                        name        => "foo",
                        transitions => [qw/t1 t2/],

        the parameters are preprocessed so that it's as if you called:

                my @transitions = map { $wf->state($_) } qw/t1 t2/;
                        name        => "foo",
                        transitions => [@transitions],

        so you don't have to worry about creating objects first.

    add_state $name, $object
    add_transition $name, $object
        Explicitly register an object by the name $name.

    delete_state $name
    delete_transition $name
        Remove an object by the name $name.

        Note that this will NOT remove the object from whatever other object
        reference it, so that:

                        name        => "foo",
                        transitions => ["bar"],


        will not remove the object that was created by the name "bar" from
        the state "foo", it's just that the name has been freed.

        Use this method with caution.

    rename_state $old, $new
    rename_transition $old, $new
        Change the name of an object.

    get_state $name
    get_transition $name
        Get the object by that name or return undef.

    create_state $name, @args
    create_transition $name, @args
        Call "construct_state" or "construct_transition" and then
        "add_state" or "add_transition" with the result.

    construct_state @args
    construct_transition @args
        Call ->new on the appropriate class.

    expand_attrs \%attrs
        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.

    autovivify_states @things
    autovivify_transitions @things
        Coerce every element in @things into an object by calling
        "$wf->state($thing)" or "$wf->transition($thing)".

    create_or_set_state %attrs
    create_or_set_transition %attrs
        If the object by the name $attrs{name} exists, update it's attrs,
        otherwise create a new one.

    Workflow - Chris Winters' take on workflows - it wasn't simple enough
    for me (factoring out the XML/factory stuff was difficult and I needed a
    much more dynamic system).

    <> - lots of explanation and
    lovely flash animations.

    Class::Workflow::YAML - load workflow definitions from YAML files.

    Class::Workflow::Transition::Simple, Class::Workflow::State::Simple,
    Class::Workflow::Instance::Simple - easy, useful classes that perform
    all the base roles.


    This module is maintained using Darcs. You can get the latest version
    from <>, and use "darcs
    send" to commit changes.

    Yuval Kogman <>

            Copyright (c) 2006-2008 Infinity Interactive, Yuval Kogman. All rights
            reserved. This program is free software; you can redistribute
            it and/or modify it under the same terms as Perl itself.