Action View の概要

このガイドの内容:

1 Action Viewについて

RailsにおけるWebリクエストはAction ControllerとAction Viewで扱われます。通常、Action Controllerは、データベースとのやりとりや、必要に応じたCRUD (Create/Read/Update/Delete) アクションの実行にかかわります。Action View はその後レスポンスを実際のWebページにまとめる役割を担います。

Action Viewのテンプレートは、HTMLタグの合間にERB (Embedded Ruby) を含む形式で書かれます。ビューテンプレートがコードの繰り返しでうずまって乱雑になるのを避けるために、フォーム・日付・文字列に対して共通の動作を提供するヘルパークラスが多数用意されています。アプリケーションの機能向上に応じて独自のヘルパーを追加することも簡単にできます。

Action Viewの一部の機能はActive Recordと結びついていますが、Action ViewがActive Recordに依存しているわけではありません。Action Viewは独立したパッケージであり、どのようなRubyライブラリとでも組み合わせて使用できます。

2 Action ViewをRailsで使用する

アプリケーションのapp/viewsディレクトリには、1つのコントローラごとに1つのディレクトリが作成され、そこにビューテンプレートファイルが置かれます。このビューテンプレートはそのコントローラと関連付けられています。これらのファイルは、コントローラ内にあるアクションごとに出力された結果をビューで表示するために使用されます。

scaffoldを使用してリソースを生成するときに、Railsがデフォルトでどんなことを行なうのか見てみましょう。

$ rails generate scaffold article
      [...]
      invoke  scaffold_controller
      create    app/controllers/articles_controller.rb
      invoke    erb
      create      app/views/articles
      create      app/views/articles/index.html.erb
      create      app/views/articles/edit.html.erb
      create      app/views/articles/show.html.erb
      create      app/views/articles/new.html.erb
      create      app/views/articles/_form.html.erb
      [...]

Railsのビューには命名規則があります。上で生成されたファイルを見るとわかるように、ビューテンプレートファイルは基本的にコントローラのアクションと関連付けられています。 たとえば、articles_controller.rbコントローラのindexアクションは、app/views/articlesディレクトリのindex.html.erbを使用します。 これらのERBファイルに、それらを内包するレイアウトテンプレートや、ビューから参照されるあらゆるパーシャル (部分テンプレート) が組み合わさって完全なHTMLが生成され、クライアントに送信されます。この後、本ガイドではこれらの3つの要素について詳細に説明します。

3 テンプレート、パーシャル、レイアウト

前述のとおり、Railsが出力する最終的なHTMLはテンプレートパーシャルレイアウトの3つの要素から成ります。 まずこれらについて簡単に説明いたします。

3.1 テンプレート

Action Viewのテンプレートはさまざまな方法で記述することができます。テンプレートの拡張子が.erbであれば、ERB (ここにRubyのコードが含まれます) とHTMLが含まれます。テンプレートの拡張子が.builderであれば、Builder::XmlMarkupライブラリの新鮮なインスタンスが使用されます。

Railsでは複数のテンプレートシステムがサポートされており、テンプレートファイルの拡張子で区別されます。たとえば、ERBテンプレートシステムを使用するHTMLファイルの拡張子は.html.erbになります。

3.1.1 ERB

ERBテンプレートの内部では、<% %>タグや<%= %>タグにRubyコードを含めることができます。最初の<% %>タグはその中に書かれたRubyコードを実行しますが、実行結果は出力されません。条件文やループ、ブロックなど出力の不要な行はこのタグの中に書くとよいでしょう。次の<%= %>タグでは実行結果がWebページに出力されます。

以下は、名前を出力するためのループです。

<h1>Names of all the people</h1>
<% @people.each do |person| %>
  Name: <%= person.name %><br>
<% end %>

ループの開始行と終了行は通常のERBタグ (<% %>) に書かれており、名前を出力する行は出力用のERBタグ (<%= %>) に書かれています。上のコードは、単にERBの書き方を説明しているだけではありません。Rubyでよく使用されるprintputsのような通常の出力関数はERBでは使用できませんのでご注意ください。以下のコードは誤りです。

<%# 間違い %>
Hi, Mr. <% puts "Frodo" %>

なお、Webページへの出力結果の最初と最後からホワイトスペースを取り除きたい場合は<%- および -%>を通常の<% および %>と交互にご使用ください (訳注: これは英語のようなスペース分かち書きを行なう言語向けのノウハウです)。

3.1.2 Builderテンプレート

BuilderテンプレートはERBの代わりに使用できる、よりプログラミング向きな記法です。これは特にXMLコンテンツの生成を得意とします。テンプレートの拡張子を.builderにすると、xmlという名前のXmlMarkupオブジェクトが自動で使用できるようになります。

基本的な例を以下にいくつか示します。

xml.em("emphasized")
xml.em { xml.b("emph & bold") }
xml.a("A Link", "href" => "https://rubyonrails.org")
xml.target("name" => "compile", "option" => "fast")

上のコードから以下が生成されます。

<em>emphasized</em>
<em><b>emph &amp; bold</b></em>
<a href="https://rubyonrails.org">A link</a>
<target option="fast" name="compile" />

ブロックを後ろに伴うメソッドはすべて、ブロックの中にネストしたマークアップを含むXMLマークアップタグとして扱われます。以下の例で示します。

xml.div {
  xml.h1(@person.name)
  xml.p(@person.bio)
}

上のコードの出力は以下のようなものになります。

<div>
  <h1>David Heinemeier Hansson</h1>
  <p>A product of Danish Design during the Winter of '79...</p>
</div>

以下はBasecampで実際に使用されているRSS出力コードをそのまま引用したものです。

xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
  xml.channel do
    xml.title(@feed_title)
    xml.link(@url)
    xml.description "Basecamp: Recent items"
    xml.language "en-us"
    xml.ttl "40"

    for item in @recent_items
      xml.item do
        xml.title(item_title(item))
        xml.description(item_description(item)) if item_description(item)
        xml.pubDate(item_pubDate(item))
        xml.guid(@person.firm.account.url + @recent_items.url(item))
        xml.link(@person.firm.account.url + @recent_items.url(item))
        xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
      end
    end
  end
