NOTE : the latest version of ruote (2.1) is documented at http://ruote.rubyforge.org. Meet you there.

ruote expressions

A few paragraphs and examples about each of the expressions found in Ruote (OpenWFEru).

For each expression, a link is provided to its “rdoc”, which might be pretty “empty”.

Usage examples are given in XML and / or as Ruby process definitions.

Expression names in Ruby process definitions are often preceded with an ‘_’ (underscore) to help differentiate them from actual Ruby keywords and identifiers.

It’s also worth to have a look at how Ruote implements each of the workflow control patterns.

 

Expressions are enumerated in the alphabetical order, but here is some kind of ‘importance order’ :


a

‘a’ is a shortcut for the attribute expression.

attribute

Makes for ugly process definitions, but anyway… With this expression, a complex value (like a list or a map) can be described directly in XML or YAML in the process definition.

See the restore expression for some examples.

rdoc for more information

cancel-process

Among the first expressions in the alphabetical order… When the workflow engine encounters this expression, it cancels completely the process instance to which it belongs.

1   <sequence>
2     <participant ref="before" />
3     <cancel-process />
4     <participant ref="after" />
5   </sequence>

It this example, the participant named “after” is never reached.

Most of the time, “cancel-process” is used as a consequence to an “if” expression or uses its own ‘if’ attribute, like in :

 1   require 'openwfe/def'
 2 
 3   class TestDefinition1 < OpenWFE::ProcessDefinition
 4     sequence do
 5       participant "customer"
 6       _cancel_process :if => "${f:no_thanks} == true"
 7       concurrence do
 8         participant "accounting"
 9         participant "logistics"
10       end
11     end
12   end

If the participant “customer” set the field “no_thanks” to “true”, the flow will never resume to the concurrence.

rdoc

case

The ‘case’ expression is an extended ‘if’. It accepts a list of children and expects the first one to be a condition to evaluate. If this child evaluates to true it will evaluate the second child as a consequence and then exit.

If the first child evaluates to false, it will jump to the third child. If there is none, it will exit, if there is one, it will evaluate it (and if it’s a condition…) (this is boring to explain).

 1   require 'openwfe/def'
 2 
 3   class DefinitionWithCase < OpenWFE::ProcessDefinition
 4     case do
 5       equals :field => "customer", :val => "El Wasabi"
 6       participant :ref => "salesman_1"
 7       equals :field => "customer", :val => "Rio Coffee"
 8       participant :ref => "salesman_2"
 9       participant :ref => "salesman_3"
10     end
11       #
12       # attributing customers to salesmen.
13       # salesman number 3 handles anything that is not 'El Wasabi' or
14       # 'Rio Coffee'
15   end

rdoc

concurrence

Divides the process flow into two or more concurrent lanes.

 1   <process-definition name="exp_mydef" revision="0.1">
 2     <concurrence>
 3       <participant ref="alice" />
 4       <participant ref="bob" />
 5       <sequence>
 6         <participant ref="charlie" />
 7         <participant ref="doug" />
 8       </sequence>
 9     </concurrence>
10   </process-definition>

With Ruote you could also write :

 1   require 'openwfe/def'
 2 
 3   class MyProcessDefinition01 < OpenWFE::ProcessDefinition
 4     concurrence do
 5       participant :ref => "alice"
 6       participant "bob"
 7       sequence do
 8         charlie
 9         doug
10       end
11     end
12   end

Note that in this example, three different ways of ‘invoking’ a participant have been displayed.

rdoc (for an explanation on the merge attributes of the concurrence expression)

concurrent-iterator

This is a cross between a concurrence and an iterator.

It understands the same attributes and behaves as should be : as an iterator spawning parallel copies of its child.

An explanation with some examples

See it in action in the workflow pattern 13, 14 and 15.

Some snippets, in XML :

1   <concurrent-iterator on-value="sales, logistics, lob2" to-field="p">
2     <participant field-ref="p" />
3   </concurrent-iterator>

In Ruby :

1   sequence do
2     set :field => f, :value => %w{ Alan, Bob, Clarence }
3     #... 
4     concurrent_iterator :on_field => "f", :to_field => "p" do
5       participant "${f:p}"
6     end
7   end

rdoc

cron

A ‘cron’ expression will trigger its child expression (a new copy each time) at a determined frequency.

(Note : the ‘cron’ expression found in Ruote 0.9.18 has a very different behaviour from previous versions)

This frequency can be either specified with a classical cron tab string (see man 5 crontab) or with an ‘every’ frequency, something like “every 2 hours” or “every 5 minutes and 2 seconds”.

1   <!--
2       trigger the subprocess 'send-reminder' once per hour from nine to
3       five, from Monday to Friday.
4   -->
5   <cron tab="0 9-17 * * mon-fri">
6     <send-reminder />
7   </cron>
1   #
2   # trigger the subprocess named 'send_reminder' every ten seconds.
3   #
4   cron :every => "10s"
5     send_reminder
6   end

