はじめに
この記事はエムスリーキャリア Advent Calendar 2019 18日目の記事です。
エニジニアリンググループのおどです。
新卒入社した会社を8月に辞め、エムスリーキャリアに転職しました。
転職を機にLaravelエンジニアからRailsエンジニアに転身。
今回は利用するフレームワークがLaravelからRuby on Railsに変わり、つまずいたところを軽く深堀してみようと思います。
何に詰まったのか
入社して最初、簡単な文言修正のタスクからアサインされました。
「お、viewファイル見て文字直すだけとか簡単じゃん」
Laravelで開発をしてたときのようにroutingファイルを見て、controllerにたどり着きました。
class HomeController < ApplicationController
def index
end
end
どのファイルをレンダリングしているんだ…
気になって眠れない日々が始まりました。
Laravelはわかりやすかったな…
<?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というメソッドが呼ばれます。
# 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
)
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メソッドにたどり着きます。
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なので、引数だけを省略しても同じ挙動になりました。
class HomeController < ApplicationController
def index
render
end
end
感想
たくさんのファイルが継承されていて、覚えられなくて大変でした。
これで安心してrenderを省略でき、ぐっすり眠れそうです。
ご指摘や「ここ間違ってるよ!」、感想など募集中なので気軽にコメントください。
環境
Rails 5.2.4
ruby 2.6.5
参考URL
こちらの記事でもっと詳しく追っていたので、さらに興味がある方はこちらも見てみてください。
Railsはどのようにテンプレートを見つけているか