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!等のメソッドもありますが、こちら利用法が紹介されていますので、是非確認してみてくださいませ!