ruby有一个act as state machine的插件,可以实现工作流或者多步骤向导的功能。
state machine,是计算机科学里面一种模型,这个插件主要实现的是finite state machine,既有限状态自动机。
首先,AASM的安装:
config.gem "rubyist-aasm", :source => "http://gems.github.com", :lib => 'aasm'
安装好之后需要在你的Model里面加入
include AASM
下面是一个简单的例子。来介绍AASM的使用。
加入我们有一个person的model,一个person可以处于三种状态,sleeping, working 还有eating.
初始状态是sleeping,
从sleeping只可以通过eat事件,转到eating,不可以转到working.(吃了饭才能工作)。
从working只可以通过eat事件,转到eating,不可以转到sleeping. (不能空肚子睡觉)。
然后从eating可以通过work事件转到working,或者sleep转到sleeping.
这些可以转化成下面的代码
class Person < ActiveRecord::Base
include AASM
aasm_column :current_state # 定义那个字段来储存当前状态
aasm_initial_state :sleeping #定义初始状态
#定义三个不同的状态
aasm_state :sleeping
aasm_state :eating
aasm_state :working
#定义每个event及event发生时引发的状态转换
aasm_event :eat do
transitions :from => [:sleeping,:working], :to => :eating
end
aasm_event :work do
transitions :from => :eating, :to =>:working
end
aasm_event :sleep do
transitions :from => :eating, :to => :sleeping
end
end
这样基本上就已经建好了state machine了。可以在console里面实验一下:
ruby-1.8.7-p249 > p=Person.create
=> #<Person id: 10, name: nil, current_state: "sleeping", created_at: "2010-05-09 06:32:41", updated_at: "2010-05-09 06:32:41">
ruby-1.8.7-p249 > p.aasm_events_for_current_state
=> [:eat]
ruby-1.8.7-p249 > p.eat
=> true
ruby-1.8.7-p249 > p
=> #<Person id: 10, name: nil, current_state: "eating", created_at: "2010-05-09 06:32:41", updated_at: "2010-05-09 06:32:41">
ruby-1.8.7-p249 > p.aasm_events_for_current_state
=> [:sleep, :work]
ruby-1.8.7-p249 > p.work
=> true
ruby-1.8.7-p249 > p
=> #<Person id: 10, name: nil, current_state: "working", created_at: "2010-05-09 06:32:41", updated_at: "2010-05-09 06:32:41">
ruby-1.8.7-p249 > p.aasm_current_state
=> :working
可以看到,可以直接用 p.event的形式,来触发事件。
aasm_current_state来获得当前状态,aasm_events_for_current_state 来获得当前状态可用的event.
结合到view的时候也很简单。把aasm_events_for_current_state获得的集合交给用户选择。
返回到server之后的param经过验证之后。可以用p.send(param[:event])的形式触发。
这里的例子是一个工作流的例子。多步骤向导也很类似,这里不再赘述。
除此之外,AASM还提供了很多有用的callback, 感兴趣的朋友可以深入研究一下。
github列出了下面几个:
oldstate:exit*
event:before
__find transition, if possible__
transition:on_transition*
oldstate:before_exit
newstate:before_enter
newstate:enter*
__update state__
event:success*
oldstate:after_exit
newstate:after_enter
event:after
obj:aasm_event_fired*