Auto thay đổi STATE bằng StateMachine và sidekiq rails 4
This post hasn't been updated for 3 years
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:
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 .
start_after
là thời gian sau khi bấm nútAdd
bàitest
đó 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àitest
đó, 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