Rails開発の品質を向上!fizzy Gemで不正なタスク移動を防ぎ、堅牢なワークフローを構築する方法
わたくし、メイドのアルファと申します。今回は、ベースキャンプ(Basecamp)さんが公開されている、とっても素敵な「fizzy(フィジー)」というライブラリについて、ソフトウェアエンジニアのお嬢様のお役に立てるよう、コント仕立てで、分かりやすくご説明させていただきますね!
fizzyは、Basecampの有名なプロダクトマネージャーであるジェイソン・フリード氏らが提唱する、「カンバン(Kanban)」の考え方を、Ruby on Railsアプリケーションで、とってもシンプルかつ、スマートに実装するためのライブラリでございます。
「カンバン」と聞くと、タスクを「ToDo」「In Progress」「Done」といった列に並べる、あのボードを思い浮かべますわよね? でも、fizzyが目指しているのは、その本質的な部分なのです。
メイドA(アルファ)
「お嬢様、お困りではないでしょうか? あら、このタスクボード、列が多すぎてごちゃごちゃですわね... 」
お嬢様(ユーザー)
「そうなのよ、アルファ。タスクの移動ルールが複雑で、どの列からどの列へ動かせるか、全然わからないの...」
メイドB(ベータ) 「fizzyをお使いになれば、そんなお悩みは一発解消ですわ! fizzyは、タスクを『特定の状態(ステータス)』に、そして、その状態を変化させる『特定の操作(トランジション)』に焦点を当てます。」
| 特徴 | fizzyでの表現 | 従来のカンバンとの違い |
| 状態(列) | Stated (例: :todo, :doing, :done) | シンプルなステートマシン |
| 変化(移動) | Transitions (例: to_doing, to_done) | 許可された操作に限定 |
アルファ 「つまり、fizzyは『状態遷移』という、オブジェクト指向の基本的な考え方を、カンバンに持ち込むことで、ルールのあいまいさや不正なタスク移動を、きゅっと締め出してくれますのよ!」
fizzyは、Rubyのライブラリ(Gem)として提供されております。導入は、とっても簡単ですわ!
お嬢様のRailsプロジェクトのGemfileに、この一行をそっと書き加えてください。
# Gemfile
gem 'fizzy'
そして、ターミナルでおまじないを唱えます。
$ bundle install
これで、fizzyの魔法が、プロジェクトにかけられました!
fizzyの最大の魅力は、モデル(Model)の中に、タスクの状態とその遷移ルールを、まるで物語のように書き込める点です。
わたくしどもが使うTaskモデルに、カンバンの魔法をかけてみましょう。
# app/models/task.rb
class Task < ApplicationRecord
# fizzyをモデルにインクルード!
include Fizzy::State
# -------------------------------------
# ♀ ステート(状態)の定義
# -------------------------------------
# 初期状態は「todo」です。
state :todo, default: true
state :doing
state :done
# -------------------------------------
# トランジション(遷移)の定義
# -------------------------------------
# 1. 「todo」から「doing」へ移動する操作
transition :start_work do
# 許可される元の状態
from :todo
# 遷移先の状態
to :doing
# 遷移時のコールバック(任意)
on_success { |task| task.started_at = Time.current }
end
# 2. 「doing」から「done」へ移動する操作
transition :finish_work do
from :doing
to :done
on_success { |task| task.finished_at = Time.current }
end
# 3. 「doing」から「todo」へ差し戻す操作
transition :revert_to_todo do
from :doing
to :todo
end
# -------------------------------------
# その他の設定(任意)
# -------------------------------------
# 状態を保存するデータベースのカラム名を設定
fizzy_column :status
end
メイドA(アルファ)
「お嬢様、新しいタスクを作ってみましょう!」
# 新しいタスクを作成
task = Task.create!(title: "お掃除リストの作成")
# => #<Task id: 1, title: "お掃除リストの作成", status: "todo", ...>
# 現在の状態を確認
task.status
# => "todo"
# 状態遷移メソッドの自動生成
task.can_start_work?
# => true (todo -> doing は許可されています)
# 遷移を実行!
task.start_work!
# => true
task.status
# => "doing"
# 許可されていない遷移を試みる
task.can_finish_work?
# => true (doing -> done は許可)
# 存在しないトランジションは実行できません
# task.start_work! # 呼び出すと例外が発生します
メイドB(ベータ)
「ご覧ください、お嬢様! start_work!というメソッドを実行するだけで、statusカラムが自動でtodoからdoingに変わりました!しかも、on_successブロックに書いたタイムスタンプの記録もバッチリです!」
アルファ
「そうなんです。fizzyは、『不正な状態遷移』を、メソッドの実行という形で厳しくガードしてくれるので、アプリケーションの信頼性が格段に向上しますのよ! これで、複雑な条件分岐のコードとも、さようならです!」
fizzyは、ただのカンバンボードを作るツールではございません。お嬢様のエンジニアリングライフを豊かにする、こんな素敵なメリットがございます。
ビジネスロジックの明確化
タスクの状態と、状態間の移動ルール(トランジション)が、モデル内に一箇所に集中して定義されます。これは、状態機械(State Machine)のパターンとして、コードの意図を非常に明確にします。
不正な操作の防止
can_transition_name?やtransition_name!といったメソッドが自動生成されるため、許可されていない状態遷移を、実行前に簡単にチェックしたり、実行時に例外としてキャッチしたりできます。
テストの容易性
すべてのルールがモデルに集約されているため、「この状態の時に、この操作は可能か/不可能か」というテストケースを、非常にシンプルに書くことができます。
UI/UXとの親和性
フロントエンドでは、task.can_start_work?の結果に応じて、「作業開始」ボタンを表示したり非表示にしたりといった制御が、サーバー側と完全に同期して行えます。
アルファ
「いかがでしたでしょうか、お嬢様? fizzyのシンプルさと強力さ、お分かりいただけましたでしょうか? 複雑な状態管理は、すべてわたくしにお任せくださいね!」
わたくし、メイドのアルファがお役に立てることは、他にございますでしょうか? 例えば、
「fizzyのトランジションに、特定のユーザー権限のチェックを追加する例」
「Railsのコントローラーで、fizzyのメソッドをどう使うか」