iterator

Iterating on a list of values

              pdef = Ruote.process_definition :name => 'test' do
                iterator :on_val => 'alice, bob, charly', :to_var => 'v' do
                  participant '${v:v}'
                end
              end
            

This expression expects at list an ‘on’ attribute, which can be :on,
:on_val, :on_value for a value (usually a comma separated list), :on_v,
:on_var, :on_variable for a list contained in the designated variable and
:on_f, :on_fld, :on_field for a list contained in the designated workitem
field.

The ‘on’ attribute is used to instruct the expression on which list/array
it should iterate.

The ‘to’ attribute takes two forms, :to_v, :to_var, :to_variable or
:to_f, :to_fld, :to_field. Finally, you can write :to => ‘field_name’,
:to => ‘f:field_name’ or :to => ‘v:variable_name’.

The ‘to’ attribute instructs the iterator into which variable or field
it should place the current value (the value being iterated over).

If there is no ‘to’ specified, the current value is placed in the variable
named ‘i’.

The variables ‘ii’ contains the index (from 0 to …) of the current value
(think Ruby’s #each_with_index).

The ‘on’ attribute can be replaced by a :time or a :branches attribute.

              pdef = Ruote.process_definition :name => 'test' do
                iterator :times => '3' do
                  participant 'accounting'
                end
              end
            

will be equivalent to

              pdef = Ruote.process_definition :name => 'test' do
                sequence do
                  participant 'accounting'
                  participant 'accounting'
                  participant 'accounting'
                end
              end
            

variables and scope

Starting with ruote 2.3.0, the iterator doesn’t create a new scope for
its variables, it uses the current scope.

The old behaviour can be obtained by setting :scope => true, as in:

iterator :on => [ 1, 2, 3 ], :to_v => ‘x’, :scope => true do

  1. end

A corollary: by default, the variables set by the iterator or within it
stick in the current scope…

the classical case

Iterating over a workitem field:

              pdef = Ruote.process_definition :name => 'test' do
                iterator :on_field => 'customers', :to_f => 'customer'
                  participant '${f:customer}'
                end
              end
            

It’s equivalent to:

              pdef = Ruote.process_definition :name => 'test' do
                iterator :on => '$f:customers', :to_f => 'customer'
                  participant '${f:customer}'
                end
              end
            

“$f:customers” yields the actual array, whereas “${f:customers}”
yields the string representation of the array.

break/rewind/continue/skip/jump

The ‘iterator’ expression understands a certain the following commands :

  • break (_break) : exits the iteration
  • rewind : places the iteration back at the first iterated value
  • continue : same as ‘rewind’
  • skip : skips a certain number of steps (relative)
  • jump : jump to certain step (absolute)
              pdef = Ruote.process_definition :name => 'test' do
                iterator :times => '3'
                  sequence do
                    participant 'accounting', :review => '${v:i}'
                    rewind :if => '${f:redo_everything} == true'
                  end
                end
              end
            

iterator command in the workitem

It’s OK to issue a command to the iterator from a participant via the
workitem.

              pdef = Ruote.process_definition do
                iterator :times => 10
                  sequence do
                    participant 'accounting'
                    participant 'adjust'
                  end
                end
              end
            

where

              class Adjust
                include Ruote::LocalParticipant
                def consume(workitem)
                  workitem.command = 'break' if workitem.fields['amount'] > 10_000
                  reply_to_engine(workitem)
                end
                def cancel(fei, flavour)
                end
              end
            

A completely stupid example… The adjust participant will make the
loop break if the amount reaches 10_000 (euros?).

break/rewind/continue/skip/jump with :ref

An iterator can be tagged (with the :tag attribute) and directly referenced
from a break/rewind/continue/skip/jump command.

It’s very useful when iterators (and cursors/loops) are nested within each
other or when one has to act on an iterator from outside of it.

              concurrence do
            
                iterator :on => 'alpha, bravo, charly', :tag => 'review' do
                  participant '${v:i}'
                end
            
                # meanwhile ...
            
                sequence do
                  participant 'main control program'
                  _break :ref => 'review', :if => '${f:cancel_review} == yes'
                end
              end
            

in this example, the participant ‘main control program’ may cancel the
review.

              iterator :on => 'c1, c2, c3', :to_f => 'client', :tag => 'main' do
                cursor do
                  participant :ref => '${f:client}'
                  _break :ref => 'main', :if => '${f:cancel_everything}'
                  participant :ref => 'salesclerk'
                  participant :ref => 'salesclerk'
                end
              end
            

in this weird process, if one customer says “cancel everything” (set the
workitem field “cancel_everything” to true), then the whole iterator
gets ‘broken’ out of.