Railsのjbuilderの書き方と便利なイディオムやメソッド

[株式会社TECH LUCK](https://techluck.co.jp/?utm_source=qiita&utm_medium=article&utm_campaign=myarticle)という会社で代表兼エンジニアをしている齊藤です。

DXプロジェクト、開発プロジェクト、Rails開発などでお困りごとがありましたら[弊社HP](https://techluck.co.jp/?utm_source=qiita&utm_medium=article&utm_campaign=myarticle)からご相談をいただけますと幸いです。
以下のような問題に対応することが可能です。
- プロジェクトでRailsエンジニアが足りなくて困っている
- Railsのバージョンアップをしたいがノウハウ・リソースが足りなくて困っている
- オフショア開発をしているが、要件の齟齬やコード品質が悪いので改善したい

また、Railsエンジニアも募集しておりますので、興味がありましたら[弊社HP](https://techluck.co.jp/?utm_source=qiita&utm_medium=article&utm_campaign=myarticle)からご連絡いただけますと幸いです。

# 前提

Railsのjbuilderでjsonを返す場合のメソッドやイディオムをまとめました。
まだまだjbuilder初心者なので、他の便利なメソッドやイディオムがありましたら、教えていただけると幸いです。


# インスタンス変数を使わないでjsonを返す
まずはインスタンス変数を使わないで、特定のjsonを返す記述の仕方を列挙します。

### 単純な書き方
以下のように記述すると、単純なキーとバリューを返すことができます。

```index.json.jbuilder
json.text "テキスト"

# {"text": "テキスト"}
```

後ほど説明する`set!`を使っても、単純なキーとバリューを返すことができます。

```index.json.jbuilder
json.set! :text, "テキスト"

# {"text": "テキスト"}
```

複数のキーとバリューを返す時は以下のように記述します。

```index.json.jbuilder
json.text1 "テキスト1"
json.text2 "テキスト2"

# {"text1": "テキスト1", "text2": "テキスト2"}
```


### set!メソッド
属性をまとめることができるメソッドです。

```index.json.jbuilder
json.set! :tweet do
  json.text, "テキスト"
end

# {"tweet": {"text": "テキスト"} }
```

さきほどの知識を応用して以下のようにも書けます。

```index.json.jbuilder
json.set! :tweet do
  json.set! text, "テキスト"
end

# {"tweet": {"text": "テキスト"} }
```

属性をまとめて、複数の値を返すことができます。

```index.json.jbuilder
json.set! :tweet1 do
  json.text2 "テキスト2"
# json.set! text2, "テキスト2" でもOK
end

json.set! :tweet3 do
  json.text4 "テキスト4"
# json.set! text4, "テキスト4" でもOK
end

# {"tweet1": {"text2": "テキスト2"}, "tweet3": {"text4": "テキスト4"} }
```

キーが被ると合成された値になります。

```index.json.jbuilder
json.set! :tweet do
  json.text2 "テキスト2"
# json.set! text2, "テキスト2" でもOK
end

json.set! :tweet do
  json.text4 "テキスト4"
# json.set! text4, "テキスト4" でもOK
end

# {"tweet": {"text2": "テキスト2", "text4": "テキスト4"} }
```


# インスタンス変数を使ったjbuidlerの書き方

## 前提
アプリケーションとしては、Twitterの簡易版でユーザーがツイートを投稿できるサービスです。
テーブル構造としては
**user : tweet = 1 : N**
の関係になっています。
テーブル名はそれぞれ、`users`と`tweets`です。
tweetsテーブルには、`id`, `title`, `text`, `user_id`の4つのカラムがあります。

## インスタンス変数の中身が1つの時
コントローラでは単数のインスタンス変数を定義しています。

```tweets_controller.rb
class Api::TweetsController < ApplicationController
  def show
    @tweet = Tweet.find(params[:id])
  end
end
```

### 単純な書き方
特定の値を返す場合は以下のように記述します。

```show.json.jbuilder
json.text @tweet.text

# {"text": "テキスト1"}
```

複数の値を返す場合は以下のように記述します。

```show.json.jbuilder
json.text  @tweet.text
json.title @tweet.title

# {"text": "テキスト1", "title": "タイトル1"}
```

特定のキーでまとめて返す場合は以下のように記述します。

```show.json.jbuilder
json.tweet @tweet, :text, :title

# {"tweet": {"text": "テキスト1", "title": "タイトル1"}}
```

以下のように書くこともできます。

```show.json.jbuilder
json.tweet do |tweet|
  tweet.title @tweet.title
  tweet.text  @tweet.text
end

# {"tweet": {"title": "タイトル1", "text": "テキスト1"} }
```

また、`|tweet|`を取ってしまって、以下のようにも書くことができます。
（先ほどの`tweet.title`が`json.title`のように変わります。）

```show.json.jbuilder
json.tweet do
  json.text  @tweet.text
  json.title @tweet.title
end

# {"tweet": {"text": "テキスト1", "title": "タイトル1"} }
```

### extract!メソッド
引数のオブジェクトの中身を指定して返すことができるメソッドです。

```show.json.jbuilder
json.extract! @tweet, :text, :title

# {"text": "テキスト", "title": "タイトル"}
```

さきほど書いた、特定のキーでまとめて返す場合にも使うことができます。

```show.json.jbuilder
json.tweet do
  json.extract! @tweet, :text, :title
end

# {"tweet": {"text": "テキスト1", "title": "タイトル1"} }
```

### merge!メソッド
jsonの値に対して、ハッシュで値を追加することができるメソッドです。


```index.json.jbuilder
text_hash = { text: "テキスト" }

json.tweet do
  json.title "タイトル"
  json.merge! text_hash
end

# {"tweet": {"title": "タイトル", "text": "テキスト"} }
```

`json.merge! @tweet.attributes`と記述すると、オブジェクトの中身のすべての値を返すことができます。

```show.json.jbuilder
json.merge! @tweet.attributes

# {"id": 1, "text": "テキスト1", "title": "タイトル1", "user_id": 1}
```

こちらも、特定のキーでまとめて返す場合にも使うことができます。

```show.json.jbuilder
json.tweet do
  json.merge! @tweet.attributes
end


# {"tweet": {"id": 1, "text": "テキスト1", "title": "タイトル1", "user_id": 1} }
```

## インスタンスの中身が複数の時
コントローラでは複数のインスタンス変数を定義しています。

```tweets_controller.rb
class Api::TweetsController < ApplicationController
  def index
    @tweets = Tweet.all
  end
end
```

### array!メソッド
配列を回して値を返すことができるメソッドです。
返す値は配列になります。

```index.json.jbuilder
json.array! @tweets, :title, :text

# [{"title": "タイトル1", "text": "テキスト1"}, {"title": "タイトル2", "text": "テキスト2"}]
```

冗長ですが、以下のように書くこともできます。

```index.json.jbuilder
json.array! @tweets do |tweet|
  json.title tweet.title
  json.text  tweet.text
end

# [{"title": "タイトル1", "text": "テキスト1"}, {"title": "タイトル2", "text": "テキスト2"}]
```

特定のキーでまとめて、配列を回したい場合は以下のように記述します。

```index.json.jbuilder
json.tweet do
  json.array! @tweets, :title, :text
end

# {"tweet": [{"title": "タイトル1", "text": "テキスト1" }, {"title": "タイトル2",  "text": "テキスト2"}] }
```


# 参考にしたサイト
[Rails4でJSONを作るならto_jsonよりjbuilder](http://llcc.hatenablog.com/entry/2015/03/07/103121)
[Rails Jbuilderのあまり知られてないかもしれない？メソッド3選](http://tech.gmo-media.jp/post/90700224919/rails-jbuilder-merge-ignore-nil-child)
[jsonのjbuilderの使い方メモ](http://o.inchiki.jp/obbr/196)
[RailsでJSONを返却するAPIを作成する時のあれこれ](https://altarf.net/computer/rails/2523)