From ccfa01c36e79013881ffdb7ebe397cec733d15b2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Jan 2019 15:36:55 -0800 Subject: Pull output buffer conditional up This pulls the "output buffer existence" conditional up. Instead of evaling the same conditional over and over, we can pull it in to "only compiled once" Ruby code. --- actionview/lib/action_view/template.rb | 2 +- actionview/lib/action_view/template/handlers/erb/erubi.rb | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index 070d82cf17..da9f20990f 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -155,7 +155,7 @@ module ActionView # This method is instrumented as "!render_template.action_view". Notice that # we use a bang in this instrumentation because you don't want to # consume this in production. This is only slow if it's being listened to. - def render(view, locals, buffer = nil, &block) + def render(view, locals, buffer = ActionView::OutputBuffer.new, &block) instrument_render_template do compile!(view) view.send(method_name, locals, buffer, &block) diff --git a/actionview/lib/action_view/template/handlers/erb/erubi.rb b/actionview/lib/action_view/template/handlers/erb/erubi.rb index db75f028ed..d379d7ec12 100644 --- a/actionview/lib/action_view/template/handlers/erb/erubi.rb +++ b/actionview/lib/action_view/template/handlers/erb/erubi.rb @@ -13,7 +13,7 @@ module ActionView # Dup properties so that we don't modify argument properties = Hash[properties] - properties[:preamble] = "@output_buffer = output_buffer || ActionView::OutputBuffer.new;" + properties[:preamble] = "@output_buffer = output_buffer;" properties[:postamble] = "@output_buffer.to_s" properties[:bufvar] = "@output_buffer" properties[:escapefunc] = "" @@ -23,6 +23,8 @@ module ActionView def evaluate(action_view_erb_handler_context) pr = eval("proc { #{@src} }", binding, @filename || "(erubi)") + # Double assignment to eliminate -w warnings + output_buffer = output_buffer = ActionView::OutputBuffer.new action_view_erb_handler_context.instance_eval(&pr) end -- cgit v1.2.3 From c740ebdaf580c8c9772a9099c7a9c11ef3105f3a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Jan 2019 17:28:03 -0800 Subject: Always evaluate views against an ActionView::Base Methods created by views should always be evaluated against an AV::Base instance. This way we can extract and refactor things in to classes. --- actionview/lib/action_view/base.rb | 7 +++++++ actionview/lib/action_view/template.rb | 6 +++--- actionview/lib/action_view/template/handlers/erb/erubi.rb | 10 ++++++---- actionview/test/abstract_unit.rb | 2 +- actionview/test/template/template_test.rb | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index d41fe2a608..4318791760 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -210,6 +210,13 @@ module ActionView #:nodoc: _prepare_context end + def run(method, locals, buffer, &block) + _old_output_buffer = @output_buffer + send(method, locals, buffer, &block) + ensure + @output_buffer = _old_output_buffer + end + ActiveSupport.run_load_hooks(:action_view, self) end end diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index da9f20990f..ed680df4c6 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -158,7 +158,7 @@ module ActionView def render(view, locals, buffer = ActionView::OutputBuffer.new, &block) instrument_render_template do compile!(view) - view.send(method_name, locals, buffer, &block) + view.run(method_name, locals, buffer, &block) end rescue => e handle_render_error(view, e) @@ -301,9 +301,9 @@ module ActionView # encoding of the code source = +<<-end_src def #{method_name}(local_assigns, output_buffer) - _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code} + _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};#{locals_code};#{code} ensure - @virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer + @virtual_path = _old_virtual_path end end_src diff --git a/actionview/lib/action_view/template/handlers/erb/erubi.rb b/actionview/lib/action_view/template/handlers/erb/erubi.rb index d379d7ec12..d0e87e6e7f 100644 --- a/actionview/lib/action_view/template/handlers/erb/erubi.rb +++ b/actionview/lib/action_view/template/handlers/erb/erubi.rb @@ -22,10 +22,12 @@ module ActionView end def evaluate(action_view_erb_handler_context) - pr = eval("proc { #{@src} }", binding, @filename || "(erubi)") - # Double assignment to eliminate -w warnings - output_buffer = output_buffer = ActionView::OutputBuffer.new - action_view_erb_handler_context.instance_eval(&pr) + src = @src + view = Class.new(ActionView::Base) { + include action_view_erb_handler_context._routes.url_helpers + class_eval("define_method(:_template) { |local_assigns, output_buffer| #{src} }", @filename || "(erubi)", 0) + }.new(action_view_erb_handler_context) + view.run(:_template, {}, ActionView::OutputBuffer.new) end private diff --git a/actionview/test/abstract_unit.rb b/actionview/test/abstract_unit.rb index f90626ad9e..d71944e938 100644 --- a/actionview/test/abstract_unit.rb +++ b/actionview/test/abstract_unit.rb @@ -61,7 +61,7 @@ module RenderERBUtils ActionView::Template::Handlers::ERB, {}) - template.render(self, {}).strip + template.render(ActionView::Base.new, {}).strip end end diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb index b348d1f17b..8257d97b7c 100644 --- a/actionview/test/template/template_test.rb +++ b/actionview/test/template/template_test.rb @@ -18,7 +18,7 @@ class TestERBTemplate < ActiveSupport::TestCase attr_accessor :formats end - class Context + class Context < ActionView::Base def initialize @output_buffer = "original" @virtual_path = nil -- cgit v1.2.3 From ec5c946138f63dc975341d6521587adc74f6b441 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Jan 2019 17:35:01 -0800 Subject: Pull buffer assignment up Since everything goes through a `run` method, we can pull the buffer assignment up. --- actionview/lib/action_view/base.rb | 1 + actionview/lib/action_view/template/handlers/erb/erubi.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index 4318791760..d86dd0b6c4 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -212,6 +212,7 @@ module ActionView #:nodoc: def run(method, locals, buffer, &block) _old_output_buffer = @output_buffer + @output_buffer = buffer send(method, locals, buffer, &block) ensure @output_buffer = _old_output_buffer diff --git a/actionview/lib/action_view/template/handlers/erb/erubi.rb b/actionview/lib/action_view/template/handlers/erb/erubi.rb index d0e87e6e7f..e155bae89d 100644 --- a/actionview/lib/action_view/template/handlers/erb/erubi.rb +++ b/actionview/lib/action_view/template/handlers/erb/erubi.rb @@ -13,7 +13,7 @@ module ActionView # Dup properties so that we don't modify argument properties = Hash[properties] - properties[:preamble] = "@output_buffer = output_buffer;" + properties[:preamble] = "" properties[:postamble] = "@output_buffer.to_s" properties[:bufvar] = "@output_buffer" properties[:escapefunc] = "" -- cgit v1.2.3 From 54095aa676692eb8919e3b5f935670812c14a840 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Jan 2019 17:39:42 -0800 Subject: Pull up virtual path assignment --- actionview/lib/action_view/base.rb | 4 ++-- actionview/lib/action_view/template.rb | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index d86dd0b6c4..f3a9b363ca 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -211,11 +211,11 @@ module ActionView #:nodoc: end def run(method, locals, buffer, &block) - _old_output_buffer = @output_buffer + _old_output_buffer, _old_virtual_path = @output_buffer, @virtual_path @output_buffer = buffer send(method, locals, buffer, &block) ensure - @output_buffer = _old_output_buffer + @output_buffer, @virtual_path = _old_output_buffer, _old_virtual_path end ActiveSupport.run_load_hooks(:action_view, self) diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index ed680df4c6..e86a49f222 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -301,9 +301,7 @@ module ActionView # encoding of the code source = +<<-end_src def #{method_name}(local_assigns, output_buffer) - _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};#{locals_code};#{code} - ensure - @virtual_path = _old_virtual_path + @virtual_path = #{@virtual_path.inspect};#{locals_code};#{code} end end_src -- cgit v1.2.3 From e1fe21f62454d72203f97f7328a74bb164781962 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 17 Jan 2019 10:41:25 -0800 Subject: Templates should be eval'd in the context of an AV::Base object --- .../controller/new_base/render_context_test.rb | 55 ---------------------- 1 file changed, 55 deletions(-) delete mode 100644 actionpack/test/controller/new_base/render_context_test.rb diff --git a/actionpack/test/controller/new_base/render_context_test.rb b/actionpack/test/controller/new_base/render_context_test.rb deleted file mode 100644 index 5e570a1d79..0000000000 --- a/actionpack/test/controller/new_base/render_context_test.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -require "abstract_unit" - -# This is testing the decoupling of view renderer and view context -# by allowing the controller to be used as view context. This is -# similar to the way sinatra renders templates. -module RenderContext - class BasicController < ActionController::Base - self.view_paths = [ActionView::FixtureResolver.new( - "render_context/basic/hello_world.html.erb" => "<%= @value %> from <%= self.__controller_method__ %>", - "layouts/basic.html.erb" => "?<%= yield %>?" - )] - - # 1) Include ActionView::Context to bring the required dependencies - include ActionView::Context - - # 2) Call _prepare_context that will do the required initialization - before_action :_prepare_context - - def hello_world - @value = "Hello" - render action: "hello_world", layout: false - end - - def with_layout - @value = "Hello" - render action: "hello_world", layout: "basic" - end - - protected def __controller_method__ - "controller context!" - end - - private - # 3) Set view_context to self - def view_context - self - end - end - - class RenderContextTest < Rack::TestCase - test "rendering using the controller as context" do - get "/render_context/basic/hello_world" - assert_body "Hello from controller context!" - assert_status 200 - end - - test "rendering using the controller as context with layout" do - get "/render_context/basic/with_layout" - assert_body "?Hello from controller context!?" - assert_status 200 - end - end -end -- cgit v1.2.3 From 63d96adb685ae3cf464327c59a090ede25c55414 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 17 Jan 2019 14:33:14 -0800 Subject: Remove args from `default_render` It's always called with 0 params, so just remove the parameter --- actionpack/lib/action_controller/metal/basic_implicit_render.rb | 2 +- actionpack/lib/action_controller/metal/implicit_render.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/metal/basic_implicit_render.rb b/actionpack/lib/action_controller/metal/basic_implicit_render.rb index 2dc990f303..f9a758ff0e 100644 --- a/actionpack/lib/action_controller/metal/basic_implicit_render.rb +++ b/actionpack/lib/action_controller/metal/basic_implicit_render.rb @@ -6,7 +6,7 @@ module ActionController super.tap { default_render unless performed? } end - def default_render(*args) + def default_render head :no_content end end diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb index d3bb58f48b..8365ddca57 100644 --- a/actionpack/lib/action_controller/metal/implicit_render.rb +++ b/actionpack/lib/action_controller/metal/implicit_render.rb @@ -30,9 +30,9 @@ module ActionController # :stopdoc: include BasicImplicitRender - def default_render(*args) + def default_render if template_exists?(action_name.to_s, _prefixes, variants: request.variant) - render(*args) + render elsif any_templates?(action_name.to_s, _prefixes) message = "#{self.class.name}\##{action_name} is missing a template " \ "for this request format and variant.\n" \ -- cgit v1.2.3 From 8611b15924c5deb083221026b93792e36400bb02 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 18 Jan 2019 15:07:22 -0800 Subject: Only cache the view_context_class in one place This patch removes the instance writer of view_context_class. Subclasses may override it, but it doesn't need to be written. This also eliminates the need to cache the return value of the class level `view_context_class` method. --- actionpack/lib/action_dispatch/testing/assertions/routing.rb | 9 ++++++++- actionview/lib/action_view/rendering.rb | 4 +--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index af41521c5c..28cde6704e 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -160,9 +160,16 @@ module ActionDispatch @controller.singleton_class.include(_routes.url_helpers) if @controller.respond_to? :view_context_class - @controller.view_context_class = Class.new(@controller.view_context_class) do + view_context_class = Class.new(@controller.view_context_class) do include _routes.url_helpers end + + custom_view_context = Module.new { + define_method(:view_context_class) do + view_context_class + end + } + @controller.extend(custom_view_context) end end yield @routes diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index cb4327cf16..205665a8c6 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -55,10 +55,8 @@ module ActionView end end - attr_internal_writer :view_context_class - def view_context_class - @_view_context_class ||= self.class.view_context_class + self.class.view_context_class end # An instance of a view class. The default view class is ActionView::Base. -- cgit v1.2.3 From 195f39804a7a4a0034f25e8704220e03d95a752a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 18 Jan 2019 15:46:05 -0800 Subject: Directly include "CompiledTemplates" module We always want to include this module. It'll be used in production (maybe) --- actionview/lib/action_view/base.rb | 6 ++++++ actionview/lib/action_view/context.rb | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index f3a9b363ca..384439c8fb 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -10,6 +10,10 @@ require "action_view/template" require "action_view/lookup_context" module ActionView #:nodoc: + module CompiledTemplates #:nodoc: + # holds compiled template code + end + # = Action View Base # # Action View templates can be written in several ways. @@ -141,6 +145,8 @@ module ActionView #:nodoc: class Base include Helpers, ::ERB::Util, Context + include CompiledTemplates + # Specify the proc used to decorate input tags that refer to attributes with errors. cattr_accessor :field_error_proc, default: Proc.new { |html_tag, instance| "
#{html_tag}
".html_safe } diff --git a/actionview/lib/action_view/context.rb b/actionview/lib/action_view/context.rb index 3c605c3ee3..2b22c30a3a 100644 --- a/actionview/lib/action_view/context.rb +++ b/actionview/lib/action_view/context.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true module ActionView - module CompiledTemplates #:nodoc: - # holds compiled template code - end - # = Action View Context # # Action View contexts are supplied to Action Controller to render a template. @@ -16,7 +12,6 @@ module ActionView # object that includes this module (although you can call _prepare_context # defined below). module Context - include CompiledTemplates attr_accessor :output_buffer, :view_flow # Prepares the context by setting the appropriate instance variables. -- cgit v1.2.3 From 0f081611e6746ebbf17ffc13e119b24c9ad7aa73 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 18 Jan 2019 15:54:37 -0800 Subject: Ask the view for its method container Rather than doing is_a? checks, ask the view object for its compiled method container. This gives us the power to replace the method container depending on the instance of the view. --- actionview/lib/action_view/base.rb | 4 ++++ actionview/lib/action_view/template.rb | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index 384439c8fb..6cb342a0a9 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -224,6 +224,10 @@ module ActionView #:nodoc: @output_buffer, @virtual_path = _old_output_buffer, _old_virtual_path end + def compiled_method_container + CompiledTemplates + end + ActiveSupport.run_load_hooks(:action_view, self) end end diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index e86a49f222..8ce8053011 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -264,11 +264,7 @@ module ActionView # re-compilation return if @compiled - if view.is_a?(ActionView::CompiledTemplates) - mod = ActionView::CompiledTemplates - else - mod = view.singleton_class - end + mod = view.compiled_method_container instrument("!compile_template") do compile(mod) -- cgit v1.2.3