Динамический конечный автомат в Ruby? Должны ли государственные машины быть классами?
Вопрос в том, всегда ли конечные автоматы определены статически (на классах)? Или есть способ для меня, чтобы у каждого экземпляра класса был свой набор состояний?
Я проверяю Stonepath для реализации Task Engine. Я действительно не вижу различия между "состояниями" и "задачами", поэтому думаю, что могу просто сопоставить задачу непосредственно состоянию. Это позволило бы мне иметь возможность определять списки задач (или рабочие процессы) динамически, без необходимости делать такие вещи, как:
aasm_event :evaluate do
transitions :to => :in_evaluation, :from => :pending
end
aasm_event :accept do
transitions :to => :accepted, :from => :pending
end
aasm_event :reject do
transitions :to => :rejected, :from => :pending
end
Вместо этого WorkItem (основная модель рабочего процесса / менеджера задач) просто будет иметь много задач. Тогда задачи будут работать как состояния, поэтому я мог бы сделать что-то вроде этого:
aasm_initial_state :initial
tasks.each do |task|
aasm_state task.name.to_sym
end
previous_state = nil
tasks.each do |tasks|
aasm_event task.name.to_sym do
transitions :to => "#{task.name}_phase".to_sym, :from => previous_state ? "#{task.name}_phase" : "initial"
end
previous_state = state
end
Тем не менее, я не могу сделать это с помощью Aasm Gem, потому что эти методы (aasm_state
а также aasm_event
) являются методами класса, поэтому каждый экземпляр класса с этим конечным автоматом имеет одинаковые состояния. Я хочу, чтобы "WorkItem" или "TaskList" динмически создавали последовательность состояний и переходов на основе задач, которые у них есть.
Это позволило бы мне динамически определять рабочие процессы и просто сопоставлять состояния задачам.
Государственные машины когда-либо используются таким образом? Кажется, что этот рубиновый рабочий процесс похож на то, что я описываю.
Обновление: я вижу, что делаю что-то вроде следующего, но это выглядит как хакерский:
@implementation_state_machine = Class::new do
include AASM
aasm_initial_state :initial
tasks.each { |state| aasm_state :"#{task.name}"}
# ...
end
... где будет свойство на моей модели implementation_state_machine
, Я бы переопределил method_missing
делегировать методы, связанные с состоянием (accepted_phase?
) к реализации анонимного класса.
2 ответа
В моей реализации конечным автоматом является хеш https://github.com/mpapis/state_attr
state_attr :state, {
nil => :first,
:first => [:second, :third],
:second => :last,
:third => nil,
}
Вы можете определить столько атрибутов состояния, сколько захотите
Кстати: на заднем плане есть еще класс, но только в качестве прокси для атрибута
Да, это кажется очень хакерским и довольно грязным. Недавно я написал новый гем, который позволяет вам использовать динамические переходы "к" с установкой решения.
Таким образом, вместо того чтобы динамически создавать события и переходы, можно ли сначала отобразить их и использовать параметр выбора, чтобы позволить переходу решить, в какое новое состояние войти? Вы также можете заключить ваш переход from в массив, чтобы вам не нужно было делать:from => previous_state? "#{task.name}_phase": "initial", вы можете просто сделать:from => [:cool_task_phase,:initial ]
Я обнаружил, что излагая ваши переходы и события в первую очередь, вы можете получить более полное представление о том, что делает ваша модель.
Проверьте это на http://github.com/ryanza/stateflow
Надеюсь, вы можете найти какую-то пользу от этого.