Rails Jbuilderのあまり知られてないかもしれない?メソッド3選
こんにちは山田コーダーです。先日Rails Jbuilderのコードを読んでみたら存在自体を知らなかったメソッドを3つ発見しましたので、今日はそれらを紹介したいと思います。
■その1:merge!
Jbuilderテンプレート内ではextract!
メソッドを利用して、ActiveRecordオブジェクトのattributesを簡単に列挙することができます。
※Scaffoldで自動生成されるJbuilderテンプレートも以下のような感じですね!
json.extract! @item, :id, :name, :number, :public, :created_at, :updated_at
でもこれ、カラム数が多いテーブル全体を表示したい時はちょっと面倒じゃないでしょうか?行が長くなってRuboCop等の規約チェックツールに怒られますし、カラムの追加・削除の度にテンプレートの修正が必要になります。
という訳で弊社では試行錯誤の結果、RubyのSplat演算子(*)を用いて以下のように記述していました。
json.extract! @item, *@item.attribute_names
が、これでもまだ冗長に感じ、extract_all!
みたいなメソッドは無いのかな?とチームメンバーにたずねた所、彼がStack Overflowで質問してくれて、merge!
というメソッドがあるよ!との回答を頂きました。
※この回答をしてくれたかた日本の方のようです。ありがとうございました!
なおこのメソッドを利用するにはJbuilder 2.0以上が必要になります。
Rails 4.0系のGemfileは
gem 'jbuilder', '~> 1.x'
となってることが多いと思いますので以下のように修正します。
※Rails 4.1系であれば、すでに以下のようになってると思います。
gem 'jbuilder', '~> 2.0'
これでめでたくmerge!
メソッドを利用して、以下のようにスッキリ書けるようになりました。もちろんネストした要素内やpartial内でも利用できます。
json.merge! @item.attributes
※merge!
メソッドはextract!
メソッドと違い、「要素の摘出」ではなく「ハッシュをここにマージしてください」という意味合いのメソッドのようです。そのためハッシュ(.attributes
)を渡してあげる必要があります。
Jbuilderから少し話しがそれますが、created_at
やupdated_at
を表示させたくない場合は(そもそもDBから取得しないのがベストですが)、ActiveSupportのHash拡張のexcept
メソッドを利用すれば以下のように書くことも可能です。
json.merge! @item.attributes.except('created_at', 'updated_at')
ちなみに残念なことにここでシンボルが使えません…。シンボルを使いたい場合は、同じくActiveSupportのHash拡張のwith_indifferent_access
メソッドを利用して以下のように書くことも可能です。が、こんなのテンプレートに書いてられませんね…。こうなったら何か別の方法を探すか、大人しくexcept!
で列挙する方がいいかもしれません。
json.merge! @item.attributes.with_indifferent_access.except(:created_at, :updated_at)
■その2:ignore_nil!
よく考えたら上記のような問題、わざわざStack Overflowで聞かなくてもコードを読めば分かることでしたね。反省。
という訳でコードを読んでみたらmerge!
の他にもJbuilderのREADEME.mdで利用方法が紹介されてないメソッドがあと2つありましたので、それらを紹介したいと思います。
二つ目はignore_nil!
です。例えば以下のコード、@item.number
がnilの場合はJSON上はnumber: null
と表示されます。
json.number @item.number
この時「値がnilの場合はそもそも要素自体を表示させたくない」という場合は以下のようにifやunlessでの制御が必要になります。
json.number @item.number unless @item.number.nil?
でもこのコードもちょっと汚いですよね。
このような場合、ignore_nil!
を利用すれば値がnilの時は要素自体が表示されなくなります。なおこの設定は、ignore_nil!
メソッドの記述直後に有効化され、元の「nilをignoreしない状態」に戻すには、ignore_nil! false
を記述します。
json.var1 nil json.ignore_nil! json.var2 nil json.ignore_nil! false json.var3 nil
結果(var2: null
は表示されていません):
{ var1: null, var3: null }
こちらはクラスメソッドも用意されていますので、設定ファイルに以下を記述しておけば、アプリケーション全体でこの設定を有効化できます。
Jbuilder.ignore_nil
■その3:child!
最後がchild!メソッドです。ブロックを引数に取り、現在の要素内に配列の子要素を追加します。
json.users do json.child! do json.name 'yamada' json.email 'yamada@example.com' end json.child! do json.name 'yamada2' json.email 'yamada2@example.com' end end
結果:
users: [ { name: "yamada", email: "yamada@example.com" }, { name: "yamada2", email: "yamada2@example.com" } ]
しかしながら実際にはJbuilderテンプレートの中で配列を構築しなければいけないパターンはあまりなさそうで、このメソッドの有用な使い方はよく分かりませんでした。
ひょっとすると既存の配列の最初や最後に、そのテンプレート特有の要素を追加する場合等に利用できるかもしれません。
json.array!(@items) do |item| json.extract! item, :name, :number end json.child! do json.name 'DUMMY' json.number 9999 end
結果:
[ { name: "yamada1", number: 1 }, { name: "yamada2", number: 2 }, { name: "DUMMY", number: 9999 } ]
以上です。
他にもcache!
やkey_format!
等のメソッドもありますが、こちら利用法が紹介されていますので、是非確認してみてくださいませ!