process definitions
Process definitions are documents that describe how a [business] process should orchestrate the flow of work among participants.
Participants are registered at start time usually. While process definitions are ‘parsed’ at their launch time. Each launch of a process definition creates a process instance.
Ruote turns process definitions into an abstract syntax tree (a ‘tree’ for short) at launch time. This tree gets interpreted during the workflow execution.
Process definitions in four “languages” are understood:
There is an interesting complementary technique, workflow generation.
A process definition contains 1 main process definition and may contain 1 or more sub-process definitions. For example:
Ruote.define 'test' do
subprocess :ref => 'analysis'
subprocess :ref => 'publication'
define 'analysis' do
participant :ref => 'alice'
participant :ref => 'bob'
end
define 'publication' do
participant :ref => 'charly'
end
end
Ruby process definitions
Some kind of Ruby DSL can be used to define processes :
Ruote.define 'my_def' do
participant :ref => 'alice'
participant :ref => 'bob'
end
One advantage of this ‘notation’ is that passing complex data structures from the process definition is possible :
Ruote.define 'my_def' do
set :field => 'items', :value => { 'cars' => 2, 'trucks' => 0 }
participant :ref => 'alice'
participant :ref => 'bravo'
end
Not necessarily a good practice but can come in handy, and remember that ruote will flatten that to something that is JSONifiable.
One interesting twist of Ruby process definitions: they are Ruby, so it’s code executed (at definition time). A process like:
Ruote.define 'my_def' do
cursor do
alice
bob
charly
doug
end
end
can be simplified to:
Ruote.define 'my_def' do
cursor do
%w[ alice bob charly doug ].collect { |name| __send__(name) }
end
end
…
Radial process definitions
Radial is a mini-language introduced with ruote 2.3.0. It looks like a cross of Python and JSON, but is very similar to definitions expressed in the usual Ruby DSL (without the definition time execution).
The above process definition in Ruby would like this in radial:
define my_def
set field: items, value: { cars: 2, trucks: 0 }
participant ref: alice
participant ref: bravo
Radial is very accomodating, no need to enclose one word strings in quotes for examples. Indentation matters though (a bit like in Python).
No possibility for running Ruby code at definition, but a lighter weight syntax.
Quotes are OK (and they are necessaring for more than word strings or forcing numbers to strings:
define my_def
set field: items, value: { cars: '2', trucks: 0 }
participant ref: "alice azer"
participant ref: "bob bitume", "next mission": "conquer the moon"
XML process definitions
XML may be advantageous for some people as it looks enterprisey. Perhaps a better reason for using it is the fact that libraries for generating XML documents abound, as well as other XML related tools.
<process-definition name="my_def">
<participant ref="alice" />
<participant ref="bob" />
</process-definition>
You can use the parser to turn XML into a Ruote tree :
require 'ruote/reader'
tree = Ruote::Reader.read(%{
<process-definition name="my_def">
<participant ref="alice" />
<participant ref="bob" />
</process-definition>
})
p tree
# =>
# ["process_definition", {"name"=>"my_def"}, [
# ["participant", {"ref"=>"alice"}, []],
# ["participant", {"ref"=>"bob"}, []]]]]]
Or directly pass the XML as a String to the engine :
engine.launch(%{
<process-definition name="my_def">
<sequence>
<participant ref="alice" />
<participant ref="bob" />
</sequence>
</process-definition>
})
raw [JSON] process definitions
In JSON, they would look like :
[ 'process-definition', { 'name': 'my_def' }, [
[ 'participant', { 'ref': 'alice' }, [] ],
[ 'participant', { 'ref': 'bob' }, [] ]
] ]
In Ruby, that translates to :
[ 'process-definition', { 'name' => 'my_def' }, [
[ 'participant', { 'ref' => 'alice' }, [] ],
[ 'participant', { 'ref' => 'bob' }, [] ]
] ]
The engine understands various formats :
engine.launch('[ "participant", { "ref" : "alpha" }, [] ]')
# JSON string
engine.launch(Rufus::Json.decode('[ "participant", { "ref" : "alpha" }, [] ]'))
# decoded JSON
engine.launch([ "participant", { "ref" => "alpha" }, [] ])
# Ruby raw process definition
This raw representation of a process definition can also accomodate deep data structures like the Ruby DSL does.
process definition generation
So we’ve seen nice “ruby process definition”, but shouldn’t we rather say “process definition generation via ruby” ?
require 'ruote'
pdef = Ruote.process_definition do
sequence do
alpha
bravo
end
end
p pdef
# =>
# ["define", {}, [
# ["sequence", {}, [["alpha", {}, []], ["bravo", {}, []]]]]]
The class method “process_definition” is used to generate a process definiton (a tree of expressions).
Maybe this piece of Ruby code makes the “generation” aspect more visible :
pdef = Ruote.process_definition do
%w[ alpha bravo charly doug ].each do |pa|
participant :ref => pa
end
end
p pdef
# =>
# ["define", {}, [
# ["participant", {"ref"=>"alpha"}, []],
# ["participant", {"ref"=>"bravo"}, []],
# ["participant", {"ref"=>"charly"}, []],
# ["participant", {"ref"=>"doug"}, []]]]
see also
- process testing – testing process definitions