end
3.1.3 Jbuilder

JbuilderはRailsチームによってメンテナンスされているgemのひとつで、RailsのGemfileにデフォルトで含まれています。

JbuilderはBuilderと似ていますが、XMLではなくJSONを生成するのに使われます。

Jbuilderが導入されていない場合は、Gemfileに以下を追加できます。

gem 'jbuilder'

.jbuilderという拡張子を持つテンプレートでは、jsonという名前のJbuilderオブジェクトが自動的に利用できるようになります。

基本的な例を以下に示します。

json.name("Alex")
json.email("alex@example.com")

上のコードから以下が生成されます。

{
  "name": "Alex",
  "email": "alex@example.com"
}

この他のコード例や詳しい情報についてはJbuilder documentationを参照してください。

3.1.4 テンプレートをキャッシュする

Railsは、デフォルトですべてのビューテンプレートをコンパイルしてメソッド化し、出力に備えます。developmentモードの場合、ビューテンプレートが変更されるとファイルの更新日時で変更が検出され、再度コンパイルされます。

3.2 パーシャル

部分テンプレートまたはパーシャルは、出力を扱いやすく分割するための仕組みです。パーシャルを使用することで、ビュー内のコードをいくつものファイルに分割して書き出し、他のテンプレートでも使いまわすことができます。

3.2.1 パーシャルの命名ルール

パーシャルをビューの一部に含めて出力するには、ビューでrenderメソッドを使用します。

<%= render "menu" %>

上の呼び出しにより、_menu.html.erbという名前のファイルの内容が、renderメソッドを書いたその場所でレンダリングされます。パーシャルファイル名の冒頭にはアンダースコアが付いていることにご注意ください。これは通常のビューと区別するために付けられています。ただしrenderで呼び出す際にはこのアンダースコアは不要です。以下のように、他のフォルダの下にあるパーシャルを呼び出す際にもアンダースコアは不要です。

<%= render "shared/menu" %>

上のコードでは、app/views/shared/_menu.html.erbパーシャルを読み込んで使用します。

3.2.2 パーシャルを活用してビューを簡潔に保つ

すぐに思い付くパーシャルの使い方といえば、パーシャルをサブルーチンと同等のものとみなすというのがあります。ビューの詳細部分をパーシャルに移動し、コードの見通しを良くするために、パーシャルを使うのです。たとえば、以下のようなビューがあるとします。

<%= render "shared/ad_banner" %>

<h1>Products</h1>

<p>Here are a few of our fine products:</p>
<% @products.each do |product| %>
  <%= render partial: "product", locals: {product: product} %>
<% end %>

<%= render "shared/footer" %>

上のコードの_ad_banner.html.erbパーシャルと_footer.html.erbパーシャルに含まれるコンテンツは、アプリケーションの多くのページと共有できます。あるページを開発中、パーシャルの部分については詳細を気にせずに済みます。

3.2.3 partialのないrenderlocalsとオプション

上の例では、renderpartiallocalesの2つのオプションを取っています。しかし、渡したいオプションが他にない場合は、これらのオプションを省略できます。

次の例で説明します。

<%= render partial: "product", locals: { product: @product } %>

上のコードは以下のようにも書けます。

<%= render "product", product: @product %>
3.2.4 asobjectオプション

ActionView::Partials::PartialRendererは、デフォルトでテンプレートと同じ名前を持つローカル変数の中に自身のオブジェクトを持ちます。以下のコードを見てみましょう。

<%= render partial: "product" %>

上のコードでは、_productパーシャルはローカル変数productから@productを取得できます。これは以下のコードと同等の結果になります。

<%= render partial: "product", locals: { product: @product } %>

objectオプションは、パーシャルで出力するオブジェクトを直接指定したい場合に使用します。これは、テンプレートのオブジェクトが他の場所 (別のインスタンス変数や別のローカル変数) にある場合に便利です。

たとえば、以下のコードがあるとします。

<%= render partial: "product", locals: { product: @item } %>

上のコードは以下のようになります。

<%= render partial: "product", object: @item %>

asオプションを使うと、ローカル変数に異なる名前を指定できます。たとえば、productではなくitemにしたい場合は次のようにします。

<%= render partial: "product", object: @item, as: "item" %>

上は以下と同等です。

<%= render partial: "product", locals: { item: @item } %>
3.2.5 コレクションを出力する

テンプレート上にコレクションを1つ表示し、サブテンプレートでそのコレクションの要素を1つずつ出力(レンダリング)するというのは、よくあるパターンです。このパターンは1つのメソッドだけで実行できます。このメソッドは配列を受け取り、配列内の各要素ごとにパーシャルを出力します。

すべての製品(products)を出力するコード例は以下のようになります。

<% @products.each do |product| %>
  <%= render partial: "product", locals: { product: product } %>
<% end %>

上のコードは以下のように1行で書けます。

<%= render partial: "product", collection: @products %>

パーシャルでこのようにコレクションなどが使用されている場合、パーシャルの各インスタンスは、パーシャル名に基づいた変数を経由して出力されるコレクションのメンバーにアクセスします。このパーシャルは_productという名前なので、productを指定すれば、出力されるインスタンスを取得できます。

コレクション出力には短縮記法があります。@productsProductインスタンスのコレクションであれば、以下のコードでも同じ結果を得られます。

<%= render @products %>

使用されるパーシャル名は、コレクションの中にある「モデル名」を参照して決定されます。この場合のモデル名はProductです。作成するコレクションの各要素が不揃い (訳注: 要素ごとにモデルが異なる場合を指します) であっても、Railsはコレクションのメンバごとに適切なパーシャルを選んで出力してくれます。

3.2.6 スペーサーテンプレート

:spacer_templateオプションを使用すると、主要なパーシャル同士の間を埋める第二のパーシャルを指定することができます。

<%= render partial: @products, spacer_template: "product_ruler" %>

主要な_productパーシャルの合間に、スペーサーとなる_product_rulerパーシャルが出力されます (_product_rulerにはデータは渡していません)。

3.3 レイアウト

Railsにおける「レイアウト」は、多くのコントローラのアクションにわたって共通して使用できるテンプレートのことです。Railsアプリケーションには必ず全体用のレイアウトがあり、ほぼすべてのWebページ出力はこの全体レイアウトの内側で行われますが、これが典型的なレイアウトです。たとえば、あるWebサイトにはユーザーログイン用のレイアウトが使用されていたり、別のWebサイトにはマーケティングやセールス用のレイアウトが使用されていたりします。ログインしたユーザー向けのレイアウトであれば、ナビゲーションツールバーをページのトップレベルに表示し、多くのコントローラ/アクションで共通して使用できるようにするでしょう。SaaSアプリケーションにおけるセールス用のレイアウトであれば、トップレベルのナビゲーションに「お値段」や「お問い合わせ先」を共通して表示するでしょう。レイアウトごとに異なる外観を設定してこれらを使い分けることができます。レイアウトの詳細については、ビューのレイアウトとレンダリング ガイドを参照してください。

4 パーシャルレイアウト

パーシャルに独自のレイアウトを適用することができます。パーシャル用のレイアウトは、アクション全体にわたるグローバルなレイアウトとは異なりますが、動作は同じです。

試しに、ページ上に投稿を1つ表示してみましょう。表示制御のためdivタグで囲むことにします。最初に、Articleを1つ新規作成します。

Article.create(body: 'パーシャルレイアウトはいいぞ!')

showテンプレートは、boxレイアウトに内包された_articleパーシャルを出力します。

articles/show.html.erb

<%= render partial: 'article', layout: 'box', locals: { article: @article } %>

boxレイアウトは、divタグの中に_articleパーシャルを内包した簡単な構造です。

articles/_box.html.erb

<div class='box'>
  <%= yield %>
</div>

このパーシャルレイアウトでは、render呼び出しに渡されたローカルのarticle変数にアクセスできる点にご注目ください。ただし、アプリケーション全体で共通のレイアウトとは異なり、パーシャルレイアウトのファイル名冒頭にはアンダースコアが必要です。

yieldを呼び出す代わりに、パーシャルレイアウト内にあるコードのブロックを出力することもできます。たとえば、_articleというパーシャルがない場合でも、以下のような呼び出しが行えます。

articles/show.html.erb

<% render(layout: 'box', locals: { article: @article }) do %>
  <div>
    <p><%= article.body %></p>
  </div>
<% end %>

ここでは、同じ_boxパーシャルを使う前提であり、先の例と同じ出力が得られます。

5 ビューのパス

レスポンスを出力(レンダリング)する場合、個別のビューが置かれている場所をコントローラが解決する必要があります。デフォルトでは、app/viewsディレクトリの下のみを探索します。

prepend_view_pathメソッドやappend_view_pathメソッドを用いることで、パスの解決時に優先して検索される別のディレクトリを追加できます。

5.1 ビューパスの冒頭にパスを追加する

これは、たとえばサブドメインで使うビューを別のディレクトリ内に配置したい場合などに便利です。

次のように利用できます。

prepend_view_path "app/views/#{request.subdomain}"

Action Viewは、ビューの解決時にこのディレクトリ内を最初に探索します。

5.2 ビューパスの末尾にパスを追加する

同様に、パスを末尾に追加することもできます。

append_view_path "app/views/direct"

上のコードは、探索パスの末尾にapp/views/directを追加します。

6 Action Viewのヘルパーメソッドの概要

WIP: ここに記載されているヘルパーは一部のみです。完全なリストについてはAPIドキュメントを参照してください。

以下に記載されているのは、Action Viewで提供されているヘルパーのごく一部の概要にとどまっています。詳しくはAPIドキュメントで参照できますが、本セクションは最初に参照する概要として有用です。

6.1 AssetTagHelper

このモジュールは、画像・JavaScriptファイル・スタイルシート・フィードなどのアセットにビューをリンクするHTMLを生成するメソッドを提供します。

デフォルトでは、現在ホストされているpublicフォルダ内のアセットに対してリンクしますが、アプリケーション設定 (通常はconfig/environments/production.rb) のconfig.action_controller.asset_hostで設定されているアセット用サーバーにリンクすることもできます。たとえば、assets.example.comというアセット専用ホストを使用したいとします。

config.action_controller.asset_host = "assets.example.com"
image_tag("rails.png") # => <img src="http://assets.example.com/images/rails.png" />

ブラウザやフィードリーダーが検出可能なRSSフィード、Atomフィード、JSONフィードのリンクタグを返します。

auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "RSS Feed"}) # =>
  <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="http://www.example.com/feed" />
6.1.2 image_path

app/assets/imagesディレクトリの下に置かれている画像アセットへのパスを算出します。ドキュメントルート・ディレクトリからの完全なパスが返されます。このメソッドはimage_tagの内部で画像へのパス作成に使用されています。

image_path("edit.png") # => /assets/edit.png

config.assets.digestがtrueに設定されている場合、ファイル名にフィンガープリントが追加されます。

image_path("edit.png") # => /assets/edit-2d1a2db63fc738690021fedb5a65b68e.png
6.1.3 image_url

app/assets/imagesに置かれている画像アセットへのURLを算出します。このメソッドは内部でimage_pathを呼び出しており、現在のホストまたはアセット用のホストとマージしてURLを生成します。

image_url("edit.png") # => http://www.example.com/assets/edit.png
6.1.4 image_tag

HTML imgタグを返します。画像へのフルパス、またはapp/assets/imagesディレクトリ内にあるファイルを引数として与えられます。

image_tag("icon.png") # => <img src="/assets/icon.png" />
6.1.5 javascript_include_tag

引数に与えられたソースごとにHTML scriptタグを返します。app/assets/javascriptsディレクトリにあるJavaScriptファイル名 (拡張子.jsはあってもなくても構いません) を引数として渡すことができます。この結果は現在のページにインクルードされます。ドキュメントルートからの相対完全パスを渡すこともできます。

javascript_include_tag "common" # => <script src="/assets/common.js"></script>
6.1.6 javascript_path

app/assets/javascriptsに置かれているJavaScriptアセットへのパスを算出します。ソースのファイル名に拡張子.jsがない場合は自動的に補われます。ドキュメントルート・ディレクトリからの完全なパスが返されます。このメソッドはjavascript_include_tagの内部でスクリプトパス作成に使用されています。

javascript_path "common" # => /assets/common.js
6.1.7 javascript_url

app/assets/javascriptsに置かれているJavaScriptアセットへのURLを算出します。このメソッドは内部でjavascript_pathを呼び出しており、現在のホストまたはアセット用のホストとマージしてURLを生成します。

javascript_url "common" # => http://www.example.com/assets/common.js

引数として指定されたソースにあるスタイルシートへのリンクタグを返します。拡張子が指定されていない場合は、.cssが自動的に補われます。

stylesheet_link_tag "application" # => <link href="/assets/application.css" media="screen" rel="stylesheet" />
6.1.9 stylesheet_path

app/assets/stylesheetsに置かれているスタイルシートアセットへのパスを算出します。ソースのファイル名に拡張子.cssがない場合は自動的に補われます。ドキュメントルート・ディレクトリからの完全なパスが返されます。このメソッドはstylesheet_link_tagの内部でスタイルシートへのパス作成に使用されています。

stylesheet_path "application" # => /assets/application.css
6.1.10 stylesheet_url

app/assets/stylesheetsに置かれているスタイルシートアセットへのURLを算出します。このメソッドは内部でstylesheet_pathを呼び出しており、現在のホストまたはアセット用のホストとマージしてURLを生成します。

stylesheet_url "application" # => http://www.example.com/assets/application.css

6.2 AtomFeedHelper

6.2.1 atom_feed

このヘルパーを使用して、Atomフィードを簡単に生成できます。以下にすべての使用例を示します。

config/routes.rb

resources :articles

app/controllers/articles_controller.rb

def index
  @articles = Article.all

  respond_to do |format|
    format.html
    format.atom
  end
end

app/views/articles/index.atom.builder

atom_feed do |feed|
  feed.title("Articles Index")
  feed.updated(@articles.first.created_at)

  @articles.each do |article|
    feed.entry(article) do |entry|
      entry.title(article.title)
      entry.content(article.body, type: 'html')

      entry.author do |author|
        author.name(article.author_name)
      end
    end
  end
end

6.3 BenchmarkHelper

6.3.1 benchmark

テンプレート内の1つのブロックの実行時間測定と、結果のログ出力に使用します。実行に時間のかかる行や、ボトルネックになる可能性のある行をこのブロックで囲み、実行にかかった時間を読み取ります。

<% benchmark "Process data files" do %>
  <%= expensive_files_operation %>
<% end %>

上のコードは、"Process data files (0.34523)"のようなログを出力します。このログは、コード最適化のためにタイミングを比較する際に役立てることができます。

6.4 CacheHelper

6.4.1 cache

cacheメソッドは、(アクション全体やページ全体ではなく) ビューの断片をキャッシュするメソッドです。この手法は、メニュー・ニュース記事・静的HTMLの断片などをキャッシュするのに便利です。このメソッドには、キャッシュしたいコンテンツを1つのブロックに含めて引数として渡します。詳細については、AbstractController::Caching::Fragmentsを参照してください。

<% cache do %>
  <%= render "shared/footer" %>
<% end %>

6.5 CaptureHelper

6.5.1 capture

captureメソッドを使用することで、テンプレートの一部を変数に保存することができます。保存された変数は、テンプレートやレイアウトのどんな場所でも自由に使用できます。

<% @greeting = capture do %>
  <p>ようこそ!日付と時刻は<%= Time.now %>です</p>
<% end %>

上でキャプチャした変数は以下のように他の場所で自由に使用できます。

<html>
  <head>
    <title>ようこそ!</title>
  </head>
  <body>
    <%= @greeting %>
  </body>
</html>
6.5.2 content_for

content_forを呼び出すと、後の利用に備えて、idに対応するマークアップのブロックが保存されます。以後、保存されたコンテンツを他のテンプレートやレイアウトで呼び出すことができます。呼び出しの際には、yieldの引数となるidを渡します。

たとえば、あるRailsアプリケーション全体にわたって標準のアプリケーションレイアウトを使用しているが、特定のページでのみ特定のJavaScriptコードが必要となり、他のページではこのJavaScriptはまったく不要であるとします。このようなときにはcontent_forを使用します。これにより、そのJavaScriptコードを特定のページにだけインクルードし、サイトの他の部分でインクルードされることのないようにできます。

app/views/layouts/application.html.erb

<html>
  <head>
    <title>ようこそ!</title>
    <%= yield :special_script %>
  </head>
  <body>
    <p>ようこそ!日付と時刻は<%= Time.now %>です</p>
  </body>
</html>

app/views/articles/special.html.erb

<p>これは特別なページです。</p>

<% content_for :special_script do %>
  <script>alert('Hello!')</script>
<% end %>

6.6 DateHelper

6.6.1 date_select

日付用のselectタグのセットを返します。タグは年・月・日用にそれぞれあり、日付に関する特定の属性にアクセスして年月日を選択済みの状態にします。

date_select("article", "published_on")
6.6.2 datetime_select

日付・時刻用のselectタグのセットを返します。タグは年・月・日・時・分用にそれぞれあり、日付・時刻に関する特定の属性にアクセスして日時が選択済みになります。

datetime_select("article", "published_on")
6.6.3 distance_of_time_in_words

TimeオブジェクトやDateオブジェクト、秒を表す整数同士を比較して近似表現を返します。include_secondstrueにすると、さらに詳しい差を得られます。

distance_of_time_in_words(Time.now, Time.now + 15.seconds)        # => less than a minute
distance_of_time_in_words(Time.now, Time.now + 15.seconds, include_seconds: true)  # => less than 20 seconds
6.6.4 select_date

日付用のselectタグのセットを返します。タグは年・月・日用にそれぞれあり、dateで得られる値で選択済みの状態にします。

# 指定された日付 (ここでは本日から6日後) をデフォルト値とする日付セレクトボックスを生成する
select_date(Time.today + 6.days)

# 日付の指定がない場合、本日をデフォルト値とする日付セレクトボックスを生成する
select_date()
6.6.5 select_datetime

日付・時刻用のselectタグのセットを返します。タグは年・月・日・時・分用にそれぞれあり、datetimeで得られる値で選択済みの状態にします。

# 指定された日時 (ここでは本日から4日後) をデフォルト値とする日時セレクトボックスを生成する
select_datetime(Time.now + 4.days)

# 日時の指定がない場合、本日をデフォルト値とする日時セレクトボックスを生成する
select_datetime()
6.6.6 select_day

1から31までの日付をオプションに持ち、当日が選択されているselectタグを返します。

# 指定された日付をデフォルト値に持つセレクトボックスを生成する
select_day(Time.today + 2.days)

# 指定された数値をデフォルトの日付として持つセレクトボックスを生成する
select_day(5)
6.6.7 select_hour

0から23までの時をオプションに持ち、現在時刻が選択されているselectタグを返します。

# 指定された時をデフォルト値として持つセレクトボックスを生成する
select_hour(Time.now + 6.hours)
6.6.8 select_minute

0から59までの分をオプションに持ち、現在時刻の分が選択されているselectタグを返します。

# 指定された分をデフォルト値として持つセレクトボックスを生成する
select_minute(Time.now + 10.minutes)
6.6.9 select_month

JanuaryからDecemberまでの月をオプションに持ち、現在の月が選択されているselectタグを返します(訳注: 日本語環境では1月から12月が表示されます)。

# 現在の月をデフォルト値に持つセレクトボックスを生成する
select_month(Date.today)
6.6.10 select_second

0から59までの秒をオプションに持ち、現在時刻の秒が選択されているselectタグを返します。

# 指定の秒を現在時刻に加えた値をデフォルト値に持つ秒用のセレクトボックスを生成する
select_second(Time.now + 16.seconds)
6.6.11 select_time

HTML selectタグのセットを返します。時のタグと分のタグがそれぞれ表示されます。

# 現在時刻をデフォルト値に持つ時刻セレクトボックスを生成する
select_time(Time.now)
6.6.12 select_year

当年を含む直近の5つの年をオプションに持ち、当年がデフォルトとして選択されているselectタグを返します。:start_yearキーと:end_yearキーをoptionsに設定することで、デフォルトの5年を変更できます。

# 今年をデフォルト値に持ち、Date.todayで得られた日の前後5年をオプションに持つセレクトボックスを生成する
select_year(Date.today)

# 今年をデフォルト値に持ち、1900年から2009年までをオプションに持つセレクトボックスを生成する
select_year(Date.today, start_year: 1900, end_year: 2009)
6.6.13 time_ago_in_words

distance_of_time_in_wordsと基本的に同じ動作であり、to_timeの部分がTime.nowに固定されている点だけが異なります。

time_ago_in_words(3.minutes.from_now)  # => 3分
6.6.14 time_select

時刻用のselectタグのセットを返します。タグは時・分用の他にオプションで秒もあります。時刻に関する特定の属性にアクセスして日時が選択済みになります。このタグで選択された項目は、Active Recordオブジェクトにマルチパラメータとして割り当て可能な形式になります。

# 時刻選択用タグを作成する。フォームがPOSTされると、submitted属性のorder変数が保存される。
time_select("order", "submitted")

6.7 DebugHelper

YAMLからダンプしたオブジェクトを含むpreタグを返します。これを利用することで、オブジェクトの内容が非常に読みやすくなります。

my_hash = { 'first' => 1, 'second' => 'two', 'third' => [1,2,3] }
debug(my_hash)
<pre class='debug_dump'>---
first: 1
second: two
third:
- 1
- 2
- 3
</pre>

6.8 FormHelper

フォームヘルパーを使用すると、標準のHTML要素だけを使用するよりもはるかに容易に、モデルと連携動作するフォームを作成することができます。Formヘルパーはフォーム用のHTMLを生成し、テキストやパスワードといった入力の種類に応じたメソッドを提供します。(送信ボタンがクリックされたり、JavaScriptでform.submitを呼び出すなどして) フォームが送信されると、フォームの入力内容はparamsオブジェクトにまとめて保存され、コントローラに渡されます。

フォームヘルパーは、モデル属性の操作に特化したものと、より一般的なものの2種類に分類できます。ここではモデル属性の扱いに特化したものについて説明します。モデル属性に特化していない一般的なフォームヘルパーについては、ActionView::Helpers::FormTagHelperのドキュメントを参照してください。

ここで扱うフォームヘルパーの中心となるのはform_forメソッドです。このメソッドはモデルのインスタンスからフォームを作成することができます。たとえば、以下のようにPersonというモデルがあり、このモデルをもとにしてインスタンスを1つ作成するとします。

# メモ: @person変数はコントローラ側で設定済みであるとする (@person = Person.newなど)
<%= form_for @person, url: { action: "create" } do |f| %>
  <%= f.text_field :first_name %>
  <%= f.text_field :last_name %>
  <%= submit_tag 'Create' %>
<% end %>

上のコードによって生成されるHTMLは以下のようになります。

<form action="/people/create" method="post">
  <input id="person_first_name" name="person[first_name]" type="text" />
  <input id="person_last_name" name="person[last_name]" type="text" />
  <input name="commit" type="submit" value="Create" />
</form>

上のフォームが送信される時に作成されるparamsオブジェクトは以下のようになります。

{ "action" => "create", "controller" => "people", "person" => { "first_name" => "William", "last_name" => "Smith" } }

上のparamsハッシュには、Personモデル用の値がネストした形で含まれているので、コントローラでparams[:person]と書くことで内容にアクセスできます。

6.8.1 check_box

指定された属性にアクセスするためのチェックボックスタグを生成します。

# @article.validated?が1の場合
check_box("article", "validated")
# => <input type="checkbox" id="article_validated" name="article[validated]" value="1" />
#    <input name="article[validated]" type="hidden" value="0" />
6.8.2 fields_for

form_for のような特定のモデルオブジェクトの外側にスコープを作成しますが、フォームタグ自体は作成しません。このため、fields_for は同じフォームに別のモデルオブジェクトを追加するのに向いています。

<%= form_for @person, url: { action: "update" } do |person_form| %>
  First name: <%= person_form.text_field :first_name %>
  Last name : <%= person_form.text_field :last_name %>

  <%= fields_for @person.permission do |permission_fields| %>
    Admin?  : <%= permission_fields.check_box :admin %>
  <% end %>
<% end %>
6.8.3 file_field

特定の属性にアクセスするための、ファイルアップロード用inputタグを返します。

file_field(:user, :avatar)
# => <input type="file" id="user_avatar" name="user[avatar]" />
6.8.4 form_for

フィールドの値をユーザーに問い合わせるのに使われる、特定のモデルオブジェクトの外側にフォームを1つとスコープを1つ作成します。

<%= form_for @article do |f| %>
  <%= f.label :title, 'Title' %>:
  <%= f.text_field :title %><br>
  <%= f.label :body, 'Body' %>:
  <%= f.text_area :body %><br>
<% end %>
6.8.5 hidden_field

特定の属性にアクセスするための、隠されたinputタグを返します。

hidden_field(:user, :token)
# => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
6.8.6 label

特定の属性用のinputフィールドに与えるラベルを返します。

label(:article, :title)
# => <label for="article_title">Title</label>
6.8.7 password_field

特定の属性にアクセスするための、種類が"password"のinputタグを返します。

password_field(:login, :pass)
# => <input type="text" id="login_pass" name="login[pass]" value="#{@login.pass}" />
6.8.8 radio_button

特定の属性にアクセスするためのラジオボタンタグを返します。

# @article.categoryが"rails"を返す場合
radio_button("article", "category", "rails")
radio_button("article", "category", "java")
# => <input type="radio" id="article_category_rails" name="article[category]" value="rails" checked="checked" />
#    <input type="radio" id="article_category_java" name="article[category]" value="java" />
6.8.9 text_area

特定の属性にアクセスするための、テキストエリア用開始タグと終了タグを返します。

text_area(:comment, :text, size: "20x30")
# => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
#      #{@comment.text}
#    </textarea>
6.8.10 text_field

特定の属性にアクセスするための、種類が"text"のinputタグを返します。

text_field(:article, :title)
# => <input type="text" id="article_title" name="article[title]" value="#{@article.title}" />
6.8.11 email_field

特定の属性にアクセスするための、種類が"email"のinputタグを返します。

email_field(:user, :email)
# => <input type="email" id="user_email" name="user[email]" value="#{@user.email}" />
6.8.12 url_field

特定の属性にアクセスするための、種類が"url"のinputタグを返します。

url_field(:user, :url)
# => <input type="url" id="user_url" name="user[url]" value="#{@user.url}" />

6.9 FormOptionsHelper

さまざまな種類のコンテナを1つのオプションタグのセットにまとめるためのメソッドを多数提供します。

6.9.1 collection_select

selectタグと、objectが属するクラスのメソッド値の既存の戻り値をコレクションにしたoptionタグを返します。

例として、このメソッドを適用するオブジェクトの構造が以下のようになっているとします。

class Article < ApplicationRecord
  belongs_to :author
end

class Author < ApplicationRecord
  has_many :articles
  def name_with_initial
    "#{first_name.first}. #{last_name}"
  end
end

利用法は、たとえば以下のようになります。ここでは、Articleモデルのインスタンスである@articleに関連付けられているAuthorモデルから選択肢を取り出しています。

collection_select(:article, :author_id, Author.all, :id, :name_with_initial, { prompt: true })

@article.author_idが1の場合、以下が返されます。

<select name="article[author_id]">
  <option value="">Please select</option>
  <option value="1" selected="selected">D. Heinemeier Hansson</option>
  <option value="2">D. Thomas</option>
  <option value="3">M. Clark</option>
</select>
6.9.2 collection_radio_buttons

objectが属するクラスのメソッド値の既存の戻り値をコレクションにしたradio_buttonタグを返します。

例として、このメソッドを適用するオブジェクトの構造が以下のようになっているとします。

class Article < ApplicationRecord
  belongs_to :author
end

class Author < AApplicationRecord
  has_many :articles
  def name_with_initial
    "#{first_name.first}. #{last_name}"
  end
end

利用法は、たとえば以下のようになります。ここでは、Articleモデルのインスタンスである@articleに関連付けられているAuthorモデルから選択肢を取り出しています。

collection_radio_buttons(:article, :author_id, Author.all, :id, :name_with_initial)

@article.author_idが1の場合、以下が返されます。

<input id="article_author_id_1" name="article[author_id]" type="radio" value="1" checked="checked" />
<label for="article_author_id_1">D. Heinemeier Hansson</label>
<input id="article_author_id_2" name="article[author_id]" type="radio" value="2" />
<label for="article_author_id_2">D. Thomas</label>
<input id="article_author_id_3" name="article[author_id]" type="radio" value="3" />
<label for="article_author_id_3">M. Clark</label>
6.9.3 collection_check_boxes

objectが属するクラスのmethodの既存の戻り値をコレクションにしたcheck_boxタグを返します。

例として、このメソッドを適用するオブジェクトの構造が以下のようになっているとします。

class Article < ApplicationRecord
  has_and_belongs_to_many :authors
end

class Author < ApplicationRecord
  has_and_belongs_to_many :articles
  def name_with_initial
    "#{first_name.first}. #{last_name}"
  end
end

利用法は、たとえば以下のようになります。ここでは、Articleモデルのインスタンスである@articleに関連付けられているAuthorsから選択肢を取り出しています。

collection_check_boxes(:article, :author_ids, Author.all, :id, :name_with_initial)

@article.author_idsが1の場合、以下が返されます。

<input id="article_author_ids_1" name="article[author_ids][]" type="checkbox" value="1" checked="checked" />
<label for="article_author_ids_1">D. Heinemeier Hansson</label>
<input id="article_author_ids_2" name="article[author_ids][]" type="checkbox" value="2" />
<label for="article_author_ids_2">D. Thomas</label>
<input id="article_author_ids_3" name="article[author_ids][]" type="checkbox" value="3" />
<label for="article_author_ids_3">M. Clark</label>
<input name="article[author_ids][]" type="hidden" value="" />
6.9.4 option_groups_from_collection_for_select

optionタグの文字列を返します。後述のoptions_from_collection_for_selectと似ていますが、引数のオブジェクトリレーションに基いてoptgroupタグを使用する点が異なります。

例として、このメソッドを適用するオブジェクトの構造が以下のようになっているとします。

class Continent < ApplicationRecord
  has_many :countries
  # attribs: id, name
end

class Country < ApplicationRecord
  belongs_to :continent
  # attribs: id, name, continent_id
end

使用例は以下のようになります。

option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)

出力結果は以下のようになります。

<optgroup label="Africa">
  <option value="1">Egypt</option>
  <option value="4">Rwanda</option>
  ...
</optgroup>
<optgroup label="Asia">
  <option value="3" selected="selected">China</option>
  <option value="12">India</option>
  <option value="5">Japan</option>
  ...
</optgroup>

返されるのはoptgroupタグとoptionだけです。従って、出力結果の外側を適切なselectタグで囲む必要があります。

6.9.5 options_for_select

コンテナ (ハッシュ、配列、enumerable、独自の型) を引数として受け付け、オプションタグの文字列を返します。

options_for_select([ "VISA", "MasterCard" ])
# => <option>VISA</option> <option>MasterCard</option>

返されるのはoptionだけです。従って、出力結果の外側を適切なHTML selectタグで囲む必要があります。

6.9.6 options_from_collection_for_select

collectionを列挙した結果をoptionタグ化した文字列を返し、呼び出しの結果をvalue_methodにオプション値として割り当て、text_methodにオプションテキストとして割り当てます。

options_from_collection_for_select(collection, value_method, text_method, selected = nil)

たとえば、@project.people に入っているpersonをループですべて列挙してinputタグを作成するのであれば、以下のようになります。

options_from_collection_for_select(@project.people, "id", "name")
# => <option value="#{person.id}">#{person.name}</option>

返されるのはoptionだけです。従って、出力結果の外側を適切なHTML selectタグで囲む必要があります。

6.9.7 select

指定されたオブジェクトとメソッドに従って、selectタグの中に一連のoptionタグを含んだものを作成します。

例:

select("article", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: true })

@article.person_idが1の場合、以下が返されます。

<select name="article[person_id]">
  <option value=""></option>
  <option value="1" selected="selected">David</option>
  <option value="2">Eileen</option>
  <option value="3">Rafael</option>
</select>
6.9.8 time_zone_options_for_select

世界のほぼすべてのタイムゾーンを含むオプションタグの文字列を返します。

6.9.9 time_zone_select

time_zone_options_for_selectを使用してオプションタグを生成し、指定されたオブジェクトとメソッド用のselectタグとoptionタグを返します。

time_zone_select("user", "time_zone")
6.9.10 date_field

特定の属性にアクセスするための、種類が"date"のinputタグを返します。

date_field("user", "dob")

6.10 FormTagHelper

フォームタグを作成するためのメソッドを多数提供します。これらのメソッドは、テンプレートに割り当てられているActive Recordオブジェクトに依存しない点がFormHelperと異なります。その代わり、FormTagHelperのメソッドでは名前と値を個別に指定します。

6.10.1 check_box_tag

チェックボックス用のフォームinputタグを作成します。

check_box_tag 'accept'
# => <input id="accept" name="accept" type="checkbox" value="1" />
6.10.2 field_set_tag

HTMLフォーム要素をグループ化するためのfieldsetタグを作成します。

<%= field_set_tag do %>
  <p><%= text_field_tag 'name' %></p>
<% end %>
# => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
6.10.3 file_field_tag

ファイルアップロード用のフィールドを作成します。

<%= form_tag({ action: "post" }, multipart: true) do %>
  <label for="file">File to Upload</label> <%= file_field_tag "file" %>
  <%= submit_tag %>
<% end %>

出力例:

file_field_tag 'attachment'
# => <input id="attachment" name="attachment" type="file" />
6.10.4 form_tag

url_for_optionsで設定されたURLへのアクションに送信されるフォームタグを作成します。これはActionController::Base#url_forと似ています。

<%= form_tag '/articles' do %>
  <div><%= submit_tag 'Save' %></div>
<% end %>
# => <form action="/articles" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
6.10.5 hidden_field_tag

フォームinputの「隠しフィールド」を作成します。この隠しフィールドは、通常であればHTTPがステートレスであることによって失われる可能性のあるデータを送信したり、ユーザーから見えないようにしておきたいデータを送信するのに使用されます。

hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
# => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
6.10.6 image_submit_tag

送信画像を表示します。この画像をクリックするとフォームが送信されます。

image_submit_tag("login.png")
# => <input src="/images/login.png" type="image" />
6.10.7 label_tag

フィールドのラベルを作成します。

label_tag 'name'
# => <label for="name">Name</label>
6.10.8 password_field_tag

パスワード用のフィールドを作成します。このフィールドへの入力はマスク用文字で隠されます。

password_field_tag 'pass'
# => <input id="pass" name="pass" type="password" />
6.10.9 radio_button_tag

ラジオボタンを作成します。ユーザーが同じオプショングループ内から選択できるよう、同じname属性でラジオボタンをグループ化してください。

radio_button_tag 'favorite_color', 'maroon'
# => <input id="favorite_color_maroon" name="favorite_color" type="radio" value="maroon" />
6.10.10 select_tag

ドロップダウン選択ボックスを作成します。

select_tag "people", "<option>David</option>"
# => <select id="people" name="people"><option>David</option></select>
6.10.11 submit_tag

キャプションとして指定されたテキストを使って送信ボタンを作成します。

submit_tag "この記事を公開"
# => <input name="commit" type="submit" value="この記事を公開" />
6.10.12 text_area_tag

textareaタグでテキスト入力エリアを作成します。ブログへの投稿や説明文などの長いテキストを入力するにはtextareaをお使いください。

text_area_tag 'article'
# => <textarea id="article" name="article"></textarea>
6.10.13 text_field_tag

通常のテキストフィールドを作成します。ユーザー名や検索キーワード入力用のフィールドにはこの通常のテキストフィールドをご使用ください。

text_field_tag 'name'
# => <input id="name" name="name" type="text" />
6.10.14 email_field_tag

種類がemailの標準入力フィールドを作成します。

