Railsのテンプレートレンダリングを分解調査する#2 ActionView編(翻訳)

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: Disassembling Rails — Template Rendering (2) – Ruby Inside – Medium 原文公開日: 2018/12/08 著者: Stan Lo — Goby言語の作者でありRails開発者です。 Railsのテンプレートレンダリングを分解調査する#2 ActionView編(翻訳) 本記事は、Railsのテンプレートレンダリングを分解調査する#1探索編の続きです(半年も前の記事で失礼!)。今回は、RailsがRubyオブジェクトからテンプレートをレンダリングする過程を解説します。 今回チェックするファイル actionview/lib/action_view/renderer/template_renderer.rb actionview/lib/action_view/template.rb ここまでのおさらい 前回の記事では、Railsがテンプレートファイルを読み取り、それを用いてテンプレートオブジェクトを初期化するところまでをご紹介しました。 # actionview/lib/action_view/template/resolver.rb def query(path, details, formats, outside_app_allowed) query = build_query(path, details) template_paths = find_template_paths(query) …… template_paths.map do |template| …… contents = File.binread(template) Template.new(contents, File.expand_path(template), handler, virtual_path: path.virtual, format: format, variant: variant, updated_at: mtime(template) ) end end Railsはテンプレートオブジェクトをどう使っているか ActionView::TemplateRendererがテンプレートを見つけると、#render_templateを呼び出し、テンプレートを引数として渡します。このときにlayoutやlocalsも同時に渡され、オプションから展開される点にご注意ください。 # actionview/lib/action_view/renderer/template_renderer.rb module ActionView class TemplateRenderer < AbstractRenderer #:nodoc: def render(context, options) …… # Found your template template = determine_template(options) …… render_template(template, options[:layout], options[:locals]) end end end そして以下が#render_templateです。 def render_template(template, layout_name = nil, locals = nil) ….. render_with_layout(layout_name, locals) do |layout| # instrumenting block template.render(view, locals) { |*name| view._layout_for(*name) } # end end end 実際のレンダリングがrender_with_layoutブロックでラップされていることがわかりますね。今回はこの部分をスキップしますが、また別のテンプレートレンダリング記事でご説明したいと思います。 Template#render Template#renderでは2つのステップを実行します。テンプレートのコンテンツをメソッド(もちろんRubyのメソッドです)にコンパイルする作業と、そのメソッドを呼び出す作業です。これだけシンプルなら十分理解できますね。 def render(view, locals, buffer = nil, &block) …… compile!(view) view.send(method_name, locals, buffer, &block) end テンプレートの「コンパイル」とは ここで言うテンプレートのコンテンツのコンパイルとは、RailsがActionView::Base(より正確にはActionView::CompiledTemplates)にメソッドを1つ定義することを指します。そのメソッドはlocalsを受け取って文字列を1つ返します。ここで例を用いて解説します。localを1つ受け取るsay_hiというテンプレートをレンダリングしたいとしましょう。 # say_hi.erbHi <%= name %> テンプレートがコンパイルされてメソッドになると、以下のようにRailsで使えるようになります。 def say_hi(local_assigns) name = local_assigns[:name] “Hi #{name}” end view.say_hi(name: “Stan”) #=> Hi Stan ここの仕組みはどうなっているのでしょうか?Railsはこれを実現するために、次のようにRubyのメタプログラミングサポートをフル活用しています。 require “erb” # ViewはActionView::Base的に振る舞うクラス class View end class Template def initialize(name, content) @name = name @content = content end def render(view) compile(view) … Continue reading Railsのテンプレートレンダリングを分解調査する#2 ActionView編(翻訳)