The ‘cron’ expression never replies to its parent expression, thus, used a process whose body is simply a cron expression will never terminate (unless cancelled).

The classical usage scenario involves wrapping the cron in a concurrence not expecting its reply.

1   concurrence :count => 1 do
2     participant :toto
3     cron :every => "10m" do
4       subprocess :ref => "send_reminder" :target => "toto@headlost.org.uk"
5     end
6   end

where the subprocess “send_reminder” is triggered every 10 minutes until participant toto is done with his activity. Note that the ‘concurrence’ is set to wait for only 1 reply (it will then cancel the branch that didn’t reply, hence cancel the cron expression, which will get descheduled).

rdoc

cursor

The ‘cursor’ expression is a sequence with extended capabilities.

It understands the following commands :

Cursor commands accept condition attributes like in :

 1   <cursor>
 2     <participant field-ref="reference_1" />
 3     <participant field-ref="reference_2" />
 4 
 5     <break if="${field:do_not_hire} == true" />
 6       <!-- do not hire if one of the references set the field
 7            'do_not_hire' to true -->
 8 
 9     <participant ref="faculty" />
10     <participant ref="hr_department" />
11   </cursor>

More documentation of those conditions in the cursor command rdoc

Since Ruote 0.9.20, the cursor (and the loop) expressions have ‘rewind-if’ / ‘break-if’ attributes.

 1   class Pr0 < OpenWFE::ProcessDefinition
 2   #OpenWFE.process_definition :name => 'pr', :revision => '0'
 3 
 4     # at launch time, the fields 'manager', 'initiator' and 'pr_url' are set
 5     # by the initiator
 6 
 7     cursor :rewind_if => '${f:rework}', :break_if => '${f:rejected}' do
 8 
 9       participant '${f:initiator}', :activity => 'prepare pr', :if => '${f:rejected}'
10         # when entering the cursor, the field 'rejected' will not have been
11         # set so the flow will jump directly to the manager.
12 
13       manager :activity => 'pr approval'
14 
15       cfo :activity => 'pr approval', :if => '${f:dollar_value} > 10000'
16         # only goes to the CFO if the dollar value is high enough
17 
18       accounting :activity => 'pr approval'
19       participant :field_ref => 'initiator'
20     end
21   end

In this example, the process follows a serie of participant and each time one of the replies, it checks the fields ‘rework’ and ‘rejected’. The cursor exits if the field ‘rejected’ is set to true, or gets rewound if the field ‘rework’ holds a true value.

The attributes ‘rewind_unless’ and ‘break_unless’ are understood as well.

Original is at http://gist.github.com/73813

The loop expression is a cursor that loops (you have to explicitely break or cancel it, to exit it).

cursor rdoc
cursor command rdoc

The ‘cursor’ expression is used for example in the workflow control pattern 10 :Arbitrary Cycles.

pattern

defined

1   <if>
2     <defined field="customer">
3     <!-- then -->
4     <subprocess ref="call_customer" />
5   </if>

Note that since Ruote 0.9.17, it’s possible to write :

1   _if :test => "${field:customer} is set" do
2     sequence do
3       participant "A"
4       participant "B"
5     end
6   end

rdoc

equals

The ‘equals’ expression is mainly used nested within an if expression. It’s used to compare two values. A value may be a plain string, a variable or a workitem field (attribute).

Read more in the rdoc link that follows.

rdoc

error

The ‘error’ expression is used to trigger/force an error in a process instance. The error will thus interrupt the process or ‘redirect’ it to an error handler if one is present.

This example process will get interrupted if the field customer is empty or not present. Note the usage of the optional :if attribute :

1   sequence do
2     participant :ref => 'unit1'
3     error 'missing info', :if => "${f:customer} == ''"
4     participant :ref => 'unit2'
5   end

This XML example will route the process (in fact the whole ‘sequence’ block, the one adorned with an ‘on_error’ attribute) to an “fail_path” subprocess :

 1   <process-definition name="myprocess" revision="0">
 2 
 3     <sequence on-error="fail_path">
 4       <participant ref="unit1" />
 5       <error unless="${f:customer}">missing field customer</error>
 6       <participant ref="unit2" />
 7     </sequence>
 8 
 9     <process-definition name="fail_path">
10       <participant ref="special_cases_unit" />
11     </process-definition>
12 
13   </process-definition>

rdoc

eval

A dangerous expression, it takes the value of a field or a variable and evaluates it as a segment of process definition.

At some point in this example, the workitem field ‘code’ is filled with a segment of [Ruby] process definition, that gets executed later :

 1   <process-definition name="myprocess" revision="0">
 2     <sequence>
 3 
 4       <set field="code">
 5         concurrence do
 6           participant 'alpha'
 7           participant 'toto'
 8         end
 9       </set>
