Help us understand the problem. What is going on with this article?

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

More than 3 years have 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:  

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away