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