Documentation
NAME
Tinky::Declare - Declarative creation of Tinky machines
SYNOPSIS
This is the functional equivalent to the Tinky synopsis:
use Tinky;
use Tinky::Declare;
class Ticket does Tinky::Object {
has Str $.ticket-number = (^100000).pick.fmt("%08d");
has Str $.owner;
}
my $workflow = workflow 'ticket-workflow', {
initial-state 'new';
workflow-state 'new';
workflow-state 'open';
workflow-transition 'open', 'new', 'open';
workflow-state 'rejected' , {
on-enter -> $object {
say "** sending rejected e-mail for Ticket '{ $object.ticket-number }' **";
}
}
workflow-transition 'reject', 'new', 'rejected';
workflow-transition 'reject', 'open','rejected';
workflow-transition 'reject', 'stalled','rejected';
workflow-state 'in-progress';
workflow-state 'stalled';
workflow-transition 'stall', 'open', 'stalled';
workflow-transition 'stall', 'in-progress', 'stalled', {
on-apply-transition -> $object {
say "** rescheduling tickets for '{ $object.owner }' on ticket stall **";
}
}
workflow-state 'completed';
workflow-transition 'unstall', 'stalled', 'in-progress';
workflow-transition 'take', 'open', 'in-progress';
workflow-transition 'complete', 'open', 'complete';
workflow-transition 'complete', 'in-progress', 'complete';
on-transition -> ( $transition, $object ) {
say "Ticket '{ $object.ticket-number }' went from { $transition.from.name }' to '{ $transition.to.name }'";
}
on-final-state -> ( $state, $object) {
say "** updating performance stats with Ticket '{ $object.ticket-number }' entered State '{ $state.name }'"
}
};
my $ticket-a = Ticket.new(owner => "Operator A");
$ticket-a.apply-workflow($workflow);
$ticket-a.open;
$ticket-a.take;
$ticket-a.next-states>>.name.say;
$ticket-a.state = $workflow.state('stalled');
$ticket-a.reject;
DESCRIPTION
This provides a declarative interface to create Tinky 'workflow' objects. You probably want to familiarise yourself with the Tinky documentation to get an idea of what is going on under the hood.
Essentially it creates a small DSL that allows you to create a Tinky::Workflow populated with the State and Transition objects that describe the workflow. Because the underlying objects are created for you only those features of Tinky that don't require sub-classing are exposed, such as tapping the supplies for leaving and entering a state (or all states,) the application of a transition (or all transitions,) and application of the workflow to an object, as well as validation callbacks for all of those events.
class Tinky::Declare::Workflow
This is a sub-class of Tinky::Workflow that provides some extra functionality that is required to create and find the states and transitions by name.
method state
method state(
Str:D $state
) returns Tinky::State
Gets the named state or undefined type object
method states
method states() returns Positional
This over-rides the Tinky::Workflow because the states will be populated as they are seen. The behaviour of the base version in the absence of states or transitions is undesirable.
method get-state
method get-state(
Str:D $state
) returns Tinky::State
Returns either an existing state with the specified name or a new one which will be added to the C collection
class Tinky::Declare::Workflow::X::Tinky::DuplicateTransition
This exception will be thrown if an attempt is made to define a transition which has the same 'from' and 'to' states as an existing one but with a differing name. Transitions must be unique by 'from' and 'to' state
method get-transition
method get-transition(
Str $name,
Str $from,
Str $to
) returns Tinky::Transition
Returns either an existing transition with the specified 'from' and 'to' states or creates a new one with 'name', 'from' and 'to' An exception will be thrown if an existing transition exists with a different name for the same 'from' and 'to' states
has Tinky::State $.initial-state
Read/Write version of initial state
Workflow definition
These routines define the workflow.
sub workflow
sub workflow(
Str $name,
&declare-workflow
) returns Tinky::Declare::Workflow
This returns the workflow that is defined, all of the other routines must be called within the block passed to this one.
class Tinky::Declare::X::Tinky::Declare::NoWorkflow
This exception is thrown if any of the following routines are called outside of the block of "workflow" above.
sub initial-state
sub initial-state(
Str $name
) returns Nil
Defines the initial state for objects that have the workflow applied if they do not already have a defined state. If this is not set for a given workflow then the first defined stated will be used instead.
sub on-enter-state
sub on-enter-state(
&on-enter-tap
) returns Nil
This defines a routine that will be called whenever any state is entered by any object in the workflow. The routine will be called with the Tinky::State object and the object that changed state. If you only want the "enters" for a specific State then it may be simpler to use "on-enter" defined in a "workflow-state" block. This can be called multiple times to add different handlers.
sub on-leave-state
sub on-leave-state(
&on-leave-tap
) returns Nil
This defines a routine that will be called whenever any state is left by any object in the workflow. The routine will be called with the Tinky::State object and the object that changed state. If you only want the "leaves" for a specific State then it may be simpler to use "on-leave" defined in a "workflow-state" block. This can be called multiple times to add different handlers.
sub on-final-state
sub on-final-state(
&on-final-tap
) returns Nil
This defines a routine that will be called when a "final" state is entered by any object in the workflow. A final state is one for which there is no transition from the state to another one. The routine will be called with the Tinky::State object and the object that changed state. This can be called multiple times to add different handlers.
sub on-apply
sub on-apply(
&on-applied-tap
) returns Nil
This defines a routine that will be called whenever a new object has the workflow applied, it will be called with the object as an argument after the initial state has been applied. This can be called multiple times to add different handlers.
sub on-transition
sub on-transition(
&on-transition-tap
) returns Nil
This defines a routine that will be called with a Tinky::Transition object and the object that is changing state whenever any object changes state. This may be useful for logging for instance. If you want to act on the application of a particular transition then it may be more convenient to define a handler within a specific 'workflow-transition'. This can be used multiple times to add different handlers.
sub validate-apply
sub validate-apply(
Callable $validate-apply where { ... }
) returns Nil
This defines a routine that is called before the workflow is applied to an object in order to check whether the application is valid, the routine must explicitly comply with the Tinky::ValidateCallback subset, that is to say it must explicity have a single 'Tinky::Object' argument and return a Bool. The type of the argument can be a more specific type that does the Tinky::Object role which will only be called with objects of that type so you can define different validators for different types.
multi sub workflow-state
multi sub workflow-state(
Str $name
) returns Mu
This defines a Tinky::State with the specified name and without any specific behaviour. These can be specified in any order as whenever a state is referred to by name elsewhere it will be created in the workflow if it doesn't already exist.
multi sub workflow-state
multi sub workflow-state(
Str $name,
&declare-state
) returns Mu
This defines a Tinky::State with the specified name and with the behaviours as defined defined in the block.
class Tinky::Declare::X::Tinky::Declare::NoState
This exception will be thrown if any of the following routines are used outside the 'workflow-state' block.
sub on-enter
sub on-enter(
&enter-tap
) returns Mu
This defines a routine that will be called with the Tinky::Object whenever an object enters this state. This can be used multiple times to define multiple handlers, however no guarantee is made as to the order in which they may be called, so some care may be required if you are altering the object at all.
sub validate-enter
sub validate-enter(
Callable $validate-enter where { ... }
) returns Mu
Define a validator for entry into the state. As with the validate-apply for the workflow, this must have a specific signature that has a single positional parameter of a Tinky::Object (or a more specific type that does that role,) and returns a Bool. This can be specified multiple times with different more specific types and only those where the type of the object matches the signature will be called. If any of the validators called returns False and exception will be thrown before the transition is applied.
sub on-leave
sub on-leave(
&leave-tap
) returns Mu
This defines a routine that will be called with the Tinky::Object whenever an object leaves this state. This can be used multiple times to define multiple handlers, however no guarantee is made as to the order in which they may be called, so some care may be required if you are altering the object at all.
sub validate-leave
sub validate-leave(
Callable $validate-leave where { ... }
) returns Mu
Define a validator for leaving the state. As with the validate-apply for the workflow, this must have a specific signature that has a single positional parameter of a Tinky::Object (or a more specific type that does that role,) and returns a Bool. This can be specified multiple times with different more specific types and only those where the type of the object matches the signature will be called. If any of the validators called returns False and exception will be thrown before the transition is applied.
multi sub workflow-transition
multi sub workflow-transition(
Str $name,
Str $from,
Str $to
) returns Mu
Define a Tinky::Transition between the two named states without any specific behavours. If the named states have not already been defined they will be created. The name of the transition does not need to be unique, the name of the transitions will be used to create methods on the Tinky::Object when the workflow is applied which determine which actual transition to apply by comparing the current state of the object with the 'from' state of the similarly named transitions. If there is a transition already defined with a different name but with the same 'from' and 'to' states then an exception will be thrown.
multi sub workflow-transition
multi sub workflow-transition(
Str $name,
Str $from,
Str $to,
&declare-transition
) returns Mu
Define a Tinky::Transition between the two named states with the behaviour defined in the supplied block. The same constraints on naming and the 'from' and 'to' states as described above apply.
class Tinky::Declare::X::Tinky::Declare::NoTransition
This exception will be thrown if any of the following routines are called outside a 'workflow-transition' block.
sub on-apply-transition
sub on-apply-transition(
&apply-transition
) returns Mu
This defines a tap on the transition's supply which will have the Tinky::Object which has changed its state emitted after the transition has been fully applied. This can be used multiple times, but altering the object should be handled with care as no guarantee is made as to the order the handlers will be executed.
sub validate-apply-transition
sub validate-apply-transition(
Callable $validate-apply where { ... }
) returns Mu
Define a validator for the application of the transition. As with the validate-apply for the workflow, this must have a specific signature that has a single positional parameter of a Tinky::Object (or a more specific type that does that role,) and returns a Bool. This can be specified multiple times with different more specific types and only those where the type of the object matches the signature will be called. If any of the validators called returns False and exception will be thrown before the transition is applied.