在 AASM 中自动更改状态的最佳方法是什么
What's the best way to automatically change state in AASM
我一直在使用 AASM 在我当前的项目中制作状态机,想知道自动调用事件并进入下一个状态的最佳方式是什么?
我正在考虑两种方法:
设置一个后台作业来定期检查是否满足某些条件,从而调用事件进行到下一个状态。
让 before_save
调用一个连续尝试下一个事件的方法。有了守卫,如果满足条件,它就不会成功,否则,状态会改变,下次更新模型时,我们会检查一个新事件。
我倾向于第二种选择,因为设置一个 background_job 队列只是为了过渡事件似乎有点矫枉过正。我找不到这方面的最佳做法,所以我很想知道最好的方法以及为什么会这样?
Example
例如,我们有 start_onboarding
和 complete_onboarding
事件。我不想手动调用这些事件,但我想在 pending -> in_progress -> completed 事件之间自动转换。
enum status: {
pending: 1,
in_progress: 2,
completed: 3
}
aasm column: :status, enum: true, whiny_transitions: false do
state :pending, initial: true
state :in_progress
state :completed
event :start_onboarding do
transitions from: :pending, to: :in_progress
end
event :complete_onboarding do
transitions from: :in_progress,
to: :completed,
if: :onboarding_completed?
end
end
在类似任务中:
我们摆脱了:
- 切换状态的回调,因为它们会降低性能
- 实时轮询(带后台作业)因为它也会带来性能下降
我们来使用:
代码看起来像这样:
require 'active_record'
require 'aasm'
require 'sidekiq'
class Task < ActiveRecord::Base
include AASM
establish_connection adapter: 'sqlite3', database: 'todo.db'
connection.create_table table_name, force: true do |t|
t.string :name, null: false
t.string :aasm_state, null: false, index: true
t.datetime :expired_at, null: false
end
validates :name, :aasm_state, :expired_at, presence: true
aasm do
state :pending, initial: true
state :in_progress
state :completed
state :expired
event :run do
transitions to: :in_progress
end
event :complete do
transitions to: :completed
end
event :expire do
transitions to: :expired, unless: :completed?
end
end
end
class Task::ExpireJob
include Sidekiq::Worker
def perform task
task.expire!
end
end
class Task::CreationService
def self.create! params
task = Task.create! params
task.run!
Task::ExpireJob.perform_at task.expired_at, task
task
end
def self.complete! task
task.complete!
task
end
end
task = Task::CreationService.create! \
name: 'first',
expired_at: DateTime.now + 30.seconds
p task
p Task::CreationService.complete! task
我一直在使用 AASM 在我当前的项目中制作状态机,想知道自动调用事件并进入下一个状态的最佳方式是什么?
我正在考虑两种方法:
设置一个后台作业来定期检查是否满足某些条件,从而调用事件进行到下一个状态。
让
before_save
调用一个连续尝试下一个事件的方法。有了守卫,如果满足条件,它就不会成功,否则,状态会改变,下次更新模型时,我们会检查一个新事件。
我倾向于第二种选择,因为设置一个 background_job 队列只是为了过渡事件似乎有点矫枉过正。我找不到这方面的最佳做法,所以我很想知道最好的方法以及为什么会这样?
Example
例如,我们有 start_onboarding
和 complete_onboarding
事件。我不想手动调用这些事件,但我想在 pending -> in_progress -> completed 事件之间自动转换。
enum status: {
pending: 1,
in_progress: 2,
completed: 3
}
aasm column: :status, enum: true, whiny_transitions: false do
state :pending, initial: true
state :in_progress
state :completed
event :start_onboarding do
transitions from: :pending, to: :in_progress
end
event :complete_onboarding do
transitions from: :in_progress,
to: :completed,
if: :onboarding_completed?
end
end
在类似任务中:
我们摆脱了:
- 切换状态的回调,因为它们会降低性能
- 实时轮询(带后台作业)因为它也会带来性能下降
我们来使用:
代码看起来像这样:
require 'active_record'
require 'aasm'
require 'sidekiq'
class Task < ActiveRecord::Base
include AASM
establish_connection adapter: 'sqlite3', database: 'todo.db'
connection.create_table table_name, force: true do |t|
t.string :name, null: false
t.string :aasm_state, null: false, index: true
t.datetime :expired_at, null: false
end
validates :name, :aasm_state, :expired_at, presence: true
aasm do
state :pending, initial: true
state :in_progress
state :completed
state :expired
event :run do
transitions to: :in_progress
end
event :complete do
transitions to: :completed
end
event :expire do
transitions to: :expired, unless: :completed?
end
end
end
class Task::ExpireJob
include Sidekiq::Worker
def perform task
task.expire!
end
end
class Task::CreationService
def self.create! params
task = Task.create! params
task.run!
Task::ExpireJob.perform_at task.expired_at, task
task
end
def self.complete! task
task.complete!
task
end
end
task = Task::CreationService.create! \
name: 'first',
expired_at: DateTime.now + 30.seconds
p task
p Task::CreationService.complete! task