Merge pull request #35036 from rails/av-base-subclass

Move compiled ERB to an AV::Base subclass
This commit is contained in:
Aaron Patterson 2019-02-06 18:08:36 -08:00 committed by GitHub
commit 973b62dcdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 135 additions and 64 deletions

View File

@ -15,6 +15,10 @@ module ActionDispatch
super(renderer, assigns)
end
def compiled_method_container
self.class
end
def debug_params(params)
clean_params = params.clone
clean_params.delete("action")

View File

@ -1,3 +1,13 @@
* ActionView::Template.finalize_compiled_template_methods is deprecated with
no replacement.
*tenderlove*
* config.action_view.finalize_compiled_template_methods is deprecated with
no replacement.
*tenderlove*
* Ensure unique DOM IDs for collection inputs with float values.
Fixes #34974

View File

@ -35,7 +35,6 @@ module ActionView
eager_autoload do
autoload :Base
autoload :Context
autoload :CompiledTemplates, "action_view/context"
autoload :Digestor
autoload :Helpers
autoload :LookupContext

View File

@ -11,10 +11,6 @@ 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.
@ -146,8 +142,6 @@ 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| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
@ -186,6 +180,20 @@ module ActionView #:nodoc:
def xss_safe? #:nodoc:
true
end
def with_empty_template_cache # :nodoc:
subclass = Class.new(self) {
# We can't implement these as self.class because subclasses will
# share the same template cache as superclasses, so "changed?" won't work
# correctly.
define_method(:compiled_method_container) { subclass }
define_singleton_method(:compiled_method_container) { subclass }
}
end
def changed?(other) # :nodoc:
compiled_method_container != other.compiled_method_container
end
end
attr_reader :view_renderer
@ -260,7 +268,11 @@ module ActionView #:nodoc:
end
def compiled_method_container
CompiledTemplates
raise NotImplementedError, <<~msg
Subclasses of ActionView::Base must implement `compiled_method_container`
or use the class method `with_empty_template_cache` for constructing
an ActionView::Base subclass that has an empty cache.
msg
end
ActiveSupport.run_load_hooks(:action_view, self)

View File

@ -68,12 +68,17 @@ module ActionView
end
def self.clear
@view_context_class = nil
@details_keys.clear
end
def self.digest_caches
@details_keys.values
end
def self.view_context_class(klass)
@view_context_class ||= klass.with_empty_template_cache
end
end
# Add caching behavior on top of Details.

View File

@ -6,11 +6,13 @@ require "rails"
module ActionView
# = Action View Railtie
class Railtie < Rails::Engine # :nodoc:
NULL_OPTION = Object.new
config.action_view = ActiveSupport::OrderedOptions.new
config.action_view.embed_authenticity_token_in_remote_forms = nil
config.action_view.debug_missing_translation = true
config.action_view.default_enforce_utf8 = nil
config.action_view.finalize_compiled_template_methods = true
config.action_view.finalize_compiled_template_methods = NULL_OPTION
config.eager_load_namespaces << ActionView
@ -48,8 +50,11 @@ module ActionView
initializer "action_view.finalize_compiled_template_methods" do |app|
ActiveSupport.on_load(:action_view) do
ActionView::Template.finalize_compiled_template_methods =
app.config.action_view.delete(:finalize_compiled_template_methods)
option = app.config.action_view.delete(:finalize_compiled_template_methods)
if option != NULL_OPTION
ActiveSupport::Deprecation.warn "action_view.finalize_compiled_template_methods is deprecated and has no effect"
end
end
end

View File

@ -35,24 +35,36 @@ module ActionView
end
module ClassMethods
def view_context_class
@view_context_class ||= begin
supports_path = supports_path?
routes = respond_to?(:_routes) && _routes
helpers = respond_to?(:_helpers) && _helpers
def _routes
end
Class.new(ActionView::Base) do
if routes
include routes.url_helpers(supports_path)
include routes.mounted_helpers
end
def _helpers
end
if helpers
include helpers
end
def build_view_context_class(klass, supports_path, routes, helpers)
Class.new(klass) do
if routes
include routes.url_helpers(supports_path)
include routes.mounted_helpers
end
if helpers
include helpers
end
end
end
def view_context_class
klass = ActionView::LookupContext::DetailsKey.view_context_class(ActionView::Base)
@view_context_class ||= build_view_context_class(klass, supports_path?, _routes, _helpers)
if klass.changed?(@view_context_class)
@view_context_class = build_view_context_class(klass, supports_path?, _routes, _helpers)
end
@view_context_class
end
end
def view_context_class