10 
11       <!-- a bit later... -->
12 
13       <eval field-def="code" />
14 
15     </sequence>
16   </process-definition>

One could think about many scenarii, for instance, evaluating fragments of process definitions coming back from a participant (if it’s trusted).

This expression will raise an error if the configuration parameter :dynamic_eval_allowed isn’t set to ‘true’. You have to explicitely set this parameter, it defaults to ‘false’.

Eval works with fields and variables (or nested values).

rdoc

exp

The ‘exp’ expression allows to choose at apply time, which expression (or participant or subprocess) to use :

 1 
 2   sequence do
 3 
 4     my_activity :variant => "sequence"
 5       # 
 6       # B works after A
 7 
 8     my_activity :variant => "concurrence"
 9       # 
10       # A and B work in parallel
11   end
12 
13   process_definition :name => "my_activity" do
14 
15     exp :name => "${variant}", :default => "sequence" do
16 
17       participant "A"
18       participant "B"
19     end
20   end

The ‘name’ (and the ‘default’) attribute(s) can refer to an expression, a subprocess name or a participant name, indifferently. See the following rdoc link for a more detailed example.

rdoc

f

Is a shortcut for “field”.

field

rdoc

filter

The participant expression has a ‘filter’ attribute for filtering in and out the payload of a workitem.

There is a also a ‘filter’ expression for filtering data in and out of process segments.

 1   class MyProcessDefinition001 < ProcessDefinition
 2     sequence do
 3 
 4       set :field => "readable", :value => "bible"
 5       set :field => "writable", :value => "sand"
 6       set :field => "randw", :value => "notebook"
 7       set :field => "hidden", :value => "playboy"
 8 
 9       alice
10 
11       filter :name => "filter0" do
12         sequence
13           bob
14           charly
15         end
16       end
17     end
18 
19     filter_definition :name => "filter0" do
20       field :regex => "readable", :permissions => "r"
21       field :regex => "writable", :permissions => "w"
22       field :regex => "randw", :permissions => "rw"
23       field :regex => "hidden", :permissions => ""
24     end
25   end

In that example, participant ‘alice’ will see and be able to modify all the fields of the workitem, whereas, participants ‘bob’ and ‘charly’ will be restricted to read the fields ‘readable’ and ‘randw’ and to write to the fields ‘sand’ and ‘randw’.

rdoc

filter-definition

There is already an example of a filter definition in the description of the filter expression.

