Ruby
Rails
Trailblazer

Railsアプリに秩序をもたらすGem、Trailblazerとは?

More than 1 year has passed since last update.

ここ1年で、Trailblazerの認知度は急速に上がってますよね。

BestGemsで見てみるとここ1年でめっちゃ伸びてます。

スクリーンショット 2017-09-14 18.16.31.png

1月時点で6万4千DLだったのが、9月現在で既に15万を突破しているので、今年だけで倍以上のダウンロードですね…!

今回はそんな要チェックなGem、Trailblazerについて簡単に見ていきたいと思います。


Trailblazerとは

Trailblazerは、RailsやSinatraなどで綺麗にビジネスロジックを書くためのGemです。

ModelやControllerが肥大化してカオスになっているアプリケーションに秩序をもたらしてくれます。

…なのですが、「具体的に何をするGemなの???」ってことですよね。

Trailblazerは複数のGemから構成されています。今回は代表的なGemである OperationReformCellsRepresentable を見ていきたいと思います。


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モデルに書くのってどうなんだろう? :cold_sweat:」という悩みからも解放されますね!

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も便利かもしれません(使ったことはないです :sweat_smile: )。


おわりに

Trailblazerはカバーする範囲が広いので「これをやるGemです!」となかなか言いづらいのですが、あえて言うなら「RailsのコードががスッキリするGem」という感じでしょうか。

Trailblazerでは今回紹介したGem以外にも様々なGemが使えますが、全てを使う必要はありません。

弊社では、JSONの変換ロジックをControllerやModelに書きたくないという課題感でRepresentableから使い始めて、しばらくしてOperationとReformを導入し、いまCellsを導入しているところです。

その間、普通に過去のコードも動いているので、じわじわと移行しています。特殊なテクニックを使わなくても、Trailblazer化していないコードと共存できる点は素晴らしいですね。

1つずつ使っても十分メリットありますので、皆さんも興味のある機能から使い始めてみてはいかがでしょうか。


参考

最近頑張って入門記事も書いたので(弊社メンバーと若干被りました :sweat_smile: )、実際に使ってみたいな、という方は以下リンクから是非。

Trailblazerは日本語記事がとても少ないので、ちょっとずつでも増やしていきたい所存です :thumbsup:

Trailblazerを使い、Railsのモデルの肥大化問題からサヨナラする :

OperationとReformのもう少し詳しい内容がまとまってます

Railsでサービスクラスとフォームオブジェクトを使ってサッと良い感じにする :

Railsチュートリアルのコードを使用して、実際にOperationとReformの導入を行います





  1. 次期バージョンの2.1では、さらに柔軟な制御ができるようになります! こうしたアップデートもTrailblazerの魅力です。 



  2. Helperがmix-inされる時のメソッド衝突の恐怖を経験した方もいるでしょう :scream:  



  3. いわゆるシリアライザー…なのかもしれませんが、シリアライザーというワードで思い浮かべるものに(世間的に)幅があるような気がします :thinking: