| Class | Ruote::Exp::CursorExpression |
| In: |
lib/ruote/exp/fe_cursor.rb
|
| Parent: | CommandedExpression |
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