View File

@ -2,6 +2,7 @@
require "active_support/core_ext/object/try"
require "active_support/core_ext/kernel/singleton_class"
require "active_support/deprecation"
require "thread"
require "delegate"
@ -10,7 +11,13 @@ module ActionView
class Template
extend ActiveSupport::Autoload
mattr_accessor :finalize_compiled_template_methods, default: true
def self.finalize_compiled_template_methods
ActiveSupport::Deprecation.warn "ActionView::Template.finalize_compiled_template_methods is deprecated and has no effect"
end
def self.finalize_compiled_template_methods=(_)
ActiveSupport::Deprecation.warn "ActionView::Template.finalize_compiled_template_methods= is deprecated and has no effect"
end
# === Encodings in ActionView::Template
#
@ -118,16 +125,6 @@ module ActionView
attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
# This finalizer is needed (and exactly with a proc inside another proc)
# otherwise templates leak in development.
Finalizer = proc do |method_name, mod| # :nodoc:
proc do
mod.module_eval do
remove_possible_method method_name
end
end
end
attr_reader :variable
def initialize(source, identifier, handler, details)
@ -337,9 +334,6 @@ module ActionView
end
mod.module_eval(source, identifier, 0)
if finalize_compiled_template_methods
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
end
end
def handle_render_error(view, e)

View File

@ -48,7 +48,8 @@ module RenderERBUtils
@view ||= begin
path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::PathSet.new([path])
ActionView::Base.with_view_paths(view_paths)
view = ActionView::Base.with_empty_template_cache
view.with_view_paths(view_paths)
end
end
@ -61,7 +62,8 @@ module RenderERBUtils
ActionView::Template.handler_for_extension(:erb),
{})
template.render(ActionView::Base.empty, {}).strip
view = ActionView::Base.with_empty_template_cache
template.render(view.empty, {}).strip
end
end

View File

@ -10,8 +10,10 @@ class MultifetchCacheTest < ActiveRecordTestCase
def setup
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
@view = Class.new(ActionView::Base) do
@view = Class.new(ActionView::Base.with_empty_template_cache) do
def view_cache_dependencies
[]
end

View File

@ -3,7 +3,18 @@
require "abstract_unit"
class CompiledTemplatesTest < ActiveSupport::TestCase
teardown do
attr_reader :view_class
def setup
super
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
@view_class = ActionView::Base.with_empty_template_cache
end
def teardown
super
ActionView::LookupContext::DetailsKey.clear
end
@ -72,13 +83,13 @@ class CompiledTemplatesTest < ActiveSupport::TestCase
def render_with_cache(*args)
view_paths = ActionController::Base.view_paths
ActionView::Base.with_view_paths(view_paths, {}).render(*args)
view_class.with_view_paths(view_paths, {}).render(*args)
end
def render_without_cache(*args)
path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::PathSet.new([path])
ActionView::Base.with_view_paths(view_paths, {}).render(*args)
view_class.with_view_paths(view_paths, {}).render(*args)
end
def modify_template(template, content)

View File

@ -11,10 +11,13 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
def setup
super
view_paths = ActionController::Base.view_paths
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
lookup_context = ActionView::LookupContext.new(view_paths, {}, ["test"])
renderer = ActionView::Renderer.new(lookup_context)
@view = ActionView::Base.new(renderer, {})
@view = ActionView::Base.with_empty_template_cache.new(renderer, {})
ActionView::LogSubscriber.attach_to :action_view

View File

