workflow engine Ruote初体验之二(通用属性)
罗列一下表达式所支持的属性:
- :timeout
- :if/ unless
- :forget
- :lose
- :flank
- :on_error
- :on_cancel
- :on_timeout
- :tag
- :filter
- :take
- :discard
- :timers
- :scope
- :await
:time
下面举个例子,如果两个参与者过两天之后还没有做他们的工作,这个工作流程会提交给editor:
1 sequence do 2 participant :ref => 'author' 3 sequence :timeout => '2d' do 4 participant :ref => 'reviewer1' 5 participant :ref => 'reviewer2' 6 end 7 participant :ref => 'editor' 8 end
:time不仅能够处理h,m,d,s(依次表示hour,minute,day,second)模式的时间定义,而且能够处理y,M,w(分别表示year,Month,week)的模式。
就算是你给timeout传递一个绝对时间也没有问题,完全能够处理:
1 participant :ref => 'author', :timeout => 'Sun Jan 24 17:28:28 +0900 2010'
在真实的项目中,timeout的参数一般都是取自工作流程中的变量或者传递进来的参数。
1 participant :ref => 'author', :timeout => '${f:time_limit}'
:if/ :unless
这两个属性接收一个字符串的条件,如果这个字符串的值是true(或者:unless的参数的值是false),表达式就会执行,看下面的列子:
1 concurrence do 2 participant 'ceo', :if => '${f:budget} > 23000' 3 participant 'cfo' 4 participant '${f:bu_head}' 5 end
这个意思就是:如果这个工作流程中的预算配置大于23000,那么CEO就会收到一个工作流程的任务。:if :unless条件能够处理!=、==、=~、"is set"、"is empty"、&&、||等操作符。
:forget
当一个表达式用:forget=>true 或者:forget=>'true',获取将要被“遗忘”的操作。被“遗忘”操作的返回值将被忽略:
1 concurrence do 2 participant 'alfred' 3 participant 'bob' 4 participant 'charly', :forget => true 5 end
charly将会收到一个工作流程,同时concurrence立即收到一个回复。就是说在Alfred和Bob回复了以后,concurrence就会恢复。
:lose
从来不向他的父表达式回复,
1 Ruote.process_definition do 2 concurrence :count => 1 do 3 alfred 4 lose do 5 sequence do 6 wait '2d' 7 send_reminder_to_alfred 8 wait '2h' 9 send_alarm_to_boss 10 end 11 end 12 end 13 end
像这个列子中,reminding从来不向concurrence 回复。concurrence在alfred回复以后就结束了。
:flank
前面的lost中的列子可以用下面这段代码重写:
1 Ruote.process_definition do 2 sequence do 3 sequence :flank => true do 4 wait '2d' 5 send_reminder_to_alfred 6 wait '2h' 7 send_alarm_to_boss 8 end 9 alfred 10 end 11 end
Flanking表达式会立刻向他的父表达式返回,但是仍然可以取消,因为Flanking表达式可以取消,所以他们的生命周期肯定不会比他的父表达式长。
1 Ruote.process_definition do 2 sequence do 3 bob :task => 'support work', :flank => true 4 alfred :task => 'main effort' 5 end 6 end
所以在这个例子中,在alfred完成他的'main effort'任务,bob的'support work'即将终止,因为他的父表达式已经结束了。
下面概括一下forget, lose and flank的区别:
:on_error
默认情况下,在一个工作流程中发生的任何的任何错误都能获取日志和发生错误的地方。在过程定义的时候直接指定“on error”的行为:
1 Ruote.define :name => 'x' do 2 3 sequence :on_error => 'handle_issue' do 4 participant 'alpha' 5 cursor do 6 # ... 7 end 8 end 9 10 define 'handle_issue' do 11 participant 'supervisor', :msg => 'process ${wfid} has gone ballistic' 12 end 13 end
如果在sequence 中发生错误,那么sequence 整个分支就会取消,:on_error必须指定一个子过程或者一个参与者或者像“redo”, “undo”, “cancel”这些命令。
:on_cancel
on_cancel是用来指定一个子过程或者参与者,当当前过程取消的时候,设置的子过程会被调用,或者参与者收到一个工作条目。
pdef = Ruote.process_definition :name => 'aircraft carrier' do cursor :on_cancel => 'decommission' do concurrence do participant 'naval team', :task => 'operate ship' participant 'air team', :task => 'operate planes' end end define 'decommission' do concurrence do participant 'naval team', :task => 'decom weapons' participant 'air team', :task => 'decom aircrafts' end end end
上面这个过程中, “the aircraft”是一个操作,当操作取消的时候,子过程‘decommission’被触发,里面的naval team和air team获取对应的任务。
和:on_error有点不一样的是,当一个表达式在:on_cancel表达式中被取消掉的话,将不会触发:on_cancel.例如上面的列子中“operate planes”这个活动的取消不会激活‘decommission’。只有cursor或者整个过程实例被取消的时候才能触发‘decommission’。
:on_timeout
上面描述的了:timeout属性。:on_time属性是一个补充,它表示在timeout触发的时候调用什么子过程,或者通知某个参与者。除了子过程和参与者者,:on_timeout还可以使用‘redo’,‘error’作为值。
1 sequence do 2 participant 'author' 3 participant 'reviewer', :timeout => '3d', :on_timeout => 'redo' 4 participant 'editor' 5 end
在这个列子中,reviewer每3天收到一个新的工作条目,直到他回复了这个工作流程。回复了之后将回复到editor参与者。
:tag
tag属性用来标记一个工作过程的一个阶段。
1 Ruote.process_definition do 2 sequence do 3 sequence :tag => 'phase 1' do 4 alice 5 bob 6 end 7 sequence :tag => 'phase 2' do 8 charly 9 david 10 end 11 end 12 end
例子中的tags将出现在工作流程的变量中:
p engine.process(wfid).tags.keys # => [ "phase 1" ]
:filter
Ruote2.2.0引进了一个:filter属性。
1 Ruote.process_definition do 2 3 set 'v:f' => { 4 :in => [ 5 { :fields => '/^private_/', :remove => true } 6 ], 7 :out => [ 8 { :fields => '/^private_/', :restore => true }, 9 { :fields => '/^protected_/', :restore => true }, 10 ] 11 } 12 13 alpha 14 sequence :filter => 'f' do 15 bravo 16 charly 17 end 18 delta 19 end
在这个例子中,filter被保存在变量f中,当过程经过alpha进去sequence的时候,调用filter的in端,这个工作条目中以”private_“开始的内容被移除,接下来的bravo,charly不能够访问。当charly执行完成以后,调用dilter的out端的流程。
:take and :discard
:timers
这个比较好理解,下面看一个列子:
1 Ruote.define do 2 # ... 3 alice :timers => '5d: reminder, 12d: final_reminder, 15d: timeout' 4 # ... 5 end
这个例子表示为,alice收到一个任务,她有15天来完成她,在5天以后她会收到一个提醒,在12天以后会收到一个最后一次提醒。模式为"时间段1:action1,时间段2:action2,....,时间段3:action3"。actionx可以是一个参与者的名字,也可以是一个子过程,或者一组定义好的关键字。
timeout:
1 alice :timers => '1h: timeout' 2 # is equivalent to 3 alice :timeout => '1h'
error:过来给定的时间,表达式强制转换成error
1 alice :timers => '1h: reminder, 12h: error'
在从error结束开始,到字符串的末尾或者附近的一个逗号中的字符串是错误信息。
undo, pass
1 alice :timers => '1h: reminder, 12h: error it went wrong'
在指定的12个小时内没有完成工作,那么就忽略alice了,转交给bravo.
redo,retry
1 alice :timers => '12h: redo' 2 # which is equivalent to 3 alice :timeout => '12h', :on_timeout => 'redo'
skip,back,jump.rewind,continue,break,stop,over,reset这些命令都可以执行。
:scope
默认情况下,一个工作流程中的变量在相同的范围,通过:scope属性,来创建新的访问范围
1 define 'flow' do 2 set 'v:v' => 'alice' 3 sequence :scope => true do 4 set 'v:v' => 'bob' 5 participant '${v:x}' # will deliver to bob 6 end 7 participant '${v:x}' # will deliver to alice 8 end
例子中就是创建一个”局部“范围来定义了set 'v:v' => 'bob'。
:await
await的引进是为了帮助模块的有向图。就是等待依赖的工作完成,自己在执行。
1 concurrence do 2 sequence :tag => 'ab' do 3 a 4 b :tag => 'b' 5 end 6 sequence do 7 await :left_tag => 'ab' 8 c 9 end 10 sequence do 11 await :left_tag => 'b' 12 d 13 end 14 end
这个例子就是c要执行需要得ab执行结束之后才能执行,b执行完之后d才能执行。