ここ1年で、Trailblazerの認知度は急速に上がってますよね。
BestGemsで見てみるとここ1年でめっちゃ伸びてます。
1月時点で6万4千DLだったのが、9月現在で既に15万を突破しているので、今年だけで倍以上のダウンロードですね…!
今回はそんな要チェックなGem、Trailblazerについて簡単に見ていきたいと思います。
Trailblazerとは
Trailblazerは、RailsやSinatraなどで綺麗にビジネスロジックを書くためのGemです。
ModelやControllerが肥大化してカオスになっているアプリケーションに秩序をもたらしてくれます。
…なのですが、「具体的に何をするGemなの???」ってことですよね。
Trailblazerは複数のGemから構成されています。今回は代表的なGemである Operation 、 Reform 、 Cells、 Representable を見ていきたいと思います。
Operation
まずはOperationですね! いわゆるサービスクラスを作るためのGemです。
ControllerやModelなどから複雑なビジネスロジックを切り離し、サービスクラスとして記述できるようになります。
特徴的なのは、強力な処理フローの制御。ぐっちゃぐちゃなネストの深いControllerのアクションとは強制的におさらばさせられます。1
また、サービスクラスを記述する上での細かいマクロ群もあり、なかなか使いやすいと思います。
class User::Create < Trailblazer::Operation
step Model(User, :new)
step Contract::Build(constant: MyContract) # Formオブジェクトをビルド
step Contract::Validate() # validationする
step Contract::Persist() # modelにデータを反映して保存する
end
Reform
Reformは、いわゆるフォームオブジェクトを作るためのGemです。
Modelなどからフォームに関連する処理を切り離してくれます。
例えば、バリデーションであったり(Reformを使うとModelではなくフォームオブジェクトでバリデーションします)、accepts_nested_attributes_for
のような複数のModelをフォームで表現するための処理などを書きます。
Modelの前にフォームオブジェクトが入ってくれるので、「ユーザ登録画面は利用規約のチェックが必要なんだけど、このバリデーションをUserモデルに書くのってどうなんだろう? 」という悩みからも解放されますね!
module User::Contract
class Create < Reform::Form
property :name, validates: { presence: true, length: { maximum: 50 } }
property :password, validates: { presence: true, length: { minimum: 16 }
end
end
Cells
Cellsは、いわゆるビューモデルを作るためのGemです。Modelなどから表示用のメソッドなどを切り離せます。
例えば、ユーザの状態をstatus
というpropetyに 数値 で入っている場合に、"有効"
と"無効"
という 文字列 で表現したい場合、その変換コードをCellに書きます。
「そんなのModelに書かずにHelperに書くよ」という方も多くいらっしゃるでしょう。
Cellの良い点は、Helperと違ってインスタンス化されたオブジェクトだという点です。一般的なRubyのオブジェクト指向としての利点、つまり「継承」「カプセル化」「ポリモーフィズム」などを扱えるようになります。
例えば、Cellsのクラスと特定のViewをセットにして自由に閉じた空間に置けるので、大きな空間を汚さない安心感2や可読性が得られると思います。
class BookCell < Cell::ViewModel
property :author
def show
render
end
private
def author_link
link_to "#{author.email}", author
end
end
Representable
Representableは、ObjectをJSONやYAML、XMLに変換するためのGemです。3 逆にJSONからObjectに変換する機能もあります。
JSONを返すAPIを作る時にはModelの情報をJSON変換すると思いますが、to_json
だけでは済まないこともしばしばありますよね。カラムとしては存在しないプロパティを出力するためにModelにメソッドを作ったり、数値を文字に変換する必要があったり。
そうした処理をModelに書くと肥大化していきますが、Representableを使えば切り離すことができます。
class UserRepresenter < Representable::Decorator
property :id
property :name, exec_context: :decorator
def name
"#{represented.first_name} #{represented.last_name}"
end
end
ちなみに、ほかの用途としてAPIの仕様書やモックを作る時などにも使用できます。
RoarというGemがTrailblazerにありますが、こちらはRepresentableを内部的に使ってAPIのレスポンスをレンダーしたりパースしたりできます。
APIのクライアントも作れるGemなので、マイクロサービスを作ろうとしている方にはRoarも便利かもしれません(使ったことはないです )。
おわりに
Trailblazerはカバーする範囲が広いので「これをやるGemです!」となかなか言いづらいのですが、あえて言うなら「RailsのコードががスッキリするGem」という感じでしょうか。
Trailblazerでは今回紹介したGem以外にも様々なGemが使えますが、全てを使う必要はありません。
弊社では、JSONの変換ロジックをControllerやModelに書きたくないという課題感でRepresentableから使い始めて、しばらくしてOperationとReformを導入し、いまCellsを導入しているところです。
その間、普通に過去のコードも動いているので、じわじわと移行しています。特殊なテクニックを使わなくても、Trailblazer化していないコードと共存できる点は素晴らしいですね。
1つずつ使っても十分メリットありますので、皆さんも興味のある機能から使い始めてみてはいかがでしょうか。
参考
最近頑張って入門記事も書いたので(弊社メンバーと若干被りました )、実際に使ってみたいな、という方は以下リンクから是非。
Trailblazerは日本語記事がとても少ないので、ちょっとずつでも増やしていきたい所存です
Trailblazerを使い、Railsのモデルの肥大化問題からサヨナラする :
OperationとReformのもう少し詳しい内容がまとまってます
Railsでサービスクラスとフォームオブジェクトを使ってサッと良い感じにする :
Railsチュートリアルのコードを使用して、実際にOperationとReformの導入を行います