LoginSignup
24
12

More than 5 years have passed since last update.

[Rails]パーシャルをパーシャル名のみで呼び出そうとしてハマったところ

Last updated at Posted at 2019-03-10

Railsチュートリアルの演習で、パーシャル名によるパーシャルの呼び出しでハマったポイントをまとめます。

環境

・Rails 5.1.6
・Rails チュートリアル第4版(Rails5.1)

演習問題

演習13.3.2.1

Homeページをリファクタリングして、if-else文の分岐のそれぞれに対してパーシャルを作ってみましょう。

演習前時点でのHomeビューは以下の通り。

/sample_app/app/views/static_pages/home.html.erb
<% if logged_in? %>
  <div class="row">
    <aside class="col-md-4">
      <section class="user_info">
        <%= render 'shared/user_info' %>
      </section>
      <section class="micropost_form">
        <%= render 'shared/micropost_form' %>
      </section>
    </aside>
</div>
<% else %>
  <div class="center jumbotron">
  <h1>Welcome to the Sample App</h1>

  <h2>
    This is the home page for the
    <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
    sample application.
  </h2>

  <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
</div>

<%= link_to image_tag("rails.png", alt: "Rails logo"),
            'http://rubyonrails.org/' %>
<% end %>

上記のifelseの中身をそれぞれパーシャルに切り出し、renderで呼び出すことによって、ifelseの中身を一行でスッキリ書こうという問題ですね。

自分の解答

/sample_app/app/views/static_pages/内に二つのパーシャルlogged_innot_logged_inを切り出し、home.html.erbから呼び出す。

/sample_app/app/views/static_pages/home.html.erb
<% if logged_in? %>
  <%= render 'logged_in' %>
<% else %>
  <%= render 'not_logged_in' %>
<% end %>

パーシャルをhome.html.erbと同じフォルダに配置したため、パーシャル名のみで指定できると考え、上のように記述しました。このときのフォルダ構成は以下の通り。

views/
 └ static_pages/
    ├ _logged_in.html.erb
    ├ _not_logged_in.html.erb
    └ home.html.erb

一見これで問題なく動作していたのですが、homeから空白のマイクロポストを投稿した際に下記のようなエラーが発生しました。

ActionView::Template::Error (Missing partial microposts/_logged_in

views/microposts/内に_logged_inパーシャルが無いと怒られています。
views/static_pages/内の_logged_inパーシャルを探して欲しいのですが...

原因

コントローラからrenderでビューを描画したとき、パーシャル名のみで指定できるパーシャルは、コントローラと対応するビューフォルダ(views/コントローラ名)内のパーシャルである。

今回の場合、homeから投稿したmicropostが不正(空白または141文字以上)のとき、エラーメッセージを付け加えてhomeビューを再描画します。

/sample_app/app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]

  def create
    @micropost = current_user.microposts.build(micropost_params)
    if @micropost.save
      flash[:success] = "Micropost created!"
      redirect_to root_url
    else
      render 'static_pages/home'     # micropostが不正な場合、homeを再描画
    end
  end

  def destroy
  end

  private
    def micropost_params
      params.require(:micropost).permit(:content)
    end
end

このように、マイクロポストの作成に失敗したときは、microposts_controller.rbから、render 'static_pages/home'によって、homeビューを再描画しています。このとき、パーシャル名のみで指定できるパーシャルはviews/microposts内のパーシャルです。そのため、パーシャル名のみでパーシャルを指定すると、views/microposts内を探してしまい、エラーとなります。

views/
 ├ microposts/      ⬅️ ここを探してしまう!
 └ static_pages/
    ├ _logged_in.html.erb
    ├ _not_logged_in.html.erb
    └ home.html.erb

逆に、マイクロポストの作成に成功した場合や、直接homeのページのURLを入力した場合などは、static_pages_controller.rbからhomeビューを描画するので、views/static_pages内のパーシャルを探します。そのため、この場合はパーシャル名のみでパーシャルを呼び出していても、エラーが起きていませんでした。

正しい解答

/sample_app/app/views/static_pages/home.html.erb
<% if logged_in? %>
  <%= render 'static_pages/logged_in' %>
<% else %>
  <%= render 'static_pages/not_logged_in' %>
<% end %>

views/からの相対パスを指定すると、期待通りのパーシャル(logged_in)を呼び出すことができ、エラーが出なくなりました。

まとめ

ビューから、パーシャル名のみでパーシャルを呼び出すときは、そのビューを描画するコントローラを考慮する必要がある。

24
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
12