state machine の statesman を In Memory で使ってみる
statesman を使ってみました
rails で state machine が欲しくなったので statesman を使ってみました。
なぜか ruby toolbox では state_machines のカテゴリに入ってませんでしたが、よく使われてメンテナンスも続いているようには見えます。
(ruby 久しぶりだったんですが、今どきは ruby toolbox もあまり使わないんでしょうか)
In Memory で動かす
statesman の特徴は state machine となる class と遷移の履歴を保存する transition class を model とは別に作るのが基本になっているところですかね。
state machine も勝手に model に生えるとかではなく、自分で定義して加えるので勝手にいろいろな機能が追加されていく訳ではなく驚きが少ない気がします。
Activity という model があるとして、state machine になるクラスを作ってinclude Statesman::Machine
、model にstate_machine
メソッドを作って、そこで state machine のインスタンスをセットすればまずは動きます。
class Activity < ApplicationRecord
def state_machine
@state_machine ||= ActivityStateMachine.new(self)
end
end
class ActivityStateMachine
include Statesman::Machine
state :pending, initial: true
state :done
transition from: :pending, to: :done
end
こうやって state_machine
を呼び出して使います
> activity = Activity.new
> activity.state_machine.current_state?
=> "pending"
> activity.state_machine.can_transition_to?(:done)
=> true
> activity.state_machine.transition_to!(:done)
=> true
# 状態遷移が出来ました
> activity.state_machine.current_state
=> "done"
一応、こういう感じに状態の管理は DB を使わずに In Memory で動作するのですが、基本的には状態遷移の記録を取る transition class を指定して動かす方が普段のユースケースには合うかもです。なにしろこのままでは Activity model の状態を保存出来ないので
> activity.save
# ここで保存した activity をロードしなおしたとすると
> activity2 = Activity.last
# 状態を確認すると initial state に戻ってしまってます
> activity2.state_machine.current_state
=> "pending"
(一応)RDB を使って状態を保存する
transition class を作って、DB に保存するようにすれば動きました。
そのやり方は README のままなので詳細はそっちを参考にしてもらうとして、initializers/statesman.rb
でも作って ActiveRecord のアダプタを設定して、transition class を作成して migration をかけたら動くと思います
rails g statesman:active_record_transition Activity ActivityTransition
ただ、これは最初に動かした時の記憶だけなので参考までですが、
作成される migration ファイルが微妙で、id の関連付けで落ちるかもです。rake db:rollback
も index の削除に引っかかったので add_foreign_key
の位置を最後に変更したりしました
# これを migration ファイルの最後に置いておくと、まず foreign_key が消えるので、他の add_index も rollback 可能になる
add_foreign_key :activity_transitions, :activities
(なんかわかりにくい説明かも。すいません!)
Discussion