statesmanの状態遷移の定義からmermaidのstateDiagramを書き出す

下書きに入ってたから掘り起こすシリーズ。

github.com

statesmanを使った状態遷移を伴うコードを読む機会があって、眺めてたら図に書き出したくなったのでコードを書いた。

書き出したいStatesmanの状態定義

サンプルとしてStatesmanのREADMEにあった状態定義をそのまま持ってくる。

# order_state_machine.rb
class OrderStateMachine
  include Statesman::Machine

  state :pending, initial: true
  state :checking_out
  state :purchased
  state :shipped
  state :cancelled
  state :failed
  state :refunded

  transition from: :pending,      to: [:checking_out, :cancelled]
  transition from: :checking_out, to: [:purchased, :cancelled]
  transition from: :purchased,    to: [:shipped, :failed]
  transition from: :shipped,      to: :refunded
end

statesmanのメソッド郡が使えるようになるしくみ

statesman を使ってリソースの状態遷移を扱うためには使いたいクラスで Statesman::Machine を include する必要がある。includeすると、そのクラスにクラスメソッドが定義される。

github.com

仕組みをシンプルに書くと以下のとおりとなる。

irb(main):001:1* module Foo
irb(main):002:2*   module Bar
irb(main):003:3*     def self.included(base)
irb(main):004:3*       base.extend(ClassMethod)
irb(main):005:2*     end
irb(main):006:2*
irb(main):007:3*     module ClassMethod
irb(main):008:4*       def baz
irb(main):009:4*         puts "Hello!"
irb(main):010:3*       end
irb(main):011:2*     end
irb(main):012:1*   end
irb(main):013:0> end
=> :baz
irb(main):014:1* class A
irb(main):015:1*   include Foo::Bar
irb(main):016:0> end
=> A
irb(main):017:0> A.baz
Hello!
=> nil

self.included で module Barinclude されたときの処理を記述できる。引数はインクルードしたオブジェクトである。 docs.ruby-lang.org

base.extend(ClassMethod) はつまるところ A.extend(ClassMethod) となる。extendObject#extend で、Object に対して特異メソッドとして引数に渡したモジュールのインスタンスメソッドを定義できる。ここでは ClassMethod#baz が Aクラスの特異メソッドとして定義される。 docs.ruby-lang.org

クラスの特異メソッドということはクラスメソッドとおおよそ同義として扱って良い。 docs.ruby-lang.org

mermaid の状態遷移図を出力する

実装方法がわかったので、 Statesman::Machine をいい感じに再実装することで状態の宣言と状態遷移のルールの記述に対して好きなことができる。あとはその再実装が適用される状態で直接statemachineのファイルをrubyで実行すればよい。ということで、mermaidのstateDiagramを書き出すコードを以下のgistに結果も含めてまとめた。

mermaidjsのstateDiagram

今回はRubyの実装の再定義だったので、中身を調べて上書きすればよかったのだけれども、なんらかのDSLに対してRubyのメソッドを実装して読み込ませてなんかするってのはbuildersconでやってたLTが自分の中で印象に残っている。今回のstatemachineのクラスを眺めてなんかできそうと思ったのもこのLTを思い出したことがきっかけだった。

www.slideshare.net