2017-02-15 131 views
2

我一直在使用AASM来制作当前项目中的状态机,并想知道自动调用事件并进入下一个状态的最佳方式是什么?什么是自动改变AASM状态的最佳方式

我正在考虑两种方式这样做的:

  1. 设置后台作业,定期检查,如果某些条件得到满足,因此调用事件继续到下一个状态。

  2. 有一个before_save调用连续尝试下一个事件的方法。如果条件得到满足,它将不会成功,否则,状态发生变化,下次更新模型时,我们检查新事件。

我倾向于第二种选择与设置background_job排队只是为了转移事件似乎是矫枉过正。我无法找到有关这方面的最佳做法,所以我很想知道最佳方法以及为什么如此。

例如,我们有start_onboardingcomplete_onboarding事件。我不想手动调用这些事件,但我想要在挂起 - > in_progress - >完成事件之间自动转换。

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 
+1

你能否详细说明会发生什么对象什么样的变化以及它们是如何连接到状态机?一个例子可能有助于提出一个好的解决方案。我不确定我是否同意'before_save',但我同意某种观察者模式比背景作业更好,因为观察者可以提供*实时*体验,而cron作业总是背后。 – spickermann

+0

您能否请您详细解释一下您在这里要做什么。所以我可以给你一个有用的答案。 –

+0

我只是寻找一个最佳实践的一般意见。我已经添加了一个状态机的示例@PradeepAgrawal –

回答

1

在类似的任务:

我们摆脱了:

  • 回调来切换状态,因为他们带来的性能下降
  • 一个活生生的轮询(与后台作业),因为它也带来性能下降

我们来使用:

和代码一直在寻找这样的事情:

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 
+2

太棒了,这是更符合我所寻找的。您可以详细了解您通过回调和实时轮询所看到的性能降低情况吗?还是只是,或者你认为使用回调有更多的缺点?我一般对回调的担忧是缺乏对执行顺序的控制。 –

相关问题