LoginSignup
6
2

More than 3 years have passed since last update.

renderが省略されてて眠れなくなったので調査

Last updated at Posted at 2019-12-17

はじめに

この記事はエムスリーキャリア Advent Calendar 2019 18日目の記事です。

エニジニアリンググループのおどです。
新卒入社した会社を8月に辞め、エムスリーキャリアに転職しました。
転職を機にLaravelエンジニアからRailsエンジニアに転身。

今回は利用するフレームワークがLaravelからRuby on Railsに変わり、つまずいたところを軽く深堀してみようと思います。

何に詰まったのか

入社して最初、簡単な文言修正のタスクからアサインされました。

「お、viewファイル見て文字直すだけとか簡単じゃん」

Laravelで開発をしてたときのようにroutingファイルを見て、controllerにたどり着きました。

home_controller.rb
class HomeController < ApplicationController
  def index
  end
end

どのファイルをレンダリングしているんだ…

気になって眠れない日々が始まりました。

Laravelはわかりやすかったな…

HomeController.php
<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;

class HomeController extends Controller
{
    public function index()
    {
        return view('home.index');
    }
}

調査

binding.pryを使ってトレースしていきたいと思います。

まず、railsではcontrollerのメソッドが終了すると、send_actionというメソッドが呼ばれます。

actionpack-5.2.4/lib/action_controller/metal/basic_implicit_render.rb
# frozen_string_literal: true

module ActionController
  module BasicImplicitRender # :nodoc:
    def send_action(method, *args)
      super.tap { default_render unless performed? }
    end

    def default_render(*args)
      head :no_content
    end
  end
end

controllerのメソッド内でrenderを1度も実行していないと、performed?falseを返します。

basic_implicit_renderをincludeしたaction_controller/metal/implicit_render.rb内に同名のdefault_renderが定義されていて、そこからrenderを呼び出します。

ここで呼ばれるrenderは、自分でcontrollerのメソッド内で呼び出したrenderと同じです。(action_controller/metal/instrumentation.rb)

action_controller/metal/instrumentation.rb
    def render(*args)
      render_output = nil
      self.view_runtime = cleanup_view_runtime do
        Benchmark.ms { render_output = super }
      end
      render_output
    end

さらに追っていくと、abstract_controller/rendering.rb内のrenderメソッドにたどり着きます。

abstract_controller/rendering.rb
    def render(*args, &block)
      options = _normalize_render(*args, &block)
      rendered_body = render_to_body(options)
      if options[:html]
        _set_html_content_type
      else
        _set_rendered_content_type rendered_format
      end
      self.response_body = rendered_body
    end

renderを省略した場合、このrender_to_bodyメソッドに渡る引数が異なってきます。

# renderを省略した場合
[1] pry(#<HomeController>)> options
=> {:prefixes=>["home", "application"], :template=>"index", :layout=>#<Proc:0x00007f481d31fa80@/myapp/vendor/bundle/ruby/2.6.0/gems/actionview-5.2.4/lib/action_view/layouts.rb:392>}

# renderを省略しなかった場合
[1] pry(#<HomeController>)> options
=> {:action=>"index", :prefixes=>["home", "application"], :template=>"index", :layout=>#<Proc:0x00007f481ef20a90@/myapp/vendor/bundle/ruby/2.6.0/gems/actionview-5.2.4/lib/action_view/layouts.rb:392>}

渡される引数を見ると、prefixesに呼び出したコントローラ名、templateにメソッド名が格納されているのが分かります。
renderを省略するとprefixesとtemplateから、省略しないとprefixesとactionからビューファイルの名前を組み立ててレンダリングしてくれているようです。

わかったこと

  • renderを省略すると呼び出しもとからファイル名を組み立ててレンダリングしてくれる

ちなみに

省略した場合に呼ばれるrenderは通常と同じrenderなので、引数だけを省略しても同じ挙動になりました。

home_controller.rb
class HomeController < ApplicationController
  def index
    render
  end
end

感想

たくさんのファイルが継承されていて、覚えられなくて大変でした。
これで安心してrenderを省略でき、ぐっすり眠れそうです。

ご指摘や「ここ間違ってるよ!」、感想など募集中なので気軽にコメントください。

環境

Rails 5.2.4
ruby 2.6.5

参考URL

こちらの記事でもっと詳しく追っていたので、さらに興味がある方はこちらも見てみてください。
Railsはどのようにテンプレートを見つけているか

6
2
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
6
2