#320 Jbuilder
- Download:
- source codeProject Files in Zip (102 KB)
- mp4Full Size H.264 Video (20.9 MB)
- m4vSmaller H.264 Video (10.5 MB)
- webmFull Size VP8 Video (11.6 MB)
- ogvFull Size Theora Video (22.7 MB)
Rails 3.2アプリケーションのgemfileを見ると、コメントの中にJbuilderという以前のバージョンには含まれていなかったgemがあるのに気づくでしょう。
# To use Jbuilder templates for JSON # gem 'jbuilder'
JbuilderはJSONレスポンスを出力するテンプレートエンジンです。David Heinemeier Hanssonが最近作成したものですが、Rails 3.2には含まれずに独立したgemとしてリリースされました。このアプローチをとることによって、以前のバージョンのRailsでも利用可能になっています。JbuilderはJSONを生成するための、XML Builderに似た便利なDSLを提供します。今回のエピソードでその使い方を紹介します。
アプリケーションにJSONレスポンスを追加する
Jbuilderの機能を実際に見ていくために、簡単なブログアプリケーションを使用します。このアプリケーションには複数の記事がありますが、これに各記事のJSON版を追加して、記事のURLの後ろに.json
を付けることで取得できるようにします。これを今試してみると、この機能をまだ追加していないのでエラーメッセージが表示されます。
ArticlesController
のshow
アクションにrespond_to
ブロックを追加することで、Jbuilderを使わずにこれを追加することができます。
def show @article = Article.find(params[:id]) respond_to do |format| format.html format.json { render json: @article } end end
ページをリロードすると、記事がJSON形式で出力されます。
レスポンスをカスタマイズする
返されたJSONには記事の属性がすべて含まれますが、それをカスタマイズしたい場合はどうすればいいでしょうか? この辺りから話がややこしくなってきます。記事に対してas_json
を呼び出すことで、返された値をカスタマイズできます。例えば記事の著者と一緒にid
、name
、content
の各フィールドと、その記事へのコメントのこれもまた同じ3つのフィールドが欲しいとします。
def show @article = Article.find(params[:id]) respond_to do |format| format.html format.json { render json: @article.as_json(only: [:id, :name, :content], include: [:author, {comments: {only:[:id, :name, :content]}}]) } end end
これを試すためにページをリロードします。するとカスタマイズされたJSONレスポンスが、関連するAuthor
とComment
レコードと一緒に出力されます。
Jbuilderを使う
これはうまくいきますが、使用したコードはあまりきれいではありません。モデル内のas_json
をオーバーライドすることもできますが、それでもあまりきれいにはなりません。そこでJBuilderを利用します。インストールするにはgemfileの該当する行を非コメント化してbundle
コマンドを実行します。
# To use Jbuilder templates for JSON gem 'jbuilder'
コントローラに戻ってrespond_to
の呼び出しを削除して、リクエストされた形式のテンプレートを探すというデフォルトの振る舞いに戻します。
def show @article = Article.find(params[:id]) end
次に/app/views/articles
ディレクトリにJSONテンプレートを作成します。そこでRubyコードを使ってJSONの出力を定義します。json
オブジェクトにアクセスし、次のように属性を定義できます。
json.id @article.id json.name @article.name
gemをインストールした後にサーバを再起動した上でページをリロードするとカスタム形式の出力が表示されます。
このように一つずつ属性を抽出するのが手間な場合もあります。そこでJSONオブジェクトに対してextract!
を呼び出して、オブジェクトと、それに対して呼び出したいメソッドか属性のリストを渡します。
json.extract! @article, :id, :name, :published_at
これにももう一つ別の記法があります。
json.(@article, :id, :name, :published_at)
これはRuby 1.9でのみ有効です。というのも、オブジェクトに対してバックグラウンドでcall
を呼び出し、前と同じようにextract!
メソッドを呼び出すからです。このようにビューテンプレートでJSONを出力する方法の利点は、すべてのヘルパーメソッドにアクセスできるという点です。これはURLを出力するのに特に便利です。現在のユーザがadminの場合のみJSONに記事の編集用のURLを含めたいとします。もし使用している認証機能がヘルパーメソッドとしてcurrent_user
メソッドを提供している場合は、それにもアクセスできます。
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin?
現在のユーザはadminなので、JSONにリンクが含まれています。
ネスティング
今回のアプリケーションでは、Article
はAuthor
に属し(belongs to)ます。著者の属性を含めたい場合、ひとつの方法は次の通りです。
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin? json.author @article.author, :id, :name
これはAuthor
の属性を希望通りにネストします。
著者の属性をリスト表示するよりも複雑な操作をする場合、例えば著者へのURLを設定したいのであれば、次のようにauthor
の呼び出しに対してブロックを渡すことができます。
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin? json.author do |json| json.(@article.author, :id, :name) json.url author_url(@article.author) end
ページをリロードすると、ネストされた著者の属性が出力され、そこにはURLも含まれます。
has_many
の関連についても同じような処理が可能です。例えば記事が複数のコメントを持っていたら、それらを直接追加して表示したい属性を並べます。
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin? json.author do |json| json.(@article.author, :id, :name) json.url author_url(@article.author) end json.comments @article.comments, :id, :name, :content
これでコメントも含まれます。
この方法と合わせてブロック記法を使用する場合、コメントの配列の各要素間を繰り返し処理しなくてはいけないので、書き方は少し変わります。jsonとcommentオブジェクトをブロックに渡すと、それらにアクセスしてそこで自由に操作できるようになります。
json.(@article, :id, :name, :published_at) json.edit_url edit_article_url(@article) if current_user.admin? json.author do |json| json.(@article.author, :id, :name) json.url author_url(@article.author) end json.comments @article.comments do |json, comment| json.(comment , :id, :name, :content) end
これは、基本的には一つ前のコードと同じことをおこないます。
部分テンプレート
気がついてみたらcommentブロック内の詳細情報が多くなりこの機能を他の場所でも重複して持たなくてはいけないという場合、部分テンプレート(Partial)を使うことができます。これはビューの部分テンプレートと同じように機能します。これを使うにはjson
オブジェクトに対してpartial!
を呼び出して、部分テンプレートへのパスかあるいは単にオブジェクト(今回の場合はcomment)を渡します。
json.comments @article.comments do |json, comment| json.partial! comment end
これはapp/views/comments
ディレクトリの下で_comment.json.jbuilder
という部分テンプレートを探します。この部分テンプレートで同じjson
オブジェクトにアクセスでき、commentブロックでおこなったのと同じことができます。またpartial!
の呼び出しで渡しているので、commentオブジェクトにもアクセスできます。
json.(comment, :id, :name, :content)
これによって、前と同じJSONが出力されます。
代替ツール
このような機能を持ったgemはJbuilderだけではありません。プロジェクトのREADMEファイルの最後に、導入を検討するに値する代替ツールのリストがあります。これらの中ではRABLがもっとも人気があり、これも将来のエピソードで取り上げたいと思います。