A filter definition builds and then binds a filter to an OpenWFE variable. A filter may thus be bound locally (no prefix), at the process level (“/” prefix) or at the engine level (“//” prefix).

Filter definition takes place within a process definition. They may be set directly in the process body or outside of it (still within the process definition).

1   filter_definition :name => "filter0" do
2     field :regex => "readable", :permissions => "r"
3     field :regex => "writable", :permissions => "w"
4     field :regex => "randw", :permissions => "rw"
5     field :regex => "hidden", :permissions => ""
6   end

rdoc

forget

The ‘forget’ expression is used to set up dead ends.

It triggers its child expression and then immediately replies to its own parent expression, not caring about any potential reply from the child.

 1   sequence do
 2     participant :a
 3     forget do
 4       sequence do
 5         participant :a0
 6         participant :a1
 7       end
 8     end
 9     participant :b
10   end

rdoc

if

See the exclusive choice workflow control pattern.

rdoc

iterator

Given a list and a child expression (one unique child is expected), ‘iterator’ will execute the child expression once per element in the list.

1   <iterator 
2     on-value="alice, bob, charly"
3     to-variable="next_participant"
4   >
5     <participant ref="${next_participant}" />
6   </iterator>

is thus equivalent to

1   <sequence>
2     <participant ref="alice" />
3     <participant ref="bob" />
4     <participant ref="charly" />
5   </sequence>

Of course, the list could be a bit more dynamic :

1   iterator :on_value => "${f:participant_list}", :to_variable => "p" do
2     participant "${p}"
3   end

and since Ruote 0.9.13, ‘iterator’ understands the same commands as ‘cursor’ (break/cancel, rewind/continue, skip, jump) :

 1   iterator :on_value => "${f:participant_list}", :to_variable => "p" do
 2     sequence do
 3       participant "${p}"
 4       _break :if => "${f:participant_reply} == cancel_meeting"
 5         #
 6         # if one participant wishes to cancel the meeting, no need
 7         # to notify the others
 8     end
 9   end

iterator rdoc
cursor command rdoc

listen

This expression is used to ‘listen’ on participant events (apply or reply, by default, at apply time) and triggers a piece of process.

 1   <concurrence>
 2 
 3     <listen to="alpha">
 4       <subprocess ref="send-email-notification" />
 5     </listen>
 6 
 7     <sequence>
 8       <participant ref="alpha" />
 9       <participant ref="bravo" />
10       <participant ref="alpha" />
11     </sequence>
12 
13   </concurrence>

Each time a workitem will be dispatched to participant “alpha” (applied), a copy of the workitem will be used to run an instance of the subprocess named “send-email-notification”.

1   listen :to => "^customer_.*", :upon => "reply", :where => "${f:customer_name} == Acme" do
2     concurrence do
3       participant :ref => "operational_sales"
4       participant :ref => "strategic_sales"
5     end
6   end

Makes sure that when the next workitem coming back from any participant whose name begins with “customer_” and where the field customer_name is set to “Acme”, a copy of this workitem is used to trigger a small concurrence between two sales units.

The ‘listen’ expression is not limited to its own process instance, it listens to any participant event in the engine.

Since Ruote 0.9.16, listen expressions without children are possible, then simply block, waiting for a ‘message’ and when it comes in, they let their process instance resume.

1   sequence do
2     listen :to => "^customer_.*", :upon => "reply"
3     subprocess :ref => "order_items"
4   end

Listen expressions without children only work ‘once’.

The ‘intercept’ and ‘receive’ aliases for the ‘listen’ expression are available. The concept behind this expression being “listen to”, an ‘on’ attribute as been introduced has a alias to ‘to’, think “receive on” or “intercept on”.

1   receive :on => "^customer_.*"

The “merge” attribute has a default value of ‘false’. When set to true, the workitem used to trigger the listen expression’s nested child will be a merge of the original workitem (the one that activated the listen expression) and the incoming (the listened for) workitem.

1   listen :to => "^customer_.*", :merge => true

(Ruote 0.9.20) When the attribute ‘wfid’ is set to ‘current’ or :current, only participant events in the same process instance will be listened to.

1   listen :to => "^customer_.*", :wfid => 'current'

rdoc for more information

log

Logs a message to the application log (if not specified otherwise logs/openwferu.log).

rdoc

loop

‘repeat’ is an alias for ‘loop’.

See cursor.

rdoc

lose

Loses a process segment. The ‘lose’ expression applies its child expression, but it never replies to its parent expression.

May be useful in concurrence cases where a certain number of answers are expected.

 1   <concurrence count="1">
 2     <!-- this concurrence expects two answers -->
 3 
 4     <lose>
 5       <!-- will be applied, but 'lose' will never reply 
 6            to the concurrence -->
 7       <participant ref="alice" />
 8     </lose>
 9 
10     <participant ref="bob" />
11     <participant ref="charly" />
12       <!-- the concurrence will be over as soon as bob or charly
13            will have replied -->
14 
15   </concurrence>

rdoc

parameter

‘parameter’ is not a real flow expression. It is used to state and check which required workitem/launchitem fields at present and valid at launch time.

 1 <process-definition name="exp_x" revision="0">
 2 
 3  <!-- required workitem fields -->
 4 
 5   <parameter field="customer" />
 6   <parameter field="address" default="" />
 7   <parameter field="zip" type="integer" />
 8   <parameter field="town" />
 9   <parameter field="phone" match="^[0-9]{3}-[0-9]{7}$" />
10   <parameter field="customer_type" default="2" type="int"/>
11 
12   <!-- the body of the process definition -->
13 
14   <sequence>
15     <participant ref="service0" />
16     <participant ref="service1" />
17     <play-the-music/>
18   </sequence>
19 
20   <!-- subprocesses -->
21 
22   <process-definition name="exp_play-the-music">
23     <participant ref="musician" />
24   </process-definition>
25 
26 </process-definition>

parameters (‘param’ is valid as well) have to be located outside of the body of the process definition (as a direct child of the ‘process-definition’ tag).

In that example, the process launch will immediately fail (even if the launch is asynchronous), if the launchitem doesn’t contain the fields (attributes) ‘customer’, ‘zip’, ‘town’ and ‘phone’.
If the field ‘zip’ is set to something that can be converted to an integer, the launch will fail.
If the ‘phone’ field value doesn’t match the given regular expression, the launch will fail.
If the field ‘address’ is missing, it will be replaced by an empty string (the default value for that field).
If the field ‘customer_type’ is not explicitely given, its value will be the integer ‘2’.

In a ruby process definition, it looks like :

 1   require 'openwfe/def'
 2 
 3   class MyProcessDefinition_01 < OpenWFE::ProcessDefinition
 4 
 5     #
 6     # required fields in the launchitem
 7 
 8     param :field => "customer"
 9     param :field => "address", default => ""
10     param :field => "zip", type => "integer"
11     param :field => "town"
12     param :field => "phone", match => "^[0-9]{3}-[0-9]{7}$"
13     param :field => "customer_type", default => 2, type => :int
14 
15     #
16     # process definition body
17 
18     sequence do
19       service0
20       service1
21       play_the_music
22     end
23 
24     #
25     # subprocesses
26 
27     process_definition :name => "play_the_music"
28       musician
29     end
30   end

participant

This expression is one of the most important in OpenWFE (it’s featured in most of the process definition examples of this page).

A participant is usually located outside of the engine and waits for workitem to be delivered by the engine.

Participants are registered in the participant-map of the engine.

Multiple process definitions may share the same participants (processes != organizational chart).

A classic participant may be a workitem store, queried by a web application for human participants in business processes.

participant implementations shipping with Ruote (OpenWFEru)

rdoc for more information

process-definition

Usually the “process_definition” name is used for this expression, but “define” and “workflow_definition” are usable as well.

1   process_definition :name => "my_process", :revision => "y"
2     sequence do
3       participant "alpha"
4       participant "bravo"
5     end
6   end

The same definition, in XML :

1   <process-definition name="my_process" revision="y">
2     <sequence>
3       <participant ref="alpha" />
4       <participant ref="bravo" />
5     </sequence>
6   </process-definition>

In a Ruby process definition, this is also OK :

 1   class MyDef0 < OpenWFE::ProcessDefinition
 2     sequence do
 3       sub0
 4       sub1
 5     end
 6 
 7     process_definition :name => "sub0" do
 8       participant "cold"
 9     end
10     
11     definition "sub1" do
12       #
13       # ':name => ' not used
14 
15       participant "not cold"
16     end
17   end

rdoc

print

(only used for testing and debugging purposes)

rdoc

q

Is a shortcut for “quote”.

quote

rdoc

redo

This expression is a complement to undo

Whereas ‘undo’ simply cancels an expression given its tag name, redo will cancel it and restart it.

Note that as soon as the tagged expression is done, you can’t redo it.

In a Ruby process definition, take care to prefix this expression with an underscore “_redo” to make sure Ruby won’t take it for it’s own “redo” expression.

It’s OK to append a :if or :unless attribute to a ‘_redo’ expression.

rdoc

related : using tags for signalling ‘process state’

reserve

This expression ensures that segments of a process instance are not run simultaneously.

It uses a mutex variable. A variable bound at engine level (prefixed with “//”) can be used to prevent segments of totally different processes to execute simultaneously.

 1  concurrence do
 2    sequence do
 3      activity_a
 4      reserve :mutex => :m do
 5        activity_b
 6      end
 7      activity_c
 8    end
 9    sequence do
10      activity_d
11      reserve :mutex => :m do
12        sequence do
13          activity_e
14          activity_f
15        end
16      end
17    end
18  end

In this example, the while the activity_b is performed, the sequence of activities e and f cannot be performed.

Note that for short, a symbol :m was used instead of the string “m”. Ruote turns the symbol into the corresponding string.

 1 <!-- process A -->
 2 
 3   <process_definition name="A" revision="0.0">
 4     <sequence>
 5      <participant ref="alice" />
 6      <reserve mutex="//guard0">
 7        <participant ref="alfred" />
 8      </reserve>
 9     </sequence>
10   </process_definition>
11 
12 <!-- process B -->
13 
14   <process_definition name="B" revision="0.0">
15     <sequence>
16      <participant ref="bob" />
17      <reserve mutex="//guard0">
18        <participant ref="berthe" />
19      </reserve>
20     </sequence>
21   </process_definition>

In this XML example, the engine will prevent ‘alfred’ and ‘berthe’ from performing their task simultaneously, in processes A and B respectively. This is achieved by registering the mutex at the engine level (prefix “//”).

rdoc

See the interleaved parallel routing workflow control pattern

restore

Restores the payload of a workitem previously saved in a variable or copies the value of a [sub] field as the top value of the workitem payload.

There are many options for this expression. Check the rdoc for a detailed explanation.

Since Ruote 0.9.17, ‘restore’ has a ‘set-fields’ alias. It can be quite convenient for setting the payload of a workitem at the beginning of a business process.

 1 class Registration < OpenWFE::ProcessDefinition
 2   set_fields :value => {
 3     "name" => "",
 4     "address" => "",
 5     "email" => ""
 6   }
 7   sequence do
 8     approve_registration
 9     perform_registration :if => "${approved} == true"
10     notify_customer
11   end
12 end

(The usual technique for pre-filling a workitem is by providing a launchitem when launching the business process

1   li = OpenWFE::LaunchItem.new "http://process.server.com/definitions/xyz.xml"
2   li.customer = { "name" => "Toto", "age" => 34 }
3   li.request_type = "credit"
4 
5   openwferu_engine.launch li

)

In XML, it looks like :

 1 <process-definition name="test" revision="44b9">
 2   <set-fields>
 3     <a>
 4       <hash>
 5         <entry>
 6           <string>name</string><string></string>
 7         </entry>
 8         <entry>
 9           <string>address</string><string></string>
10         </entry>
11         <entry>
12           <string>email</string><string></string>
13         </entry>
14       </hash>
15     </a>
16   </set-fields>
17   <sequence>
18     <participant ref="approve_registration" />
19     <participant ref="perform_registration if="${approved} == true" />
20     <participant ref="notify_customer" />
21   </sequence>
22 </process-definition>

Note that the passing JSON (or YAML) is OK :

1   <set-fields>
2     <a>
3       { "customer": { "name": "Zigue", "age": 34 }, "approved": false }
4     </a>
5   </set-fields>

rdoc

reval

This expression evals the Ruby code nested within it.

 1   <sequence>
 2     <reval>$i = 0</reval>
 3     <loop>
 4       <participant ref="toto" />
 5       <reval>$i = $i + 1</reval>
 6       <break if="${r:$i == 10}" />
 7       <!-- a variant :
 8       <break rif="$i == 10" />
 9       -->
10     </loop>
11   </sequence>

Within a ‘reval’ piece of code, the two principal hooks within the engine ‘bowels’ are self which points to the RawExpression instance itself and workitem

 1   class Reval4 < OpenWFE::ProcessDefinition
 2     sequence do
 3       reval """
 4         engine = self.application_context['__tracer']
 5 
 6         workitem.currently_active_processes =
 7             engine.list_processes.join('\n')
 8 
 9         'done' # will be placed in the workitem field '__result__'
10       """
11       participant :toto
12     end
13   end

The result of ‘reval’ is placed in the field named

result
as a String.

The code is evaluated at a SAFE level of 3 (see http://www.rubycentral.com/book/taint.html for more information about those levels).

You have to explicitely set the :ruby_eval_allowed param to true in the application context of the engine else the ‘reval’ expression will raise an exception.

1   engine.application_context[:ruby_eval_allowed] = true

rdoc

save

Saves a workitem to a variable or save the attributes of a workitem in a field (of that same workitem).

Is used in conjunction with restore.

rdoc

sequence

Simply chaining other flow expressions.

1   <process-definition name="exp_my_definition" revision="0">
2     <sequence>
3       <participant ref="alpha" />
4       <participant ref="bravo" />
5       <participant ref="charly" />
6     </sequence>
7   </process-definition>

In Ruby :

 1   require 'openwfe/def'
 2 
 3   class MyDefinition0 < OpenWFE::ProcessDefinition
 4     sequence do
 5       participant "alpha"
 6       participant "bravo"
 7       participant "charly"
 8     end
 9   end

pattern
rdoc

set

Is used to set the value of a variable or the value of a workitem field (attribute).

It may placed outside of the body of a process definition, it will be evaluated before the body gets applied (executed).

 1   require 'openwfe/def'
 2 
 3   class MyDefinition0 < OpenWFE::ProcessDefinition
 4     sequence do
 5       participant "alpha"
 6     end
 7 
 8     set :field => "city", :value => "Kyoto"
 9     set :field => "country", :value => "Japan"
10   end

The participant ‘alpha’ will recevive a workitem with fields ‘city’ and ‘country’ set to ‘Kyoto’ and ‘Japan’ respectively. This technique is useful for keeping long settings out of the process ‘logic’ itself.

The unset cannot be placed outside of a process body. Filter definitions can.

Shorter version of the attributes are OK :

1   require 'openwfe/def'
2 
3   class MyDefinition0 < OpenWFE::ProcessDefinition
4     sequence do
5       set :f => "customer_name", :val => "(fill this field)"
6       participant "alpha"
7     end
8   end

The ‘set’ expression accepts an ‘escape’ attribute to prevent dollar substituation from occurring.

1   set :v => "v0", :val => "my ${template} thing", escape => true

rdoc

set-fields

“set-fields” is an alias for restore.

rdoc

sleep

‘sleep’ got merged into ‘wait’ since Ruote 0.9.20.

step

This expression was first implemented as a subprocess in the trouble ticket blog post.

It’s now part of Ruote and accept participants and subprocesses as “state” as well as for “transitions”.

Note that all the attributes to the “step” expression itself will be passed as atribute for the state participant or subprocess.

The “transition” after the “state” is determined by the [string] value found in the workitem field named “outcome”. The participant or the subprocess in the “state” is meant to set this field to a meaningful value (see :outcomes and :default after the code example).

 1   # this 'step' expression will first hand the workitem to the "qa" participant
 2   # (or subprocess) and then call the participant or subprocess whose name can
 3   # be found in the workitem field named "outcome".
 4   # Then the flow will resume after the step expression, in this example with
 5   # the participant "dev".
 6   #
 7   # If the outcome field is not set, the 'step' expression will resume 
 8   # immediately to participant "dev".
 9   #
10   sequence do
11     step 'qa', :desc => 'reproduce problem'
12     participant 'dev'
13   end

Here is the “trouble ticket” example implemented with the “step” expression (note that the “define” alias to “process-definition” was used) :

 1   class TroubleTicket02 < OpenWFE::ProcessDefinition
 2   
 3     # The root of the process
 4     #
 5     sequence do
 6   
 7       # the first activity, customer support
 8       #
 9       cs :activity => "enter details"
10   
11       # initiating the first step
12       #
13       step "qa", :desc => "reproduce problem"
14     end
15   
16     #
17     # The 'outputs' of the activities
18     #
19   
20     # QA 'reproduce problem' outputs
21   
22     define "out:cannot_reproduce" do
23       step "cs", :desc => "correct report"
24     end
25     define "out:known_solution" do
26       finalsteps
27     end
28     define "out:duplicate" do
29       step "qa", :desc => "verify"
30     end
31     define "out:reproduced" do
32       step "dev", :desc => "resolution"
33     end
34   
35     # Customer Support 'correct report' outputs
36   
37     define "out:submit" do
38       step "qa", :desc => "reproduce problem"
39     end
40     define "out:give_up" do
41       finalsteps
42     end
43   
44     # QA 'verify' outputs
45   
46     define "out:qa_fixed" do
47       finalsteps
48     end
49     define "out:not_fixed" do
50       step "dev", :desc => "resolution"
51     end
52   
53     # dev 'resolution' outputs
54   
55     define "out:dev_fixed" do
56       step "qa", :desc => "verify"
57     end
58   
59     set :var => "out:not_a_bug", :variable_value => "out:dev_fixed"
60        # "not_a_bug" is an alias to "dev_fixed"
61   
62     # the final steps
63   
64     define "finalsteps" do
65       concurrence do
66         cs :activity => "communicate results"
67         qa :activity => "audit"
68       end
69     end
70   
71   end

The “step” expression also accepts two attributes : “outcomes” and “default”. “outcomes” accepts a list of transition names, while “default” names one default transition to use.

1   <step ref="one" outcomes="two, three" default="two" />

In Ruby :

1   step :one, :outcomes => [ :two, :three ], :default => :two

rdoc

subprocess

This expression is used to trigger a subprocess.

 1   <process-definition name="chores" revision="0">
 2 
 3     <sequence>
 4       <subprocess ref="take_out_garbage" />
 5       <subprocess ref="do_the_laundry" type="white" />
 6       <subprocess ref="do_the_laundry" type="colors" />
 7     </sequence>
 8 
 9     <process-definition name="take_out_garbage">
10       <concurrence>
11         <participant ref="al" activity="take out old paper" />
12         <participant ref="bob" activity="take out garbage" />
13       </concurrence>
14     </process-definition>
15 
16     <process-definition name="do_the_laundry">
17       <sequence>
18         <participant ref="al" activity="sort ${type}" />
19         <participant ref="bob" activity="clean ${type}" />
20         <sleep for="1h30m" />
21         <participant ref="al" activity="hang ${type}" />
22         <participant ref="al" activity="iron ${type}" />
23       </sequence>
24     </process-definition>
25   </process-definition>

Note how the variable ‘type’ was set to ‘white’ and then ‘colors’ and used within the “do_the_laundry” subprocess.

 1   class Chores0 < OpenWFE::ProcessDefinition
 2 
 3     sequence do
 4       subprocess :ref => "take_out_garbage"
 5       do_the_laundry :type => "white"
 6       subprocess :ref => "do_the_laundry", :type => "colors"
 7     end
 8 
 9     process_definition :name => "take_out_garbage" do
10       concurrence do
11         participant :ref => "al", :activity => "take out old paper"
12         bob :activity => "take out garbage"
13       end
14     end
15 
16     process_definition :name => "do_the_laundry" do
17       sequence do
18         participant :ref => "al", :activity => "sort ${type}"
19         participant :ref => "bob", :activity => "clean ${type}"
20         _sleep :for => "1h30m"
21         al :activity => "hang ${type}"
22         al :activity => "iron ${type}"
23       end
24     end
25   end

Note how the subprocess name (and the participant names) can be used as expression names for a shorter syntax. Of course names with white spaces and other niceties won’t work for this shorter syntax.

If the engine :remote_definition_allowed parameter has been set to true, it’s
OK to reference process definitions via their URL :

1   sequence do
2     participant "al"
3     subprocess :ref => "http://company.process.server/defs/defXYZ.xml"
4   end

rdoc

timeout

A generic timeout mechanism : sets a timeout for a whole process segment, not just a single participant or when expression.

1   <timeout after="1w">
2     <concurrence>
3       <participant ref="alpha" />
4       <subprocess ref="do_the_homework" />
5     </concurrence>
6   </timeout>
 1   _timeout :after => "2d" do
 2     sequence do
 3 
 4       participant :ref => "alpha", :timeout => "1d"
 5         # of course individal timeout settings are still ok
 6 
 7       participant :ref => "bravo"
 8     end
 9   end

Note that when using a Ruby process definition, you’d better escape the ‘timeout’ expression with an underscore at the front to avoid collisions with the ‘timeout’ method provided by the timeout.rb standard Ruby library.

rdoc

undefined

1   <if>
2     <undefined field="customer">
3     <!-- then -->
4     <subprocess ref="determine_customer" />
5   </if>

Note that since Ruote 0.9.17, it’s possible to write :

1   _if :test => "${field:customer} is not set" do
2     sequence do
3       participant "A"
4       participant "B"
5     end
6   end

rdoc

undo

Every expression accepts an implicit ‘tag’ attribute which defines a name under which a copy of the expression before evaluation is kept along with the workitem as applied (as received by the expression tagged).

The name (the tag) can then be used by a ‘redo’ or an ‘undo’ expression.

 1   concurrence do
 2     sequence :tag => "side_job" do
 3       participant "alice"
 4       participant "bob"
 5     end
 6     sequence do
 7       participant "charly"
 8       _undo :ref => "side_job"
 9     end
10   end

In this example, after participant ‘charly’ did his job, the first sequence (tagged ‘side_job’) will get cancelled (and its reply to the parent ‘concurrence’ will get triggered).

If the ‘alice / bob’ sequence is already over, the ‘undo’ will have no effect (the tag will have vanished).

It’s OK to append a :if or :unless attribute to an ‘undo’ expression.

See also the redo expression.

rdoc

related : using tags for signalling ‘process state’

unset

rdoc

v

Is a shortcut for “variable”.

variable

rdoc

when

The ‘when’ expression polls a conditional statement and triggers the nested process segment when the condition evaluates to true.

1   <concurrence>
2     <participant ref="alpha" />
3     <when test="${for_bravo} == true">
4       <participant ref="bravo" />
5     </when>
6     <participant ref="charly" />
7   </concurrence>

When the variable “for_bravo” will contain the value “true”, participant “bravo” will receive a workitem.

By default ‘when’ evaluates the condition until true every 10 seconds.

1   concurrence do
2     alpha
3     when :test => "${for_bravo} == true", :frequency => "1h", :timeout => "13d4h" do
4       bravo
5     end
6     charly
7   end

In this Ruby process definition (where all the participant expressions where simplified to just their names), the when condition is checked only every hour and after thirteen days and four hours, the ‘when’ is cancelled (and participant bravo will not receive a workitem).

Note that in both examples, the concurrence will wait until the when expression resumes the process.

rdoc

wait

Since Ruote 0.9.20, ‘sleep’ got merged into the ‘wait’ expression.

wait

This expression is very similar to when, but it doesn’t accept a nested expression. It blocks until its ‘until’ condition evaluates to true or until it times out.

 1   <concurrence>
 2     <sequence>
 3       <wait until="${done}" />
 4       <!-- as soon as one review is ready, start the sorting job ... -->
 5       <alice activity="sort reviews" />
 6     </sequence>
 7     <sequence>
 8       <bob activity="do review" />
 9       <set var="done" val="true" />
10     </sequence>
11     <sequence>
12       <charly activity="do review" />
13       <set var="done" val="true" />
14     </sequence>
15   </concurrence>
1   wait :until => "${reached} == 'stage 2'", :frequency => '30m'
2     # wait until the variable 'reached' is set to 'stage 2' (somewhere
3     # else in the process)
4     # check every thirty minutes.

This expression sees more usage in its ‘sleep’ variant.

sleep

Makes a process segment sleep for a while.

1   <sequence>
2 
3     <sleep for="3d" />
4       <!-- sleeping for 3 days -->
5 
6     <sleep until="Mon Dec 03 10:48:03 +0900 2007" />
7 
8   </sequence>

In a Ruby process definition :

 1   sequence do
 2 
 3     _sleep :for => "3m10s"
 4       # sleeping for 3 minutes and 10 seconds
 5 
 6     _sleep :until => "Mon Dec 03 10:48:03 +0900 2007"
 7       # sleeping until some point in time
 8 
 9     _sleep "3M10d"
10       # sleeping for 3 months and 10 days
11   end

‘wait’ might be more readable (and sound more businessy) though :

 1   sequence do
 2 
 3     wait :for => "3m10s"
 4       # wait for 3 minutes and 10 seconds
 5 
 6     wait :until => "Mon Dec 04 10:48:03 +0900 2007"
 7       # wait until some point in time
 8 
 9     wait "3M10d"
10       # wait for 3 months and 10 days
11   end

Note that when using a Ruby process definition, you’d better escape the ‘sleep’ expression with an underscore at the front to avoid collisions with the ‘sleep’ method provided by the Kernel class in Ruby.

rdoc for more information