@ -9,7 +9,8 @@ end
module RenderTestCases
def setup_view(paths)
@assigns = { secret: "in the sauce" }
@view = Class.new(ActionView::Base) do
@view = Class.new(ActionView::Base.with_empty_template_cache) do
def view_cache_dependencies; []; end
def combined_fragment_cache_key(key)
@ -17,7 +18,9 @@ module RenderTestCases
end
end.with_view_paths(paths, @assigns)
@controller_view = TestController.new.view_context
controller = TestController.new
@controller_view = controller.view_context_class.with_empty_template_cache.new(controller.view_renderer, controller.view_assigns, controller)
# Reload and register danish language for testing
I18n.backend.store_translations "da", {}
@ -629,6 +632,8 @@ class CachedViewRenderTest < ActiveSupport::TestCase
# Ensure view path cache is primed
def setup
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
assert_equal ActionView::OptimizedFileSystemResolver, view_paths.first.class
setup_view(view_paths)
end
@ -645,6 +650,9 @@ class LazyViewRenderTest < ActiveSupport::TestCase
# Test the same thing as above, but make sure the view path
# is not eager loaded
def setup
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::PathSet.new([path])
assert_equal ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH), view_paths.first
@ -704,6 +712,8 @@ class CachedCollectionViewRenderTest < ActiveSupport::TestCase
setup do
view_paths = ActionController::Base.view_paths
assert_equal ActionView::OptimizedFileSystemResolver, view_paths.first.class
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
ActionView::PartialRenderer.collection_cache = ActiveSupport::Cache::MemoryStore.new

View File

@ -8,8 +8,11 @@ end
class SetupFiberedBase < ActiveSupport::TestCase
def setup
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
@assigns = { secret: "in the sauce", name: nil }
@view = ActionView::Base.with_view_paths(view_paths, @assigns)
@view = ActionView::Base.with_empty_template_cache.with_view_paths(view_paths, @assigns)
@controller_view = TestController.new.view_context
end

View File

@ -19,7 +19,8 @@ class TestERBTemplate < ActiveSupport::TestCase
end
class Context < ActionView::Base
def initialize
def initialize(*)
super
@output_buffer = "original"
@virtual_path = nil
end
@ -63,7 +64,8 @@ class TestERBTemplate < ActiveSupport::TestCase
end
def setup
@context = Context.new
@context = Context.with_empty_template_cache.empty
super
end
def test_basic_template

View File

@ -36,7 +36,10 @@ class TranslationHelperTest < ActiveSupport::TestCase
}
}
)
@view = ::ActionView::Base.with_view_paths(ActionController::Base.view_paths, {})
view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
@view = ::ActionView::Base.with_empty_template_cache.with_view_paths(view_paths, {})
end
teardown do

View File

@ -590,13 +590,6 @@ Defaults to `'signed cookie'`.
* `config.action_view.default_enforce_utf8` determines whether forms are generated with a hidden tag that forces older versions of Internet Explorer to submit forms encoded in UTF-8. This defaults to `false`.
* `config.action_view.finalize_compiled_template_methods` determines
whether the methods on `ActionView::CompiledTemplates` that templates
compile themselves to are removed when template instances are
destroyed by the garbage collector. This helps prevent memory leaks in
development mode, but for large test suites, disabling this option in
the test environment can improve performance. This defaults to `true`.
### Configuring Action Mailbox

View File

@ -48,7 +48,4 @@ Rails.application.configure do
# Raises error for missing translations.
# config.action_view.raise_on_missing_translations = true
# Prevent expensive template finalization at end of test suite runs.
config.action_view.finalize_compiled_template_methods = false
end

View File

@ -2094,7 +2094,9 @@ module ApplicationTests
test "ActionView::Template.finalize_compiled_template_methods is true by default" do
app "test"
assert_equal true, ActionView::Template.finalize_compiled_template_methods
assert_deprecated do
ActionView::Template.finalize_compiled_template_methods
end
end
test "ActionView::Template.finalize_compiled_template_methods can be configured via config.action_view.finalize_compiled_template_methods" do
@ -2106,7 +2108,9 @@ module ApplicationTests
app "test"
assert_equal false, ActionView::Template.finalize_compiled_template_methods
assert_deprecated do
ActionView::Template.finalize_compiled_template_methods
end
end
test "ActiveJob::Base.return_false_on_aborted_enqueue is true by default" do