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

概要 原著者の許諾を得て翻訳・公開いたします(パブリックドメイン)。 英語記事: Disassembling Rails — Template Rendering (1) – Stan Lo – Medium 原文公開日: 2017/07/04 著者: Stan Lo — Goby言語の作者でありRails開発者です。 Railsのテンプレートレンダリングを分解調査する#1探索編(翻訳) 今回は「Rails分解調査シリーズ」第2弾です。前回の記事「Railsのフラグメントキャッシュを分解調査する」をお読み頂いてない方でも今回の記事を読むのに差し支えはありませんが、それでもご一読をおすすめいたします。 Railsのテンプレートレンダリングの背後では実に多くの処理が行われているので、2回に分けて詳しくご紹介いたします。その第1回目として、表示したいテンプレートをRailsのrenderメソッドがどのように探索しているかを説明いたします。続く第2回では、テンプレートオブジェクトがレスポンスに使えるHTMLに変換される過程を説明します。それでは始めましょう! 注目すべきファイル ソースコードを自分でも見てみたい方向けに(私からも推奨します)、今回のテーマで注目すべきファイルをリストアップします。 訳注: Rails 5.2-stableのソースにリンクしました。 actionview/lib/action_view/helpers/rendering_helper.rb actionview/lib/action_view/renderer/renderer.rb actionview/lib/action_view/lookup_context.rb actionview/lib/action_view/renderer/template_renderer.rb actionview/lib/action_view/path_set.rb ユーザーインターフェイス Railsはさまざまな方法でテンプレートをレンダリングします。その1つが#renderメソッドを手動で実行することです。そこでまず以下から始めることにします。 render template: “comments/index”, formats: :json 薄々お気づきのように、このrenderメソッドはActionView::Helpers::RenderingHelperというヘルパーにあります。 # actionview/lib/action_view/helpers/rendering_helper.rb def render(options = {}, locals = {}, &block) case options when Hash if block_given? view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block) else view_renderer.render(self, options) end else view_renderer.render_partial(self, partial: options, locals: locals, &block) end end この#renderメソッドを見てみると、テンプレートのレンダリングを担当するview_rendererがあることがおよそ見て取れます。このview_rendererは実際にはActionView::Rendererオブジェクトなので、そちらの#renderメソッドを見てみましょう。 # actionview/lib/action_view/renderer/renderer.rb module ActionView class Renderer def render(context, options) if options.key?(:partial) render_partial(context, options) else render_template(context, options) end end def render_template(context, options) #:nodoc: TemplateRenderer.new(@lookup_context).render(context, options) end def render_partial(context, options, &block) PartialRenderer.new(@lookup_context).render(context, options, block) end end end こちらを見ると、2つの異なるクラスがそれぞれtemplateのレンダリングとpartialのレンダリングを担当していることがわかります。partialのレンダリングはもう少し複雑なので、今回はtemplateのレンダリングのみをご紹介します。 ここで注目すべきは、Rendererの@lookup_contextインスタンス変数です。探索するテンプレートに関する必要な情報はすべてここにあります。 #<ActionView::LookupContext:0x00007fa8d5c7f670 @cache=true, @details= {:locale=>[:en], :formats=> [:html, :text, :js, :css, …… ], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby]}, @details_key=nil, @prefixes=[], @rendered_format=nil, @view_paths= #<ActionView::PathSet:0x00007fa8d5c7eba8 @paths= [#<ActionView::FileSystemResolver:0x00007fa8d5c9c7c0 @cache=#<ActionView::Resolver::Cache:0x7fa8d5c9c680 keys=0 queries=0>, @path=”/Users/st0012/projects/rails/actionview/test/fixtures”, @pattern=”:prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}”>]>> ActionView::LookupContextについて 私見では、このActionView::LookupContextこそがテンプレートのレンダリングにおける最も重要度の高いコンポーネントです。このコンポーネントの属性、特に@detailsや@view_pathsを見てみましょう。 @detailsはハッシュで、locale、formats、variants、handlersが含まれています。@detailsの情報の使いみちは次の2とおりです。 1. 見つかったテンプレートのキャッシュに使われるキャッシュキーの一部となる(コード) # actionview/lib/action_view/template/resolver.rb def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = []) cached(key, [name, prefix, partial], details, locals) do find_templates(name, prefix, partial, details) end end 2. … Continue reading Railsのテンプレートレンダリングを分解調査する#1探索編(翻訳)