ナガモト の blog

Full Cycle Developerを目指すエンジニアが有用そうな技術記事や、ポエムのようなよしなしごとを投稿するブログです。

Ruby on Railsで状態を扱うAASM gem

github.com 上記のAASMというgemを使用して状態遷移を扱う方法を紹介します。

gem導入

Gemfileに下記の通り記述し、

gem 'aasm'

bundlerでインストールしましょう。

bundle install

使い方

下記のようなSNSのUserモデルを例に実装します。

リポジトリはこちらです。 github.com

クラス図

f:id:ngmt83:20190208084313p:plain
クラス図

ソース

状態遷移図

f:id:ngmt83:20190208084349p:plain
状態遷移図

ソース

クラス図や状態遷移図はPlantUMLで作成しています。 ngmt83.hatenablog.com

Userモデルにaasm_stateカラムを追加

状態を表すカラムがない場合は下記のrailsコマンドで作成できます。

rails generate aasm user

実際のdiffはこちらです。

CMD: rails generate aasm user · nagamoto/state-machine-sample@2f28fe4 · GitHub

初期状態とすべて状態の定義

stateに状態を列挙し、初期状態をinitialで指定します。

    state :registered, :initial => true
    state :active, :suspended, :banned, :inactive

https://github.com/nagamoto/state-machine-sample/blob/1e3cbcc34eee14ade24a92f6f2de61fe2f18d1ce/app/models/user.rb#L12

状態遷移の定義

状態遷移を伴う操作をeventとし、transitionsで状態遷移を記述します。

    event :ban do
      transitions from: [:active, :suspended], to: :banned, guard: :build_ban
      after do
        create_ban!
      end
    end

https://github.com/nagamoto/state-machine-sample/blob/1e3cbcc34eee14ade24a92f6f2de61fe2f18d1ce/app/models/user.rb#L38

activesuspend状態のもののみ、banという操作でbanned状態に遷移できます。指定と異なる状態で操作を実行しようとするとAASM::InvalidTransitionという例外が発生します。

また、afterなどを用いて状態遷移の前後に操作を記述することもできますし、guardで状態遷移のためのより詳細な条件をチェックすることができるため、誤った状態遷移を起こさないようにすることもできます。 *1

動的に生成されるメソッド

モデルがxx状態であるか確認するxx?メソッドやeventが実行可能かを確認するmay_xxメソッドなどが使えるようになります。

# userはUser aasm_state=activeのインスタンス
user.active? # => true
user.banned? # => false
user.may_ban? # => true
user.ban
user.active? # => false
user.banned? # => true
user.may_ban? # => false
user.ban # => raises AASM::InvalidTransition

使い心地

状態遷移時の条件や操作がevent単位でまとめて記述されるため、見通しが良くなった実感があります。また、必要なメソッドが動的に定義され、コード量が少し減るのもありがたいです。

最後に

このように別の状態管理gemを教えていただいたので、こちらも試してブログに書きたいと思います。*2

※勧められたStatefulEnumについて記事書きました! ngmt83.hatenablog.com

*1:after内の操作で例外が発生した際に状態をロールバックさせるなどは考慮する必要があります。

*2:matsudaさんが作ったgemなら間違いないですね!