0

Auto thay đổi STATE bằng StateMachine và sidekiq rails 4

I. Các khái niệm

1. Sidekiq

Sidekiq là một gem hỗ trỡ xử lý ngầm dưới background mạnh mẽ cho Ruby. Nó nhằm mục đích là đơn giản để tích hợp với bất kỳ ứng dụng Rails hiện đại và hiệu suất cao hơn nhiều so với các giải pháp hiện có khác.

2. State machine

StateMachine là 1 gem được tạo ra để đơn giản hóa quản lý các hành vi của một Object. Bình thường, trạng thái của một Object lưu giữ bằng cách tạo ra nhiều thuộc tính và các hành động xử lý dựa trên các giá trị. Điều này có thể trở nên cồng kềnh và khó khăn để duy trì khi sự phức tạp của các Object bắt đầu tăng. StateMachine sẽ giúp bạn xử lý các hành động phức tạp trên, bằng cơ chế rất đơn giản.

II. Kết hợp giữa sidekiq và state_machine

Khi chúng ta muốn 1 hành động nào đó xảy ra một cách tự động hóa.

VD cụ thể: Ta có 1 User, ta muốn tạo ra hành động công việc hoạt động sau 10s khi User bấm nút create nó, sau 1 khoảng thời gian sau (thời gian được quy định bơi User) thì hoạt động đó trở về trạng thái stop và cộng điểm cho User.

1. Cài đặt state_machine

Nếu chúng ta không sử dụng gem thì phải viết các hàm:

class Test < ActiveRecord::Base
  belongs_to :user

  def start?
    state == "ongoing"
  end

  def pending

  end

  def start
    update! state: :ongoing
  end

  def after_start
    # Đây là chỗ xử lý tự động để khi bấm nút start sau khoảng thời gian state sẽ tự động về trạng thái stop
  end

  def stop

  end
end

Khi sử dụng gem chúng ta sẽ được code gọn hợn, đẹp hơn và dễ hiểu hơn

class Test < ActiveRecord::Base
  belongs_to :user

  state_machine :state, initial: :creating do
    event :prepare do
      transition creating: :pending
    end

    event :start do
      transition pending: :ongoing
    end

    event :stop do
      transition all => :ended
    end

    after_transition on: :pending do |test|

    end
end

Code ở trên có nghĩa là khi chúng ta tạo ra 1 test thì state mặc định của nó sẽ là creating và các event bên dưới là khi chúng ta có 1 record @test ta chỉ cần @test.start thì state của nó sẽ tự động chuyển về ongoing

2. Cài state_machine với sidekiq

Ở trên ta vừa lấy ví dụ với User có nhiều bài tests. Ta tạo 1 project như sau

  • Giao diện:

home2.png

Ta có 1 user đang có 10 point, và list các tests chưa có cái nào, giờ ta sẽ tạo nó, đồng thời chuyển nó về trạng thái pending, ở form nhập vào chúng ta sẽ có các thuộc tính start_after, during, name. trước khi tạo thì ta bật terminal cd đến project gõ sidekiq để bật server của sidekiq lên 😉.

modal.png

  • start_after là thời gian sau khi bấm nút Add bài test đó sẽ được bắt đầu, thời điểm bắt đầu thì test.state = ongoing
  • during là thời gian diễn ra bài test đó, lúc hết thời gian thì test.state = ended

Tất cả các trạng thái sẽ được chuyển tự động bằng phương thức after_transition của state_machine, trong phương thức này ta sẽ lập lịch cho nó tự động chuyển trạng thái bằng sidekiq.

  • Ta sẽ tạo ra file app/workers/test_worker.rb:
class TestWorker
  include Sidekiq::Worker

  def perform id, action
    TestWorker.delete_schedule id, action
    TestWorker.delete_schedule id, action
    test = Test.find_by id: id
    test.try action.to_sym
    test.user.increment! :point, 10 if action.to_s == "stop"
  end

  class << self
    def delete_schedule id, action
      jobs = Sidekiq::ScheduledSet.new.select do |retri|
        retri.klass == name && retri.item["args"] == [id, action]
      end
      jobs.each(&:delete)
    end
  end
end

Như vậy chúng ta đã khai báo 1 lịch cho Test và thời điểm đặt lịch như thế nào thì ta sẽ chuyển đến model Test, app/models/test.rb:

class Test < ActiveRecord::Base
  attr_accessor :start_after, :during
  belongs_to :user

  state_machine :state, initial: :creating do

    # ...

    after_transition on: :prepare do |test|
      TestWorker.delete_schedule test.id, :start
      TestWorker.perform_at(test.starts_at, test.id, :start)
    end

    after_transition on: :start do |test|
      TestWorker.delete_schedule test.id, :stop
      TestWorker.perform_at(test.ends_at, test.id, :stop)
    end
  end
end

Như trên có nghĩa là sau khi test chuyển đến trạng thái pending thì sẽ đặt lịch cho sidekiq đã được định nghĩa ở trên, thực hiện hành động start cho test đó tại thời điểm test.starts_at được ghi bởi lúc tạo test.

-> Như vậy chúng ta đã cho test.state auto thay đổi từ lúc tạo ra test bằng cách sử dụng state_machine và sidekiq

III. Tài liệu tham khảo


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí