レイアウトとレンダリング

本ガイドでは、Action ControllerとAction Viewによる基本的なレイアウト機能について解説します。

本ガイドの内容:

  • Railsに組み込まれているさまざまなレンダリング(出力)メソッドの利用法
  • 複数のセクションでレイアウトを作成する方法
  • パーシャルを利用してビューをDRYにする方法
  • レイアウトをネストする方法(サブテンプレート)

1 レンダリングの概要

本ガイドでは、「コントローラ・ビュー・モデル」三角形のうち、コントローラとビューの間でのやりとりを中心に扱います。ご存じのように、Railsのコントローラはリクエスト処理プロセス全体の制御を担当し、(ビジネスロジックのような)重い処理はモデルの方で行なうのが普通です。モデルの処理が完了すると、コントローラは処理結果をビューに渡し、ビューはユーザーにレスポンスを返します。本ガイドでは、コントローラからビューに結果を渡す方法について解説します。

大きな流れとしては、まずユーザーに送信すべきレスポンスの内容を決定し、次にユーザーへのレスポンスを作成する適切なメソッドを呼び出します。レスポンス画面を完全なビューで作成すると、Railsはそのビューをレイアウトでラップして、場合によってはパーシャルビューもそこに追加します。本ガイドではこれらの方法をひととおり紹介します。

2 レスポンスを作成する

コントローラ側から見たHTTPレスポンスの作成方法は、以下の3とおりです。

  • renderを呼び出す: 完全なレスポンスを作成してブラウザに送信する
  • redirect_toを呼び出す: HTTPリダイレクトステータスコードをブラウザに送信する
  • headを呼び出す: HTTPヘッダーのみのレスポンスを作成してブラウザに送信する

2.1 デフォルトのレンダリング: アクションにおける「設定より規約」

Railsでは「設定より規約(CoC: convention over configuration)」というポリシーが推奨されていることをご存じかと思います。デフォルトのレンダリング方法はCoCのよい例です。Railsのコントローラは、デフォルトでは正しいルーティングに対応する名前を持つビューを自動的に選択し、それを使ってレスポンスをレンダリングします。たとえば、BooksControllerというコントローラに以下のコードがあるとします。

class BooksController < ApplicationController
end

ルーティングファイルに以下が記載されているとします。

  resources :books

app/views/books/index.html.erbビューファイルの内容が以下のようになっているとします。

<h1>Books are coming soon!</h1>

ユーザーがブラウザで/booksにアクセスすると、Railsは自動的にapp/views/books/index.html.erbビューを利用してレスポンスをレンダリングし、その結果「Books are coming soon!」という文字が画面に表示されます。

しかしこの画面だけではほとんど実用性がないので、Bookモデルを作成し、BooksControllerにindexアクションを追加してみましょう。

class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end

上のコードでは、「設定より規約」原則のおかげでindexアクションの末尾で明示的にレンダリングを指示する必要がない点にご注目ください。ここでは「コントローラのアクションの末尾で明示的にレンダリングが指示されていない場合は、コントローラが利用可能なビューのパスからアクション名.html.erbというビューテンプレートを探し、それを使って自動的にレンダリングする」というルールが適用されます。それによって、ここではapp/views/books/index.html.erbファイルがレンダリングされます。

ビューですべての本の属性を表示したい場合は、以下のようにERBを書けます。

<h1>Listing Books</h1>

<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Content</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @books.each do |book| %>
      <tr>
        <td><%= book.title %></td>
        <td><%= book.content %></td>
        <td><%= link_to "Show", book %></td>
        <td><%= link_to "Edit", edit_book_path(book) %></td>
        <td><%= link_to "Destroy", book, data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<%= link_to "New book", new_book_path %>

実際のレンダリングは、ActionView::Template::Handlersの名前空間の中でネストされたクラスで行われます。本ガイドではこれについて詳しく述べませんが、ここで重要なのは、ビューテンプレートファイルの拡張子によってテンプレートハンドラが自動的に選択されることを理解することです。

2.2 renderメソッドを使う

ほとんどの場合、アプリケーションがブラウザで表示するコンテンツのレンダリングにはActionController::Base#renderメソッドが使われます。 renderメソッドはさまざまな方法でカスタマイズできます。Railsテンプレートのデフォルトビューをレンダリングすることも、特定のテンプレート、ファイル、インラインコードを指定してレンダリングすることも、何も出力しないようにすることもできます。テキスト、JSON、XMLをレンダリングすることもできます。レスポンスをレンダリングするときにContent-TypeヘッダーやHTTPステータスを指定することもできます。

render呼び出しの正確な結果をブラウザを使わずに調べたい場合は、render_to_stringを利用できます。このメソッドの振る舞いは、レンダリング結果をブラウザに返さずに文字列を返す点を除けば、renderと完全に同じです。

2.2.1 Action Viewでレンダリングする

同じコントローラから、デフォルト以外のテンプレートに対応するビューをレンダリングしたい場合は、renderメソッドでビュー名を指定できます。

def update
  @book = Book.find(params[:id])
  if @book.update(book_params)
    redirect_to(@book)
  else
    render "edit"
  end
end

上のupdateアクションでモデルに対するupdateメソッドの呼び出しが失敗すると、同じコントローラに用意しておいた別のedit.html.erbテンプレートがレンダリングで使われます。

レンダリングするアクションは、文字列の他にシンボルでも指定できます。

def update
  @book = Book.find(params[:id])
  if @book.update(book_params)
    redirect_to(@book)
  else
    render :edit, status: :unprocessable_entity
  end
end
2.2.2 別のコントローラからアクションテンプレートをレンダリングする

あるコントローラのアクションから、まったく別のコントローラの配下にあるテンプレートを利用してレンダリングすることは可能でしょうか。これもrenderメソッドでできます。 renderメソッドにはapp/viewsを起点とするフルパスを渡せるので、レンダリングするテンプレートをフルパスで指定します。たとえば、app/controllers/adminにあるAdminProductsコントローラのコードを実行する場合、以下のように書くことでアクションの実行結果をapp/views/productsに置かれているビューテンプレートでレンダリングできます。

render "products/show"

パスにスラッシュ/が含まれていると、Railsはこのビューが別のコントローラの配下にあることを認識します。別のコントローラのテンプレートを指定していることを明示的に指定したい場合は、以下のようにtemplate:オプションを使うこともできます (なおRails 2.2以前はtemplate:を省略できませんでした)。

render template: "products/show"
2.2.3 まとめ

これまでご紹介した2とおりのレンダリング方法(コントローラ内の別テンプレートを使う、別のコントローラのテンプレートを使う)は、実際には同じアクションのバリエーションにすぎません。

たとえばBooksControllerクラスのupdateアクションで本の更新に失敗したらeditテンプレートをレンダリングしたいとします。しかし実際は、以下のどのレンダリング呼び出しを使っても、最終的なレンダリングではviews/booksディレクトリのedit.html.erbが使われます。

render :edit
render action: :edit
render "edit"
render action: "edit"
render "books/edit"
render template: "books/edit"

どの呼び出しを使うかは開発チームのコーディングスタイルと規約の問題に過ぎませんが、経験則では、今書いているコードに合う最もシンプルな記法を使うのがよいでしょう。

2.2.4 render:inlineを指定する

renderメソッドを呼び出すときに:inlineオプションでERBを渡すと、ビューをまったく使わずにレンダリングできます。以下の書き方は完全に有効です。

render inline: "<% products.each do |p| %><p><%= p.name %></p><% end %>"

このオプションを実際に使う意味はめったにありません。コントローラのコードにERBを直接書き込むと、RailsのMVC指向が損なわれ、開発者がプロジェクトのロジックを追いかけることが難しくなってしまいます。ERBビューをお使いください。

インラインのレンダリングでは、デフォルトでERBが使われます。:typeオプションで:builderを指定すると、ERBではなくBuilderが使われます。

render inline: "xml.p {'Horrid coding practice!'}", type: :builder
2.2.5 テキストをレンダリングする

renderメソッドで:plainオプションを指定すると、平文テキストをマークアップせずにブラウザに送信できます。

render plain: "OK"

平文テキストのレンダリングは、HTML以外のレスポンスが期待されるAjaxやWebサービスリクエストにレスポンスを返すときに最も有用です。

デフォルトでは、:plainオプションを指定すると現在のレイアウトはレンダリング結果に適用されません。テキストを現在のレイアウトでレンダリングしたい場合は、layout: trueオプションを追加して、拡張子を.text.erbにする必要があります。

2.2.6 HTMLをレンダリングする

renderメソッドで:htmlオプションを指定すると、HTML文字列をブラウザに送信できます。

render html: helpers.tag.strong('Not Found')

この手法は、HTMLコードのごく小さなスニペットを出力したい場合に便利です。マークアップが複雑な場合は、テンプレートファイルに移行することを検討しましょう。

html:オプションを指定すると、文字列がhtml_safe対応のAPIでビルドされていない場合にHTMLエンティティがエスケープされます。

2.2.7 JSONをレンダリングする

JSONはJavaScriptのデータ形式の一種で、多くのAjaxライブラリで利用されています。Railsには、オブジェクトからJSON形式への変換と、変換されたJSONをブラウザに送信する機能のサポートが組み込まれています。

render json: @product

レンダリングするオブジェクトに対してto_jsonを呼び出す必要はありません。:jsonオプションを指定すれば、renderメソッドでto_jsonが自動的に呼び出されます。

2.2.8 XMLをレンダリングする

Railsでは、オブジェクトからXML形式への変換と、変換されたXMLをブラウザに送信する機能がビルトインでサポートされています。

render xml: @product

レンダリングするオブジェクトに対してto_xmlを呼び出す必要はありません。:xmlオプションが指定されていれば、renderによってto_xmlが自動的に呼び出されます。

2.2.9 vanilla JavaScriptをレンダリングする

vanilla JavaScript(素のJavaScript)もレンダリングできます。

render js: "alert('Hello Rails');"

上のコードは、引数の文字列をMIMEタイプtext/javascriptでブラウザに送信します。

2.2.10 生のコンテンツを出力する

render:bodyオプションを指定すると、Content-Typeヘッダーを指定しない生のコンテンツをブラウザに送信できます。

render body: "raw"

このオプションは、レスポンスのContent-Typeヘッダーを気にする必要がない場合にのみお使いください。ほとんどの場合、:plain:htmlなどを使うのが適切です。

このオプションを指定してブラウザにレスポンスを送信すると、上書きされない限りtext/plain(Action DispatchによるレスポンスのデフォルトのContent-Type)が使われます。

2.2.11 生のファイルをレンダリングする

絶対パスを指定して生のファイルをレンダリングできます。これは、条件を指定してエラーページのような静的ファイルをレンダリングするときに便利です。

render file: "#{Rails.root}/public/404.html", layout: false

上のコードは生のファイルをレンダリングします(ERBなどのハンドラはサポートされません)。デフォルトでは、現在のレイアウト内でレンダリングされます。

:fileオプションにユーザー入力を渡すと、セキュリティ上の問題につながる可能性があります(攻撃者がこのアクションを悪用してファイルシステム上の重要なファイルにアクセスする可能性があります)。

レイアウトが不要な場合は、多くの場合send_fileメソッドの方が高速かつ適切です。

2.2.12 オブジェクトをレンダリングする

Railsは、:render_inに応答できるオブジェクトを以下のようにレンダリングできます。

render MyRenderable.new

上のコードは、現在のビューコンテキストで指定のオブジェクト上のrender_inを呼び出します。

2.2.13 renderのオプション

renderメソッド呼び出しでは、一般に以下の6つのオプションを指定できます。

  • :content_type
  • :layout
  • :location
  • :status
  • :formats
  • :variants
2.2.13.1 :content_typeオプション

レンダリングのMIME Content-Typeヘッダーは、デフォルトでtext/htmlになります(ただし:jsonを指定するとapplication/json:xmlを指定するとapplication/xmlになります)。Content-Typeを変更したい場合は、:content_typeオプションを指定します。

render template: "feed", content_type: "application/rss"
2.2.13.2 :layoutオプション

renderで指定できるほとんどのオプションでは、レンダリングされるコンテンツが現在のレイアウトの一部としてブラウザ上で表示されます。レイアウトの詳細や利用法については本ガイドで後述します。

:layoutオプションで特定のファイルを指定すると、現在のアクションでレイアウトとして利用できるようになります。

render layout: "special_layout"

レイアウトを使わずにレンダリングすることも可能です。

render layout: false
2.2.13.3 :locationオプション

:locationオプションで、HTTP Locationヘッダーを設定できます。

render xml: photo, location: photo_url(photo)
2.2.13.4 :statusオプション

Railsが返すレスポンスのHTTPステータスコードは自動的に生成されます(ほとんどの場合200 OK)。:statusオプションを使うと、レスポンスのステータスコードを変更できます。

render status: 500
render status: :forbidden

ステータスコードは、以下の数字とシンボルのどちらでも指定できます。

レスポンスクラス HTTPステータスコード シンボル
Informational 100 :continue
101 :switching_protocols
102 :processing
Success 200 :ok
201 :created
202 :accepted
203 :non_authoritative_information
204 :no_content
205 :reset_content
206 :partial_content
207 :multi_status
208 :already_reported
226 :im_used
Redirection 300 :multiple_choices
301 :moved_permanently
302 :found
303 :see_other
304 :not_modified
305 :use_proxy
307 :temporary_redirect
308 :permanent_redirect
Client Error 400 :bad_request
401 :unauthorized
402 :payment_required
403 :forbidden
404 :not_found
405 :method_not_allowed
406 :not_acceptable
407 :proxy_authentication_required
408 :request_timeout
409 :conflict
410 :gone
411 :length_required
412 :precondition_failed
413 :payload_too_large
414 :uri_too_long
415 :unsupported_media_type
416 :range_not_satisfiable
417 :expectation_failed
421 :misdirected_request
422 :unprocessable_entity
423 :locked
424 :failed_dependency
426 :upgrade_required
428 :precondition_required
429 :too_many_requests
431 :request_header_fields_too_large
451 :unavailable_for_legal_reasons
Server Error 500 :internal_server_error
501 :not_implemented
502 :bad_gateway
503 :service_unavailable
504 :gateway_timeout
505 :http_version_not_supported
506 :variant_also_negotiates
507 :insufficient_storage
508 :loop_detected
510 :not_extended
511 :network_authentication_required

「non-content」ステータスコード (100〜199、204、205、304のいずれか)を指定してレンダリングすると、コンテンツがレスポンスから削除されます。

2.2.13.5 :formatsオプション

:formatsオプションは、リクエストで利用するフォーマットを指定します(デフォルトはhtml)。:formatsオプションにはシンボルまたは配列を渡せます。

render formats: :xml
render formats: [:json, :xml]

指定されたフォーマットのテンプレートが存在しない場合は、ActionView::MissingTemplateエラーが発生します。

2.2.13.6 :variantsオプション

:variantsオプションは、同じフォーマットの別テンプレートを探索するようRailsに指示します。 :variants`オプションに渡す別テンプレートのリストには、シンボルまたは配列が使えます。

以下は利用方法の例です。

# HomeController#indexで呼び出す
render variants: [:mobile, :desktop]

上のコードで別テンプレートのセットを渡すと、以下のテンプレートを探索して、存在するテンプレートのうち最初のものが使われます。

  • app/views/home/index.html+mobile.erb
  • app/views/home/index.html+desktop.erb
  • app/views/home/index.html.erb

指定のフォーマットを持つテンプレートが存在しない場合は、ActionView::MissingTemplateエラーが発生します。

render呼び出しで別テンプレートリストを指定する代わりに、以下のようにコントローラアクションのrequestオブジェクトで設定することもできます。

def index
  request.variant = determine_variant
end

private

def determine_variant
  variant = nil
  # 別テンプレートを決定するコード
  variant = :mobile if session[:use_mobile]

  variant
end
2.2.14 レイアウトの探索順序

Railsは現在のレイアウトを探索するときに、最初に現在のコントローラと同じ基本名を持つレイアウトがapp/views/layoutsディレクトリにあるかどうかを調べます。たとえば、PhotosControllerクラスのアクションからレンダリングする場合は、app/views/layouts/photos.html.erb(またはapp/views/layouts/photos.builder)を探索します。コントローラ固有のレイアウトが見つからない場合は、app/views/layouts/application.html.erbまたはapp/views/layouts/application.builderを使います。.erbレイアウトがない場合は、.builderレイアウトがあればそれを使います。Railsには、個別のコントローラやアクションに割り当てる特定のレイアウトをより正確に指定する方法がいくつも用意されています。

2.2.14.1 コントローラ用のレイアウトを指定する

デフォルトのレイアウト名ルールは、以下のようにlayout宣言で上書きできます。

class ProductsController < ApplicationController
  layout "inventory"
  #...
end

この宣言によって、上のコードのProductsControllerのレンダリングでapp/views/layouts/inventory.html.erbレイアウトが使われるようになります。

アプリケーション全体で特定のレイアウトを使いたい場合は、layoutApplicationControllerクラスで宣言します。

class ApplicationController < ActionController::Base
  layout "main"
  #...
end

この宣言によって、アプリケーションのすべてのビューでapp/views/layouts/main.html.erbレイアウトが使われるようになります。

2.2.14.2 実行時にレイアウトを指定する

以下のようにレイアウトをシンボルで指定すると、リクエストが実際に処理されるまでレイアウトの選択を先延ばしできます。

class ProductsController < ApplicationController
  layout :products_layout

  def show
    @product = Product.find(params[:id])
  end

  private
    def products_layout
      @current_user.special? ? "special" : "products"
    end

end

上のコードは、現在のユーザーが特別なユーザーの場合、そのユーザーが表示する製品ページに特別なレイアウトを適用します。

レイアウトを決定するときには、Procなどのインラインメソッドも利用できます。たとえば以下のようにProcオブジェクトを渡すと、Procに渡すブロックにcontrollerインスタンスが渡されるので、現在のリクエストに応じてレイアウトを切り替えられます。

class ProductsController < ApplicationController
  layout Proc.new { |controller| controller.request.xhr? ? "popup" : "application" }
end
2.2.14.3 条件付きレイアウト

コントローラレベルで指定されたレイアウトでは、:onlyオプションと:exceptオプションがサポートされています。これらのオプションは、コントローラ内のメソッド名に対応する単一のメソッド名またはメソッド名の配列を引数として受け取ります。

class ProductsController < ApplicationController
  layout "product", except: [:index, :rss]
end

上の宣言によって、rssメソッドとindexメソッド以外のすべてのメソッドにproductレイアウトが適用されます。

2.2.14.4 レイアウトの継承

レイアウト宣言は下の階層にカスケードされます。以下のように、下の階層(より具体的なレイアウト宣言)は、上の階層(より一般的なレイアウト)を常にオーバーライドします。

  • application_controller.rb

    class ApplicationController < ActionController::Base
      layout "main"
    end
    
  • articles_controller.rb

    class ArticlesController < ApplicationController
    end
    
  • special_articles_controller.rb

    class SpecialArticlesController < ArticlesController
      layout "special"
    end
    
  • old_articles_controller.rb

    class OldArticlesController < SpecialArticlesController
      layout false
    
      def show
        @article = Article.find(params[:id])
      end
    
      def index
        @old_articles = Article.older
        render layout: "old"
      end
      # ...
    end
    

上のアプリケーションは以下のように動作します。

  • 原則としてビューのレンダリングにはmainレイアウトが使われる。
  • ArticlesController#indexではmainレイアウトが使われる。
  • SpecialArticlesController#indexではspecialレイアウトが使われる。
  • OldArticlesController#showではレイアウトが適用されない。
  • OldArticlesController#indexではoldレイアウトが使われる。
2.2.14.5 テンプレートの継承

レイアウト継承のロジックと同様に、テンプレートやパーシャルが通常のパスで見つからない場合、コントローラーは継承パスを探索してレンダリングするテンプレートやパーシャルを見つけようとします。以下の例で考えてみましょう。

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
end
# app/controllers/admin_controller.rb
class AdminController < ApplicationController
end
# app/controllers/admin/products_controller.rb
class Admin::ProductsController < AdminController
  def index
  end
end

このときのadmin/products#indexアクションは以下の順に探索されます。

  • app/views/admin/products/
  • app/views/admin/
  • app/views/application/

つまり、app/views/application/は共有パーシャルを置くのに適しています。これらはERBで次のようにレンダリングされます。

<%# app/views/admin/products/index.html.erb %>
<%= render @products || "empty_list" %>

<%# app/views/application/_empty_list.html.erb %>
There are no items in this list <em>yet</em>.
2.2.15 二重レンダリングエラーを避ける

ほとんどのRails開発者は、"Can only render or redirect once per action"エラーを目にしたことがあるでしょう。いまいましいエラーですが、修正は比較的簡単です。このエラーはほとんどの場合、開発者がrenderメソッドの基本的な動作を誤解していることが原因です。

このエラーを発生する以下のコードを例にとって説明しましょう。

def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show"
  end
  render action: "regular_show"
end

@book.special?trueと評価されると、Railsはレンダリングを開始し、@book変数をspecial_showビューに転送します。しかしshowアクションのコードはそこで止まらないことにご注意ください。showアクションのコードは最終行まで実行され、regular_showビューのレンダリングを行おうとした時点でエラーが発生します。 解決法はいたって単純で、renderメソッドやredirectメソッドが1つのコード実行パス内で「1回だけ」呼び出されるようにすることです。こういうときにはand returnというとても便利な書き方があります。以下はこの方法で修正したコードです。

def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show" and return
  end
  render action: "regular_show"
end

&& returnではなくand returnを使うのがポイントです。Ruby言語の&&演算子の優先順位はandより高いので、&& returnはこの文脈では正常に動作しません。

なお、Rails組み込みのActionControllerが行なう暗黙のレンダリングは、renderメソッドが呼び出されているかどうかを確認してから開始されます。従って、以下のコードは正常に動作します。

def show
  @book = Book.find(params[:id])
  if @book.special?
    render action: "special_show"
  end
end

上のコードは、ある本がspecial?である場合にのみspecial_showテンプレートでレンダリングし、それ以外の場合はshowテンプレートでレンダリングします。

2.3 redirect_toを使う

HTTPリクエストにレスポンスを返すもう1つの方法は、redirect_toを使う方法です。前述のとおり、renderはレスポンスを構成するときに使うビュー(または他のアセット)を指定しますが、redirect_toメソッドは、この点においてrenderメソッドと根本的に異なります。redirect_toメソッドは、別のURLにリクエストを再送信するようブラウザに指示します。たとえば以下の呼び出しを行なうと、アプリケーションで現在どのページが表示されていても、写真のindexページにリダイレクトされます。

redirect_to photos_url

redirect_backメソッドを使うと、直前のページに戻ります。戻る場所にはHTTP_REFERERヘッダから取り出した情報が使われますが、このヘッダーがブラウザ側で設定されているかどうかは保証されていないので、以下のように必ずfallback_locationを設定しなければなりません。

redirect_back(fallback_location: root_path)

redirect_toredirect_backを呼び出しても、メソッドの実行がすぐに中断されるのではなく、単にHTTPのレスポンスが設定されます。もしこれらの呼び出しの後に別のメソッドがあると、そのメソッドは実行されてしまいます。必要であれば、明示的にreturnすることで(または別の方法で)中断できます。

2.3.1 リダイレクトのステータスコードを変更する

redirect_toを呼び出すと、一時的なリダイレクトを意味するHTTPステータスコード302がブラウザに返され、ブラウザはそれに基いてリダイレクトを行います。別のステータスコード(おそらくHTTP 301: 恒久的なリダイレクト)に変更するには:statusオプションを使います。

redirect_to photos_path, status: 301

render:statusオプションの場合と同様、redirect_to:statusオプションにも数値または数値に対応するシンボルを渡せます。

2.3.2 renderredirect_toの違いを理解する

redirect_toを一種のgotoコマンドとして理解している開発者を見かけることがあります(Railsコードの実行位置をある場所から別の場所にジャンプするコマンドであると考えているわけです)。これは正しくありませんredirect_toを実行すると、コードはそこで実行を停止して、ブラウザからの次のリクエストを待ちます(これは通常のスタンバイ状態です)。その直後、redirect_toでブラウザに送信したHTTPステータスコード302に従って、ブラウザは別のURLへのリクエストをサーバーに送信し、サーバーはそのリクエストを改めて処理します。

renderredirect_toの違いを以下のアクションで比較してみましょう。

def index
  @books = Book.all
end

def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    render action: "index"
  end
end

上のフォームのコードでは、@bookインスタンス変数がnilの場合に問題が生じる可能性があります。render :actionを実行しても、対象となるアクションのコードは実行されないことを思い出しましょう。つまりindexビューでおそらく必要となる@booksインスタンス変数には何も設定されず、空の蔵書リストが表示されてしまいます。 これを修正する方法の1つは、renderを以下のようにredirect_toに変更することです。

def index
  @books = Book.all
end

def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    redirect_to action: :index
  end
end

上のコードでは、ブラウザから改めてindexページにリクエストが送信されるので、indexメソッドのコードが正常に実行されます。

上のコードで惜しい点は、ブラウザとの通信が1往復必要になることです。ブラウザから/books/1に対してshowアクションが呼び出され、本が1冊もないことをコントローラが検出すると、コントローラはブラウザにステータスコード302(リダイレクト)レスポンスを返し、/books/に再度アクセスするようブラウザに指示します。ブラウザはこの指示に沿って、コントローラのindexアクションを呼び出すリクエストを改めてサーバーに送信します。コントローラはこのリクエストを受け取って、データベースからすべての蔵書リストを取り出し、indexテンプレートをレンダリングして結果をブラウザに送り返すと、ブラウザで蔵書リストが表示されます。

このやりとりによる遅延は、小規模なアプリケーションであればおそらく問題になりませんが、場合によってはレスポンスの遅延が問題になることもあります。この問題を解決する方法の1つを以下のコードで説明します。

def index
  @books = Book.all
end

def show
  @book = Book.find_by(id: params[:id])
  if @book.nil?
    @books = Book.all
    flash.now[:alert] = "Your book was not found"
    render "index"
  end
end

上のコードの動作は次のとおりです。指定されたidを持つ本が見つからない場合は、モデル内のすべての蔵書リストを@booksインスタンス変数に保存します。次にflash警告メッセージを追加してユーザーに状況を伝え、さらにindex.html.erbテンプレートを直接レンダリングしてから結果をブラウザに送り返します。

2.4 headでヘッダのみのレスポンスを生成する

headメソッドを使うと、本文(body)のないヘッダのみのレスポンスをブラウザに送信できます。headメソッドの引数には、HTTPステータスコードを示すさまざまなシンボルを指定できます(テーブルを参照)。オプションの引数は、ヘッダ名と値をペアにしたハッシュ値として解釈されます。たとえば、以下のコードはエラーヘッダーのみのレスポンスを返します。

head :bad_request

上のコードによって以下のヘッダーが生成されます。

HTTP/1.1 400 Bad Request
Connection: close
Date: Sun, 24 Jan 2010 12:15:53 GMT
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
X-Runtime: 0.013483
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache

以下のように、ヘッダーに別の情報を含めることもできます。

head :created, location: photo_path(@photo)

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

HTTP/1.1 201 Created
Connection: close
Date: Sun, 24 Jan 2010 12:16:44 GMT
Transfer-Encoding: chunked
Location: /photos/1
Content-Type: text/html; charset=utf-8
X-Runtime: 0.083496
Set-Cookie: _blog_session=...snip...; path=/; HttpOnly
Cache-Control: no-cache

3 レイアウトを構成する

Railsがビューをレスポンスとしてレンダリングすると、そのビューに現在のレイアウトも組み込まれます。現在のレイアウトを探索するときには、本ガイドで既に説明したルールが使われます。レイアウト内では、さまざまな出力の断片を組み合わせて最終的なレスポンスを得るために、以下の3つのツールを利用できます。

  • アセットタグ
  • yieldcontent_for
  • パーシャル

3.1 アセットタグヘルパー

アセットタグヘルパーは、「フィード」「JavaScript」「スタイルシート」「画像」「動画「音声」のビューにリンクするHTMLを生成するメソッドです。Railsでは以下の6つのアセットタグヘルパーが利用できます。

これらのタグは、レイアウトや別のビューでも利用できます。このうち、auto_discovery_link_tagjavascript_include_tagstylesheet_link_tagはレイアウトの<head>セクションで使うのが普通です。

これらのアセットタグヘルパーは、指定の場所にアセットがあるかどうかを確認しません。単に指示どおりにリンクを生成します。

auto_discovery_link_tagヘルパーでHTMLを生成すると、さまざまなブラウザやRSSリーダーでRSSフィードやAtomフィード、JSONフィードを検出できるようになります。このメソッドの引数には、リンクの種類(:rss:atom:json)、url_forに渡されるオプションハッシュ、タグのオプションハッシュを渡せます。

<%= auto_discovery_link_tag(:rss, {action: "feed"},
  {title: "RSS Feed"}) %>

auto_discovery_link_tagでは以下の3つのタグオプションを利用できます。

  • :rel: リンク内のrel値を指定する(デフォルト値は "alternate")。
  • :type: MIMEタイプを明示的に指定する(Railsは適切なMIMEタイプを自動生成します)。
  • :title: リンクのタイトルを指定する(デフォルト値は大文字の:type値: "ATOM" や "RSS" など)。
3.1.2 javascript_include_tagでJavaScriptファイルにリンクする

javascript_include_tagヘルパーは、指定されたソースごとにHTML <script>タグを返します。

Railsのアセットパイプライン を有効にすると、/assets/javascripts/ディレクトリにあるJavaScriptファイルにリンクされます(旧Railsのpublic/javascriptsではありません)。このリンクはアセットパイプラインによって配信されます。

Railsアプリケーション内やRailsエンジン内のJavaScriptファイルは、app/assetslib/assetsvendor/assetsのいずれかのディレクトリに置かれます。これらの置き場所について詳しくはアセットパイプラインガイドの「アセットの編成」 を参照してください。

ドキュメントルートからの相対フルパスやURLも指定できます。たとえば、app/assetslib/assets、またはvendor/assetsの下にあるjavascriptsディレクトリのJavaScriptファイルにリンクしたい場合は以下のようにします。

<%= javascript_include_tag "main" %>

上のコードにより、以下のようなscriptタグが出力されます。

<script src='/assets/main.js'></script>

このアセットへのリクエストは、sprockets gemによって配信されます。

複数のファイルをインクルードする(app/assets/javascripts/main.jsapp/assets/javascripts/columns.jsなど)には、以下のように書きます。

<%= javascript_include_tag "main", "columns" %>

app/assets/javascripts/main.jsとサブディレクトリのapp/assets/javascripts/photos/columns.jsファイルをインクルードするには以下のように書きます。

<%= javascript_include_tag "main", "/photos/columns" %>

外部のhttp://example.com/main.jsをインクルードするには以下のように書きます。

<%= javascript_include_tag "http://example.com/main.js" %>

stylesheet_link_tagヘルパーは、指定のソースごとにHTML <link>タグを返します。

Railsのアセットパイプラインを有効にすると、このヘルパーは/assets/stylesheets/ディレクトリにあるCSSファイルへのリンクを生成します。このリンクはsprockets gemによって処理されます。スタイルシートファイルは、app/assetslib/assetsvendor/assetsディレクトリのいずれかに置かれます。

ドキュメントルートからの相対フルパスやURLも指定できます。たとえば、app/assetslib/assets、またはvendor/assetsの下にあるstylesheetsディレクトリのスタイルシートファイルにリンクするには、以下のように書きます。

<%= stylesheet_link_tag "main" %>

app/assets/railsguides/stylesheets/main.cssapp/assets/stylesheets/columns.cssをインクルードするには、以下のように書きます。

<%= stylesheet_link_tag "main", "columns" %>

app/assets/railsguides/stylesheets/main.cssとサブディレクトリのapp/assets/stylesheets/photos/columns.cssファイルをインクルードするには、以下のように書きます。

<%= stylesheet_link_tag "main", "photos/columns" %>

外部のhttp://example.com/main.cssをインクルードするには、以下のように書きます。

<%= stylesheet_link_tag "http://example.com/main.css" %>

stylesheet_link_tagによって作成されるリンクには、デフォルトでrel="stylesheet"属性が追加されます。適切なオプション(:relなど)を指定するとデフォルト値を上書きできます。

<%= stylesheet_link_tag "main_print", media: "print" %>
3.1.4 image_tagで画像にリンクする

image_tagは、指定の画像ファイルにリンクするHTML <img />タグを生成します。デフォルトでは、ファイルはpublic/images以下から読み込まれます。

画像ファイルの拡張子は省略できません。

<%= image_tag "header.png" %>

画像ファイルへのパスも指定できます。

<%= image_tag "icons/delete.gif" %>

HTMLオプションをハッシュ形式で追加できます。

<%= image_tag "icons/delete.gif", {height: 45} %>

ユーザーがブラウザで画像を非表示にしている場合、alt属性のテキストを表示できます。alt属性が明示的に指定されていない場合は、ファイル名がaltテキストとして使われます。このときファイル名の先頭は大文字になり、拡張子は取り除かれます。たとえば、以下の2つのimage_tagヘルパーは同じコードを返します。

<%= image_tag "home.gif" %>
<%= image_tag "home.gif", alt: "Home" %>

"幅x高さ"形式で特殊なsizeタグも指定できます。

<%= image_tag "home.gif", size: "50x20" %>

上の特殊タグ以外にも、:class:id:nameなどの標準的なHTMLオプションを最終的にハッシュにしたものを引数に渡せます。

<%= image_tag "home.gif", alt: "Go Home",
                          id: "HomeImage",
                          class: "nav_bar" %>
3.1.5 video_tagで動画ファイルにリンクする

video_tagヘルパーは、指定の動画ファイルにリンクするHTML5 <video>タグを生成します。デフォルトでは、public/videosディレクトリからファイルを読み込みます。

<%= video_tag "movie.ogg" %>

上のコードによって以下が生成されます。

<video src="/videos/movie.ogg" />

image_tagの場合と同様、public/videosディレクトリからの絶対パスや相対パスも指定できます。さらに、image_tagの場合と同様に、size: "#{幅}x#{高さ}"オプションも指定できます。idclassなどのHTMLオプションも引数の末尾に追加できます。

video_tagには、HTMLオプションハッシュ形式で以下を含む任意の<video> HTMLオプションも指定できます。

  • poster: "image_name.png":動画再生前に表示したい画像を指定します。
  • autoplay: true: ページが読み込まれると動画を自動再生します。
  • loop: true: 動画をループ再生します。
  • controls: true: ブラウザの動画制御機能を有効にします。
  • autobuffer: true: ページ読み込み時に動画ファイルをプリロードします。

複数の動画ファイルを表示するには、video_tagに動画ファイルの配列を渡します。

<%= video_tag ["trailer.ogg", "movie.ogg"] %>

上のコードによって以下が生成されます。

<video>
  <source src="/videos/trailer.ogg">
  <source src="/videos/movie.ogg">
</video>
3.1.6 audio_tagで音声ファイルにリンクする

audio_tagは、指定の音声ファイルにリンクするHTML5 <audio>タグを生成します。デフォルトでは、public/audiosディレクトリからファイルを読み込みます。

<%= audio_tag "music.mp3" %>

音声ファイルへのパスも指定できます。

<%= audio_tag "music/first_song.mp3" %>

:id:classなどのオプションもハッシュ形式で指定できます。

video_tagと同様、audio_tagにも以下の特殊オプションがあります。

  • autoplay: true: ページ読み込み時に音声ファイルを自動再生します。
  • controls: true: ブラウザの音声ファイル制御機能を有効にします。
  • autobuffer: true: ページ読み込み時に動画ファイルをプリロードします。

3.2 yieldを理解する

レイアウトのコンテキスト内では、ビューのコンテンツを挿入する位置をyieldで指定します。yieldの最もシンプルな使い方は、以下のようにyieldを1個だけ使って、現在レンダリングされているビューのコンテンツ全体をその位置に挿入することです。

<html>
  <head>
  </head>
  <body>
  <%= yield %>
  </body>
</html>

以下のように、yieldをレイアウトの複数のセクションに配置することも可能です。

<html>
  <head>
  <%= yield :head %>
  </head>
  <body>
  <%= yield %>
  </body>
</html>

ビューのメインbodyは、常に「名前のない」yieldの位置でレンダリングされます。コンテンツを名前付きyieldの位置でレンダリングするには、content_forメソッドを使います。

3.3 content_forを使う

content_forメソッドを使うと、レイアウト内の名前付きyieldブロックの位置にコンテンツを挿入できます。たとえば、以下のビューは、上のレイアウトに挿入されます。

<% content_for :head do %>
  <title>A simple page</title>
<% end %>

<p>Hello, Rails!</p>

このページのレンダリング結果がレイアウトに挿入されると、最終的に以下のHTMLになります。

<html>
  <head>
  <title>A simple page</title>
  </head>
  <body>
  <p>Hello, Rails!</p>
  </body>
</html>

content_forメソッドは、たとえばレイアウトを「サイドバー」や「フッター」などの領域に分割して、それぞれに異なるコンテンツを挿入したい場合などに大変便利です。あるいは、多くのページで使う共通のヘッダーがあり、特定のページでのみJavaScriptやCSSファイルをそのヘッダーに挿入したい場合にも便利です。

3.4 パーシャルを使う

パーシャル(部分テンプレート)は、上と別の方法でレンダリング処理を扱いやすい単位に分割するしくみです。パーシャルを使うと、レスポンスで表示するページの特定部分をレンダリングするコードを別ファイルに切り出せます。

3.4.1 パーシャルに名前を与える

パーシャルをビューの一部としてレンダリングするには、ビュー内で以下のようにrender メソッドを使います。

<%= render "menu" %>

レンダリングされるビュー内に置かれている上のコードは、その場所で_menu.html.erbという名前のファイルをレンダリングします。パーシャルファイル名の冒頭にはアンダースコアが付いていることにご注意ください。アンダースコアは通常のビューと区別するために付けられていますが、アンダースコアなしで参照されることもあります。これは他のフォルダの下にあるパーシャルを取り込む場合も同様です。

<%= render "shared/menu" %>

上のコードは、app/views/shared/_menu.html.erbパーシャルをその位置に取り込みます。

3.4.2 シンプルなビューでパーシャルを使う

パーシャルの利用方法の1つは、パーシャルをサブルーチンと同様に扱うことです。表示の詳細をパーシャル化してビューから追い出し、コードを読みやすくします。たとえば以下のようなビューがあるとします。

<%= render "shared/ad_banner" %>

<h1>Products</h1>

<p>Here are a few of our fine products:</p>
...

<%= render "shared/footer" %>

上のコードの_ad_banner.html.erbパーシャルと_footer.html.erbパーシャルに含まれるコンテンツは、アプリケーションの多くのページと共有できます。こうすることで、そのセクションの詳細を気にせずにページの開発に集中できます。

本ガイドの直前のセクションで説明したように、yieldはレイアウトを簡潔に保つ上で極めて強力なツールです。yieldは純粋なRubyなので、ほぼどこでも利用できます。たとえばyieldを用いると、同じようなさまざまなリソースで使うフォームのレイアウトをDRYに定義できます。

  • users/index.html.erb

    <%= render "shared/search_filters", search: @q do |form| %>
      <p>
        Name contains: <%= form.text_field :name_contains %>
      </p>
    <% end %>
    
  • roles/index.html.erb

    <%= render "shared/search_filters", search: @q do |form| %>
      <p>
        Title contains: <%= form.text_field :title_contains %>
      </p>
    <% end %>
    
  • shared/_search_filters.html.erb

    <%= form_with model: search do |form| %>
      <h1>Search form:</h1>
      <fieldset>
        <%= yield form %>
      </fieldset>
      <p>
        <%= form.submit "Search" %>
      </p>
    <% end %>
    

すべてのページで共有したいコンテンツがある場合は、そのコンテンツのパーシャルをレイアウトに直接配置できます。

3.4.3 パーシャルレイアウト

ビューにレイアウトがあるのと同様に、パーシャルでも独自のレイアウトファイルを利用できます。たとえば、以下のようなパーシャルを呼び出すとします。

<%= render partial: "link_area", layout: "graybar" %>

上のコードは、_link_area.html.erbという名前のパーシャルを探索し、_graybar.html.erbという名前のレイアウトでレンダリングします。パーシャル用のレイアウトファイルは、対応する通常のパーシャルと同様、パーシャル名の冒頭にアンダースコアを追加して、(マスターのlayoutsディレクトリではなく)そのレイアウトが属しているパーシャルファイルと同じディレクトリに配置します。

:layoutなどの追加オプションも渡す場合は、:partialオプションも明示的に指定する必要があります。

3.4.4 ローカル変数を渡す

パーシャルにローカル変数を渡すことで、パーシャルがさらに強力かつ柔軟になります。たとえば、以下のようにnewページとeditページの違いがごくわずかしかない場合は、この手法でコードの重複を解消できます。

  • new.html.erb

    <h1>New zone</h1>
    <%= render partial: "form", locals: {zone: @zone} %>
    
  • edit.html.erb

    <h1>Editing zone</h1>
    <%= render partial: "form", locals: {zone: @zone} %>
    
  • _form.html.erb

    <%= form_with model: zone do |form| %>
      <p>
        <b>Zone name</b><br>
        <%= form.text_field :name %>
      </p>
      <p>
        <%= form.submit %>
      </p>
    <% end %>
    

上の2つのビューは同じパーシャルをレンダリングしますが、Action Viewのsubmitヘルパーはnewアクションで"Create Zone"を返し、editアクションで"Update Zone"を返します。

ローカル変数を特定の状況に限ってパーシャルに渡すには、local_assignsを使います。

  • index.html.erb

    <%= render user.articles %>
    
  • show.html.erb

    <%= render article, full: true %>
    
  • _article.html.erb

    <h2><%= article.title %></h2>
    
    <% if local_assigns[:full] %>
      <%= simple_format article.body %>
    <% else %>
      <%= truncate article.body %>
    <% end %>
    

これにより、すべてのローカル変数を宣言せずにパーシャルを使えるようになります。

どのパーシャルにも、パーシャル名と同じ名前のローカル変数が1つずつあります(ローカル変数の冒頭にアンダースコアは付きません)。objectオプションを使うと、以下のようにこのローカル変数にオブジェクトを渡せます。

<%= render partial: "customer", object: @new_customer %>

上のcustomerパーシャルの内側では、customerローカル変数は親のビューの@new_customer変数を指すようになります。

あるモデルのインスタンスをパーシャルでレンダリングする場合は、以下のショートハンド記法を利用できます。

<%= render @customer %>

上のコードの@customerインスタンス変数にCustomerモデルのインスタンスが含まれていると仮定すると、_customer.html.erbパーシャルを用いてレンダリングします。このパーシャルに渡したcustomerローカル変数は、親ビューにある@customerインスタンス変数を参照します。

3.4.5 コレクションをレンダリングする

パーシャルはコレクションをレンダリングするときにも極めて有用です。collection:オプションを指定してパーシャルにコレクションを渡すと、コレクションのメンバーごとにパーシャルをレンダリングしてその位置に挿入します。

  • index.html.erb

    <h1>Products</h1>
    <%= render partial: "product", collection: @products %>
    
  • _product.html.erb

    <p>Product Name: <%= product.name %></p>
    

複数形のコレクションを渡してパーシャルを呼び出すと、パーシャルの個別のインスタンスは、パーシャルと同じ名前の変数(アンダースコアなし)を経由してコレクションの個別のメンバーにアクセスできます。上の場合はパーシャル名が_productなので、_productパーシャル内でproductという名前の変数を参照することで、レンダリングされるインスタンスを取得できます。

コレクションのレンダリングにはショートハンド記法もあります。@productsproductインスタンスのコレクションであるとすると、index.html.erbに以下のように書くことで同じ結果を得られます。

<h1>Products</h1>
<%= render @products %>

ここで使われるパーシャル名は、コレクションのモデル名に基いて決定されます。実際は、一様でない(種類の異なるメンバーを含む)コレクションでも上の方法が使えます。この場合、コレクションのメンバーに応じて適切なパーシャルが自動的に選択されます。

  • index.html.erb

    <h1>Contacts</h1>
    <%= render [customer1, employee1, customer2, employee2] %>
    
  • customers/_customer.html.erb

    <p>Customer: <%= customer.name %></p>
    
  • employees/_employee.html.erb

    <p>Employee: <%= employee.name %></p>
    

上のコードでは、コレクションのメンバーに応じてcustomerパーシャルまたはemployeeパーシャルが自動的に選択されます。

コレクションが空の場合はrenderがnilを返します。以下のような簡単な方法でもよいので、代わりのコンテンツを表示するようにしましょう。

<h1>Products</h1>
<%= render(@products) || "There are no products available." %>
3.4.6 ローカル変数

パーシャル内で独自のローカル変数を使いたい場合は、:asオプションを指定してパーシャルを呼び出します。

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

上のように変更することで、itemという名前のローカル変数で@productsコレクションのインスタンスにアクセスできるようになります。

locals: {}オプションを使うと、レンダリングするパーシャルに任意のローカル変数を渡せます。

<%= render partial: "product", collection: @products,
           as: :item, locals: {title: "Products Page"} %>

上の場合、titleという名前のローカル変数に"Products Page"という値が含まれており、パーシャルからこの値にアクセスできるようになります。

コレクションによって呼び出されるパーシャル内では、カウンタ変数も利用できます。このカウンタ変数は、パーシャル名の末尾に_counterを追加した名前になります。たとえば、パーシャル内で@productsをレンダリングするときに、_product.html.erbproduct_counter変数を参照できます。product_counter変数は、それを囲むビュー内でレンダリングされた回数を表します。なお、これはas:オプションでパーシャル名を変更した場合にも該当します。たとえば上のコードのカウンタ変数はitem_countになります。

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

:spacer_templateオプションを使うと、メインパーシャルのインスタンスと交互にレンダリングしたい調整用のセカンドパーシャルを指定できます。

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

上のコードは、_productパーシャルと_productパーシャルの間に_product_rulerパーシャル(引数を受け取らない)をレンダリングします。

3.4.8 コレクションのパーシャルレイアウト

コレクションをレンダリングするときにも:layoutオプションを指定できます。

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

このレイアウトは、コレクション内の各項目をレンダリングするたびに一緒にレンダリングされます。パーシャル内の場合と同様、このレイアウトでも現在のオブジェクトとオブジェクト名_counter変数を利用できます。

3.5 ネステッドレイアウトを使う

特定のコントローラをサポートするために、アプリケーションの標準レイアウトをほんの少し変えたレイアウトが必要になることがあります。メインのレイアウトを複製して編集したりしなくても、ネステッドレイアウト(サブテンプレートと呼ばれることもあります)を使えばこのようなレイアウトを実現できます。

以下のApplicationControllerレイアウトがあるとします。

  • app/views/layouts/application.html.erb

    <html>
    <head>
      <title><%= @page_title or "Page Title" %></title>
      <%= stylesheet_link_tag "layout" %>
      <style><%= yield :stylesheets %></style>
    </head>
    <body>
      <div id="top_menu">Top menu items here</div>
      <div id="menu">Menu items here</div>
      <div id="content"><%= content_for?(:content) ? yield(:content) : yield %></div>
    </body>
    </html>
    

NewsControllerで生成されるページでは、トップメニューを隠して右メニューを追加したいとします。

  • app/views/layouts/news.html.erb

    <% content_for :stylesheets do %>
      #top_menu {display: none}
      #right_menu {float: right; background-color: yellow; color: black}
    <% end %>
    <% content_for :content do %>
      <div id="right_menu">Right menu items here</div>
      <%= content_for?(:news_content) ? yield(:news_content) : yield %>
    <% end %>
    <%= render template: "layouts/application" %>
    

以上で完了です。これによってNewsビューで新しいレイアウトが使われるようになり、トップメニューが隠されて"content" divタグ内に右メニューが新しく追加されます。

この手法を用いる別のサブテンプレートでも同様の結果を得る方法はいくつも考えられます(ネストのレベルに制限はありません)。ActionView::renderメソッドをrender template: 'layouts/news'経由で使うと、Newsレイアウトで新しいレイアウトがベースになります。Newsレイアウトをサブテンプレート化する予定がない場合は、単にcontent_for?(:news_content) ? yield(:news_content) : yieldyieldに置き換えれば済みます。

フィードバックについて

Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。

原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨

本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。よろしくお願いします。

YassLab 株式会社
https://yasslab.jp/

支援・協賛

Railsガイドは下記のサポーターから継続的な支援を受けています。Railsガイドへの支援・協賛にご興味あれば info@yasslab.jp までお問い合わせください。

  1. Star
  2. このエントリーをはてなブックマークに追加