Rails 上で ActiveRecord を使って status を保存できる statemachine 系の gem として、だいたい aasm で作ってたので twitter でなんかないかなぁと書いたところ、以下の返答をもらった。
@takkanm https://t.co/QhJoX1CKAT
— 80.0 (@flada_auxv) March 8, 2016
@takkanm @flada_auxv 更に追撃w https://t.co/v9W3Mam3el
— joker1007に宜しく (@joker1007) March 8, 2016
実はこの返答を得る前に aasm をいれちゃってたんだけど、今回はスパイク中のコードで使ったので、本実装時に再度選ぶことになるだろうから、aasm と教えてもらったものを簡単にまとめておく。
TL;DR;
どれを選ぶかは要件次第なので、これが絶対にお勧めということを書く気はないんだけど、私だったら以下のような選択するかなぁ。
- ActiveRecord の enum を利用したいなら aasm
- 状態の遷移をトラッキングしたい履歴が重要になるなら statesman
- 状態が遷移した時間は必要になりそうなら transitions
aasm
GitHub での star は、この 3 つの中では最多の 2,236(2016/03/08 19:00 現在)。
3 つの中で唯一過去プロジェクトで使ったことがあるやつ。
state 管理したいクラスに AASM
を include し、aasm do ... end
というブロックに state 管理のためのコードをまとめる。
ActiveRecord との連携では、デフォルトでは aasm_state
というカラムを利用することになるが、Rails の enum カラムを利用しても state 管理ができる。
対象の ActiveRecord クラスには、クラス.state_name
のような scope と、オブジェクトには オブジェクト.state_name?
で期待する state であるかというメソッドと オブジェクト.may_state_name?
という、指定した state に遷移できるかを確認するメソッドができる。
transition は event :event_name do ... end
というブロックに書いていき、object.event_name
で遷移できるようになる。object.event_name!
で保存までやってくれる。
event や transition 単位に、before
や after
、 error
のコールバックを記述しておくことができる。他にもコールバックとして差し込めるポイント は複数用意されている。
また、各 transition ごとに guard 条件を設定できるので、state の状態以外で遷移できるかの可否を記述することもできる。
以下、サンプルコード。
class Item < ActiveRecord::Base
include AASM
enum state: {wip: 0, ship: 10, archived: 100}
aasm column: :state, enum: :true do
state :wip, initial: true
state :ship
state :archive
event :publish do
before do
self.published_at = Time.current
end
transitions from: :wip, to: :ship
end
event :archive do
transitions from: :wip, to :archive
transitions from: :ship, to :archive
end
end
end
statesman
こちらの GitHub の star 数は、694(2016/03/08 19:00 現在)。
stateman は、他の 2 つと違い StateMachine 用のクラスが必要になるのが特徴。
statemachine を導入すると最初は少なかった状態数がプロジェクトが進むにつれ徐々に爆発していくことがあるので、その点ではスッキリ書けそう。
state
や transition
、guard_transition
, before_transition
などで、状態管理のための処理を宣言的に書ける。
状態を保存するには ActiveReocrd で、StateMachine を保存する用の table を用意したり、State 管理したい対象に関連をもたせてあげたりといろいろしてあげないといけないことが多い。以下の migrate 生成コマンドを実行する必要あり。
$ rails g statesman:active_record_transition 状態管理をしたいクラス名 状態保存用クラス名
しかし、各 state をカラムじゃなくレコードとして持つことができるので、状態遷移の履歴を後から参照できる。
状態を確認したいときや、遷移させたいときは、aasm のようにはいかず、current_state
や can_transition_to?(state名)
、transition_to(state名)
のように指定しないといけない。Scope も in_state(state名)
のようになる。
transitions
こちらの GitHub の star 数は、464(2016/03/08 19:00 現在)。
この gem は aasm のように、state_machine do ... end
で state 管理したいクラスに情報を記述していくスタイル。
state
で 状態を記述し、event :event_name do ... end
でイベントを書いて、transitions
でトランジションを書くのも似てる。
コールバックのタイミングや種類は独特な感じで、state に対しての enter
や exit
に対してもコールバックがセットできるのが他の 2 に比べると特徴的。
過去に aasm を使ったときは、だいたいほとんどの event に対して before で何かしらのタイムスタンプをいれるようにしていたんだけど(本当に必要だったかは要検討)、transitions にはタイムスタンプをいれるためのサポートある。
さいごに
また、これを書いている間に新たな statemachine が誕生しそうな息吹を感じた。
今回、3 つの gem を比較してみたけど、今後とりあえず導入するなら transitions を選択してみるかなあ。
とはいえ、だいたい適当に選ぶとつらいめにあうので、ある程度その state をどう使いたいかという用途の検討はしてから選びたい。
あと、他におもしろいのあったら教えてください !