This class implements the 'cursor' and the 'repeat' (loop) expressions.
The cursor expression is a kind of enhanced 'sequence'. Like a sequence it will execute its child expression one by one, sequentially. Unlike a sequence though, it will obey 'commands'.
cursor do
author
reviewer
rewind :if => '${f:not_ok}'
publisher
end
In this simplistic example, the process will flow from author to reviewer and back until the reviewer sets the workitem field 'not_ok' to something else than the value 'true'.
There are two ways to pass commands to a cursor either directly from the process definition with a cursor command expression, either via the workitem '__command__' [special] field.
The commands that a cursor understands are listed here. The most powerful ones are 'rewind' and 'jump'.
Rewinds the cursor up to its first child expression.
cursor do
author
reviewer
rewind :if => '${f:not_ok}'
publisher
end
Exits the cursor.
cursor do
author
reviewer
rewind :if => '${f:review} == fix'
stop :if => '${f:review} == abort'
publisher
end
'_break' or 'over' can be used instead of 'stop'.
Those two commands jump forth and back respectively. By default, they skip 1 child, but they accept a numeric parameter holding the number of children to skip.
cursor do
author
reviewer
rewind :if => '${f:review} == fix'
skip 2 :if => '${f:review} == publish'
reviewer2
rewind :if => '${f:review} == fix'
publisher
end
Jump is probably the most powerful of the cursor commands. It allows to jump to a specified expression that is a direct child of the cursor.
cursor do
author
reviewer
jump :to => 'author', :if => '${f:review} == fix'
jump :to => 'publisher', :if => '${f:review} == publish'
reviewer2
jump :to => 'author', :if => '${f:review} == fix'
publisher
end
Note that the :to accepts the name of an expression or the value of its :ref attribute or the value of its :tag attribute.
cursor do
participant :ref => 'author'
participant :ref => 'reviewer'
jump :to => 'author', :if => '${f:review} == fix'
participant :ref => 'publisher'
end
It's OK to tag a cursor/repeat/loop with the :tag attribute and then point a command to it via :ref :
concurrence do
cursor :tag => 'main' do
author
editor
publisher
end
# meanwhile ...
sequence do
sponsor
rewind :ref => 'main', :if => '${f:stop}'
end
end
This :ref technique may also be used with nested cursor/loop/iterator constructs :
cursor :tag => 'main' do
cursor do
author
editor
rewind :if => '${f:not_ok}'
_break :ref => 'main', :if => '${f:abort_everything}'
end
head_of_edition
rewind :if => '${f:not_ok}'
publisher
end
this example features two nested cursors. There is a "_break" in the inner cursor, but it will break the main 'cursor' (and thus break the whole review process).
As an attribute of the cursor/repeat expression, you can set a :break_if. It tells the cursor (loop) if it has to break.
cursor :break_if => '${f:completed}' do
participant 'alpha'
participant 'bravo'
participant 'charly'
end
If alpha or bravo replies and the field 'completed' is set to true, this cursor will break.
:break_unless is accepted. :over_if and :over_unless are synonyms for :break_if and :break_unless respectively.
:rewind_if / :rewind_unless behave the same, but the cursor/loop, instead of breaking, is put back in its first step.
A 'cursor' expression exits implicitely as soon as its last child replies to it. a 'repeat' expression will apply (again) the first child after the last child replied. A 'break' cursor command might be necessary to exit the loop (or a cancel_process, but that exits the whole process instance).
sequence do
repeat do
author
reviewer
_break :if => '${f:review} == ok'
end
publisher
end
# File lib/ruote/exp/fe_cursor.rb, line 239 def is_loop? name == 'loop' || name == 'repeat' end
Jumps to an integer position, or the name of an expression or a tag name of a ref name.
# File lib/ruote/exp/fe_cursor.rb, line 247 def jump_to (workitem, position, arg) pos = Integer(arg) rescue nil return pos if pos != nil tree_children.each_with_index do |c, i| exp_name = c[0] ref = c[1]['ref'] tag = c[1]['tag'] ref = Ruote.dosub(ref, self, workitem) if ref tag = Ruote.dosub(tag, self, workitem) if tag next if exp_name != arg && ref != arg && tag != arg pos = i break end pos ? pos : position end
# File lib/ruote/exp/fe_cursor.rb, line 213 def move_on (workitem=h.applied_workitem) position = workitem['fei'] == h.fei ? -1 : Ruote::FlowExpressionId.child_id(workitem['fei']) position += 1 com, arg = get_command(workitem) return reply_to_parent(workitem) if com == 'break' case com when 'rewind', 'continue' then position = 0 when 'skip' then position += arg when 'jump' then position = jump_to(workitem, position, arg) end position = 0 if position >= tree_children.size && is_loop? if position < tree_children.size apply_child(position, workitem) else reply_to_parent(workitem) end end
Generated with the Darkfish Rdoc Generator 2.