読者です 読者をやめる 読者になる 読者になる

ボクココ

サービス開発を成功させるまでの歩み

DB の state は State Machine で管理する

Rails MongoDB

最近は毎日プログラミングの日々を過ごしている。 そんな中、とあるテーブル(ドキュメント)で状態を管理しなきゃいけない場面があった。
この問題の一つの手としてはstate をint で保存し、一つ一つの数字を何かしらの状態に紐づけるやり方だ。これを実践してみたことがある方ならわかる通り、後でこれをメンテする人がいったいその数字が何なのか、をいちいちどっかのドキュメントを見て調べなければならない。これの管理がとても面倒。何かの状態が新しくできたらそのドキュメントもちゃんと更新しないと、色々崩壊する。

State Machine

State Machine はそれぞれの状態と、状態の遷移を管理する一種のデザインパターン。これにより、数値での管理をなくし、さらにある状態からある状態への遷移を限定することができる。例えば、状態として「走る」「歩く」「止まる」「寝る」があるとしよう。いきなり「走る」から[寝る」ことは不可能だ。このときにこの状態遷移をしようとしたら例外が発生するようにしたい。さらに、「走る」から「歩く」は「徐行する」という状態遷移名で呼び出せるようにすれば、ステータスを変える、というプログラムから「徐行する」ととても読みやすいコードを実現することができる。

Rails における 「AASM」

AASM は Acts as State Machine の頭文字だ。かつてのRailsプラグイン界隈の命名規則でありがちだった Act as ~ はDB操作を便利にしてくれるプラグインによく使われていた。今回もそれ系列のGemだ。 これを使えば以下のようにModelに定義できる。

class Job
  include AASM

  aasm do
    state :sleeping, :initial => true
    state :running
    state :cleaning

    event :run do
      transitions :from => :sleeping, :to => :running
    end

    event :clean do
      transitions :from => :running, :to => :cleaning
    end

    event :sleep do
      transitions :from => :running, :to => :sleeping, :guard => :cleaning_needed?
    end
  end

  def cleaning_needed?
    false
  end

end

job = Job.new
job.run
job.may_sleep?  # => false
job.sleep       # => raises AASM::InvalidTransition

なんとわかりやすいコードだろう。状態の遷移がすぐ理解できる。
この guard ってオプションがとても良い。一定の条件を満たさないとそのstateに変われないようにチェックすることができる。それを満たさなかったらAASM::InvalidTransitionの例外が投げられる。

まとめ

読みやすくて管理しやすいコードを書くように心がけます。