email_field_tag 'email'
# => <input id="email" name="email" type="email" />
6.10.15 url_field_tag

種類がurlの標準入力フィールドを作成します。

url_field_tag 'url'
# => <input id="url" name="url" type="url" />
6.10.16 date_field_tag

種類がdateの標準入力フィールドを作成します。

date_field_tag "dob"
# => <input id="dob" name="dob" type="date" />

6.11 JavaScriptHelper

ビューでJavaScriptを使用するための機能を提供します。

6.11.1 button_to_function

onclickハンドラを使用するJavaScript関数を起動するボタンを返します。以下の例を参照ください。

button_to_function "Greeting", "alert('Hello world!')"
button_to_function "Delete", "if (confirm('Really?')) do_delete()"
button_to_function "Details" do |page|
  page[:details].visual_effect :toggle_slide
end
6.11.2 escape_javascript

JavaScriptセグメントから改行 (CR) と一重引用符と二重引用符をエスケープします。

6.11.3 javascript_tag

渡されたコードをJavaScript用タグにラップして返します。

javascript_tag "alert('すべて良好')"
<script>
//<![CDATA[
alert('すべて良好')
//]]>
</script>

6.12 NumberHelper

数値をフォーマット済み文字列に変換するメソッド群を提供します。サポートされているフォーマットは電話番号、通貨、パーセント、精度、座標、ファイルサイズなどです。

6.12.1 number_to_currency

数値を通貨表示に変換します ($13.65など)。

number_to_currency(1234567890.50) # => $1,234,567,890.50
6.12.2 number_to_human_size

バイト数を読みやすい形式にフォーマットします。ファイルサイズをユーザーに表示する場合に便利です。

number_to_human_size(1234)          # => 1.2 KB
number_to_human_size(1234567)       # => 1.2 MB
6.12.3 number_to_percentage

数値をパーセント文字列に変換します。

number_to_percentage(100, precision: 0)        # => 100%
6.12.4 number_to_phone

数値を電話番号に変換します(デフォルトは米国式)。

number_to_phone(1235551234) # => 123-555-1234
6.12.5 number_with_delimiter

数値に3桁ごとの桁区切り文字を追加します。

number_with_delimiter(12345678) # => 12,345,678
6.12.6 number_with_precision

数値を指定された精度(precision)に変換します。デフォルトの精度は小数点以下3桁です。

number_with_precision(111.2345)                # => 111.235
number_with_precision(111.2345, precision: 2)  # => 111.23

6.13 SanitizeHelper

SanitizeHelperモジュールは、望ましくないHTML要素を除去するためのメソッド群を提供します。

6.13.1 sanitize

sanitizeヘルパーメソッドは、すべてのタグ文字をHTMLエンコードし、明示的に許可されていない属性をすべて削除します。

sanitize @article.body

:attributesオプションまたは:tagsオプションが渡されると、そこで指定された属性およびタグのみが処理の対象となります。

sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style)

さまざまな用途に合わせてデフォルト設定を変更できます。たとえば以下のようにデフォルトのタグにtableタグを追加するとします。

class Application < Rails::Application
  config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
end
6.13.2 sanitize_css(style)

CSSコードをサニタイズします。

リンクテキストを残してリンクタグをすべて削除します。

strip_links('<a href="https://rubyonrails.org">Ruby on Rails</a>')
# => Ruby on Rails
strip_links('メール送信先: <a href="mailto:me@email.com">me@email.com</a>.')
# => メール送信先: me@email.com.
strip_links('ブログ: <a href="http://myblog.com/">開く</a>')
# => ブログ: 開く
6.13.4 strip_tags(html)

HTMLからHTMLタグをすべて削除します。HTMLコメントも削除されます。 この機能にはrails-html-sanitizer gemが使われています。

strip_tags("<i>これらの</i>タグをストリップする")
# => これらのタグをストリップする
strip_tags("<b>ボールド</b>は不要!<a href='more.html'>詳細</a>を参照")
# => ボールドは不要!詳細を参照

NB: この出力にはエスケープされていない'<'、'>'、'&'文字が残ることがあり、それによってブラウザが期待どおりに動作しなくなることがあります。

6.14 CsrfHelper

csrf-param metaタグとcsrf-token metaタグを返します。これらの名称はそれぞれ、クロスサイトリクエストフォージェリ(CSRF: cross-site request foregery)のパラメータとトークンが元になっています。

<%= csrf_meta_tags %>

通常のフォームではそのための隠しフィールドが生成されるので、これらのタグは使用されません。詳細についてはセキュリティガイドを参照してください。

7 ローカライズされたビュー

Action Viewは、現在のロケールに応じてさまざまなテンプレートを出力することができます。

たとえば、ArticlesControllerにshowアクションがあるとしましょう。このshowアクションを呼び出すと、デフォルトではapp/views/articles/show.html.erbが出力されます。ここでI18n.locale = :deを設定すると、代りにapp/views/articles/show.de.html.erbが出力されます。ローカライズ版のテンプレートが見当たらない場合は、装飾なしのバージョンが使用されます。つまり、ローカライズ版ビューがなくても動作しますが、ローカライズ版ビューがあればそれが使用されます。

同じ要領で、publicディレクトリのレスキューファイル (いわゆるエラーページ) もローカライズできます。たとえば、I18n.locale = :deと設定し、public/500.de.htmlpublic/404.de.htmlを作成することで、ローカライズ版のレスキューページを作成できます。

RailsはI18n.localeに設定できるシンボルを制限していないので、ローカライズにかぎらず、あらゆる状況に合わせて異なるコンテンツを表示し分けるようにすることができます。たとえば、エキスパートユーザーには、通常ユーザーと異なる画面を表示したいとします。これを行なうには、app/controllers/application.rbに以下のように追記します。

before_action :set_expert_locale

def set_expert_locale
  I18n.locale = :expert if current_user.expert?
end

これにより、たとえばapp/views/articles/show.expert.html.erbのような特殊なビューをエキスパートユーザーにだけ表示することができます。

詳しくはRails 国際化 (i18n) API を参照してください。

支援・協賛

Railsガイドは下記のサポーターから継続的な支援を受けています。