4. View に関する変更点
2013/10/03 シナジーマーケティング(株) 鈴木 圭
[Rails 4.0] 第4章 View に関する変更点
4.1. .jbuilder テンプレート
Rails4.0 では jbuilder という JSON を生成するライブラリが標準で導入されました。
scaffold で生成されたコードを確認すると分かりやすいのですが、app/views ディレクトリに .jbuilder という拡張子のファイルが生成されます。
例えば「rails generate scaffold users name:string email:string」とすると app/views/users/index.json.jbuilder などのファイルが生成されます。
生成されたファイルの内容は次のように jbuilder を用いて JSON を生成するようになっています。
json.array!(@users) do |user| json.extract! user, :name, :email json.url user_url(user, format: :json) end
4.2. .ruby テンプレート
Rails4.0 では .ruby という拡張子の View ファイルを作成することもできます。
例として以下のコードを見てください。
# app/controllers/index_controller.rb class IndexController < ApplicationController def very_long_message # コントローラで長い文字列を組み立てると非常に見辛くなります. render text: <<-'EOS'.strip_heredoc とても非常に長いテキスト とても非常に長いテキスト とても非常に長いテキスト とても非常に長いテキスト とても非常に長いテキスト EOS end end
render で非常に長い文字列を返しているため、コントローラのコードが見づらくなっています。
これを .ruby テンプレートを使うように書き換えると次のようになります。
# app/controllers/index_controller.rb class IndexController < ApplicationController def very_long_message end end # app/views/users/very_long_message.text.ruby <<-'EOS'.strip_heredoc とても非常に長いテキスト とても非常に長いテキスト とても非常に長いテキスト とても非常に長いテキスト とても非常に長いテキスト EOS
コントローラのコードがほとんど無くなり、テキストを生成する処理が app/views/users/very_long_message.text.ruby に移動しました。
HTML であれば当たり前のように app/views にファイルを置きますが、テキストを返す場合となるとコントローラで長い文字列を構築してしまうことは多いのではないでしょうか。
短い文字列であれば問題ありませんが、長い文字列を返すような場合には .ruby テンプレートを利用することでコントローラがすっきりします。(.ruby テンプレートはテキスト以外でも使用することができます。例えば HTML であれば very_long_message.html.ruby のようなファイル名にします。)
ポイントは View の仕事をコントローラでやりすぎない、ということです。
4.3. HTML5 用のヘルパーメソッド
Rails4.0 では以下の HTML5 用のヘルパーメソッドが追加されました。
- week_field
- month_field
- datetime_field
- datetime_local_field
- color_field
- time_field
- date_field
- highlight
HTML5 への対応はブラウザごとに異なり、現状では十分にサポートされているとは言えません。使用する時は対象とするブラウザでサポートされているか確認しましょう。
4.4. collection_check_boxes, collection_radio_buttons
コレクションからリストボックスを生成する collection_select の親戚として collection_check_boxes 及び collection_radio_buttons が追加されました。
以下のように使用します。
collection_check_boxes :user, :organization_ids, Organization.all, :id, :name collection_radio_buttons :user, :organization_ids, Organization.all, :id, :name
4.5. cache_if, cache_unless
条件付きでキャッシュを行う cache_if 及び cache_unless が追加されました。
次のように使用します。
<%= cache_if @article.status == 'published', @article do %> <%= @article.title %> <%= @article.status %> <%= @article.body %> <% end %>
例えば Blog や CMS を作成しているとして、コンテンツが下書き状態のときには頻繁に変更されるのでキャッシュしない、公開状態の場合は変更されることは無いのでキャッシュしたい、という場合に cache_if や cache_unless を使用するという使い方が考えられます。
4.6. Russian Doll Caching
Rails4.0 ではフラグメントキャッシュについて、以下の改善が行われました。
- テンプレートファイルのハッシュ値 (MD5) がキャッシュのキーに含まれるようになった。
– 以前はキャッシュのキーを手作業で変更する必要があった。 - ネストしている内側のテンプレートを変更すると、外側のテンプレートのキャッシュキーが自動的に変化する。
- 以前は内側のテンプレートを変更するときに、外側テンプレートのキーを手作業で変更する必要があった。 - ネストしたテンプレートの一部が変更された場合、変更された部分のキャッシュだけが再生成される。
- それ以外のキャッシュは再利用されるため、キャッシュのヒット率が高い。
それではネストしているフラグメントキャッシュを効果的に管理する方法を、具体的なコードで説明します。
まず、ユーザ(User)がいくつかの記事(Article)を持つという、一対多の関係にあるモデルがあるとします。
ユーザ(User)のモデルは以下の通りです。
# app/models/user.rb class User < ActiveRecord::Base has_many :articles end
そして、記事(Article)のモデルは次の通りです。
# app/models/article.rb class Article < ActiveRecord::Base # 変更された時に User の更新日時も自動更新させるために touch: true を指定する. belongs_to :user, touch: true end
Article が変更されたときに User の更新日時も自動的に更新するために、belongs_to で touch: true を指定しています。
次は View です。
ユーザの詳細画面(views/users/show.html.erb)は以下の通りです。
<!-- 外側のフラグメントキャッシュ --> <% cache @user do %> [User] <%= user.name %><br /> <% user.articles.each do |article| %> <%= render article %> <% end %> <% end %>
ユーザの名前とそのユーザが持つ記事(Article)の情報を表示(render article)しています。
また、全体を「<% cache @user do > ~ < end %>」で囲むことで、この範囲をキャッシュさせています。
ネストした内側のテンプレートとなる記事の画面(views/articles/_article.html.erb)は次の通りです。
<!-- 内側のフラグメントキャッシュ --> <% cache article do %> [Article] <%= article.title %><br /> <% end %>
こちらも全体を「<% cache article do > ~ < end %>」で囲むことでキャッシュさせています。
次にキャッシュの動きを確認したいのですが、development モードではデフォルトでキャッシュが行われません。キャッシュを有効にするには config/environments/development.rb などで「config.action_controller.perform_caching = true」としておく必要があります。
この状態でサーバを起動し、http://localhost:3000/users/1 などを確認すると、次のように表示されます。
それでは内側のテンプレートで表示されている記事のデータを変更してみます。
article = Article.where(title: 'article 1').first article.update(title: 'article 1 CHANGED')
ブラウザの画面を更新すると、次のように表示内容が変わります。(ここまでは Rails3 でも同じです。)
次にネストした内側のテンプレートである記事の画面(views/articles/_article.html.erb)を変更します。
<!-- 内側のフラグメントキャッシュ --> <% cache article do %> [Article] <%= article.title %> (テンプレートの内容を変更しました)<br /> <% end %>
ここでブラウザの画面を更新しても何も変わりません。最初に「テンプレートファイルのハッシュ値 (MD5) がキャッシュのキーに含まれるようになった。」と書きましたが、テンプレートファイルのハッシュ値はアプリケーションが起動したときに一度だけ行われるためです。変更を反映するためにはアプリケーションを再起動します。(※通常、本番環境ではアプリケーションを更新するごとに再起動するため、再起動しなければテンプレートファイルの変更が反映されないことはデメリットではないでしょう。)
再起動後、ブラウザの画面を更新すると、次のように表示内容が変更されることが分かります。
以上のように、Rails4.0 ではテンプレートの内容を変更した場合でも、手作業でキャッシュのキーを変更する必要はなくなりました。
4.7. button_to_function, link_to_function は非推奨
button_to_function 及び link_to_function は非推奨となり、Rails4.1 では削除される予定です。
これらのメソッドを使わない代わりに、「Unobtrusive JavaScript」と呼ばれるアプローチが推奨されています。「Unobtrusive JavaScript」とは、onclick などの属性に Javascript コードを埋め込まず、HTML と Javascript コードを分離するアプローチのことです。
以下のコードを見てください。
<%= link_to_function('クリック!', 'alert("クリックしました")') %>
これを Unobtrusive JavaScript の考え方に沿って書き直すと、次のようになります。
<a href="#" id="hikaeme">クリック!</a> <%= javascript_tag do %> $("#hikaeme").click(function() { alert("クリックしました"); return false; }); <% end %>
Javascript コードは外部ファイルに切り出すことができるので、HTML コードは非常にすっきりします。
4.8. link_to などの confirm, disable_with は非推奨
button_to, button_tag, image_submit_tag, link_to そして submit_tag のオプション confirm と disable_with は非推奨となり、今後は以下のように書き換える必要があります。
<!-- 変更前 --> <%= link_to('クリック', '/', confirm: 'OK?') %> <!-- 変更後 --> <%= link_to('クリック', '/', data: {confirm: 'OK?'}) %>
4.9. まとめ
View に関する変更はいくつもありますが、
- View の仕事をコントローラでやりすぎない
- フラグメントキャッシュの管理がやりやすくなった
- Unobtrusive JavaScript: HTML と Javascript コードを分離する
あたりがポイントでしょう。
次回はコントローラに関する変更点を解説します。