AASM は,オブジェクトに状態遷移の仕組みを簡単に持ち込むことができるライブラリーだ。
AASM の勉強を兼ねてズンドコキヨシを書いてみた。
参考:ズンドコキヨシまとめ
コード 1
最初に書いたのがこれ。
require 'aasm'
class Zundoko
include AASM
aasm do
# ズンがまだ無い状態(初期状態)
state :z0, initial: true
# ズンが一つ溜まった状態(以下同様)
state :z1
state :z2
state :z3
# ズンが四つ以上溜まった状態
state :z4
# 完了状態
state :completed
# zun をもらったときの遷移規則
event :zun do
# z0 からは z1 へ(以下同様)
transitions from: :z0, to: :z1
transitions from: :z1, to: :z2
transitions from: :z2, to: :z3
# z3 と z4 からは z4 へ
transitions from: [:z3, :z4], to: :z4
end
# doko をもらったときの遷移規則
event :doko do
# z0~z3 からは z0 へ
transitions from: %i(z0 z1 z2 z3), to: :z0
# z4 からは完了状態へ
transitions from: :z4, to: :completed
end
end
end
zundoko = Zundoko.new
zundoko_strings = {zun: "ズン", doko: "ドコ"}
loop do
m = zundoko_strings.keys.sample
print zundoko_strings[m], " "
zundoko.send m
if zundoko.completed?
puts "キヨシ"
break
end
sleep 0.1
end
簡単のため,完了状態になったあとのことは考えてない。さらに続けられるようにするならもう少し複雑になる。
コード 2
コード 1 は,ズンが 0 回溜まった状態から 4 回以上溜まった状態までを別々の AASM 状態として管理しているのがイマイチ。仕様変更に弱そうだし,見通しが悪い。
いっそ状態は未完了・完了の二つだけにして,ズンの溜まった数をインスタンス変数で管理するのはどうか?
その場合,数によって遷移の仕方が変わるのをどう書くのか分からなくて,調べながら書いた。
できたのがこれ。
require 'aasm'
class Zundoko
include AASM
NECESSARY_ZUN_COUNT = 4
def initialize
@zun_count = 0
end
aasm do
# 未完了状態(初期状態)
state :running, initial: true
# 完了状態
state :completed
# zun をもらったときの遷移規則
event :zun do
# 未完了状態のまま
transitions from: :running, to: :running do
# 遷移後にカウントアップ
after{@zun_count += 1}
end
end
# doko をもらったときの遷移規則
event :doko do
# ズンが十分溜まっていれば未完了状態から完了状態に遷移する
transitions from: :running, to: :completed,
guard: :adequate_zun?
# 上記の規則に当てはまってなかったら未完了状態のまま
transitions from: :running, to: :running do
# 遷移後にカウンターをリセットする
after{@zun_count = 0}
end
end
end
# 十分な数のズンが溜まっているか
def adequate_zun?
@zun_count >= NECESSARY_ZUN_COUNT
end
end
zundoko = Zundoko.new
zundoko_strings = {zun: "ズン", doko: "ドコ"}
loop do
m = zundoko_strings.keys.sample
print zundoko_strings[m], " "
zundoko.send m
if zundoko.completed?
puts "キヨシ"
break
end
sleep 0.1
end
transition
にはブロックを与えることができて,遷移の前や後に何かをやらせることができる。
また,現在の状態と遷移メソッドだけで遷移先を決めるのではなく,guard
を使って条件を付けることができる。判定メソッドの名前をシンボルで与えればいい。
全部をクラスに詰め込むことも
コード 1 も 2 も,Zundoko クラスの役割は状態遷移に特化していて,ズン/ドコを与えることや画面表示などは外部で行っていた。
しかし,全部を Zundoko に詰め込んでもいい。