未验证 提交 973b62dc 编写于 作者: A Aaron Patterson 提交者: GitHub

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

Move compiled ERB to an AV::Base subclass
...@@ -15,6 +15,10 @@ def initialize(assigns) ...@@ -15,6 +15,10 @@ def initialize(assigns)
super(renderer, assigns) super(renderer, assigns)
end end
def compiled_method_container
self.class
end
def debug_params(params) def debug_params(params)
clean_params = params.clone clean_params = params.clone
clean_params.delete("action") clean_params.delete("action")
......
* 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. * Ensure unique DOM IDs for collection inputs with float values.
Fixes #34974 Fixes #34974
......
...@@ -35,7 +35,6 @@ module ActionView ...@@ -35,7 +35,6 @@ module ActionView
eager_autoload do eager_autoload do
autoload :Base autoload :Base
autoload :Context autoload :Context
autoload :CompiledTemplates, "action_view/context"
autoload :Digestor autoload :Digestor
autoload :Helpers autoload :Helpers
autoload :LookupContext autoload :LookupContext
......
...@@ -11,10 +11,6 @@ ...@@ -11,10 +11,6 @@
require "action_view/lookup_context" require "action_view/lookup_context"
module ActionView #:nodoc: module ActionView #:nodoc:
module CompiledTemplates #:nodoc:
# holds compiled template code
end
# = Action View Base # = Action View Base
# #
# Action View templates can be written in several ways. # Action View templates can be written in several ways.
...@@ -146,8 +142,6 @@ module CompiledTemplates #:nodoc: ...@@ -146,8 +142,6 @@ module CompiledTemplates #:nodoc:
class Base class Base
include Helpers, ::ERB::Util, Context include Helpers, ::ERB::Util, Context
include CompiledTemplates
# Specify the proc used to decorate input tags that refer to attributes with errors. # 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 } 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 @@ def cache_template_loading=(value) ...@@ -186,6 +180,20 @@ def cache_template_loading=(value)
def xss_safe? #:nodoc: def xss_safe? #:nodoc:
true true
end 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 end
attr_reader :view_renderer attr_reader :view_renderer
...@@ -260,7 +268,11 @@ def run(method, locals, buffer, &block) ...@@ -260,7 +268,11 @@ def run(method, locals, buffer, &block)
end end
def compiled_method_container 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 end
ActiveSupport.run_load_hooks(:action_view, self) ActiveSupport.run_load_hooks(:action_view, self)
......
...@@ -68,12 +68,17 @@ def self.get(details) ...@@ -68,12 +68,17 @@ def self.get(details)
end end
def self.clear def self.clear
@view_context_class = nil
@details_keys.clear @details_keys.clear
end end
def self.digest_caches def self.digest_caches
@details_keys.values @details_keys.values
end end
def self.view_context_class(klass)
@view_context_class ||= klass.with_empty_template_cache
end
end end
# Add caching behavior on top of Details. # Add caching behavior on top of Details.
......
...@@ -6,11 +6,13 @@ ...@@ -6,11 +6,13 @@
module ActionView module ActionView
# = Action View Railtie # = Action View Railtie
class Railtie < Rails::Engine # :nodoc: class Railtie < Rails::Engine # :nodoc:
NULL_OPTION = Object.new
config.action_view = ActiveSupport::OrderedOptions.new config.action_view = ActiveSupport::OrderedOptions.new
config.action_view.embed_authenticity_token_in_remote_forms = nil config.action_view.embed_authenticity_token_in_remote_forms = nil
config.action_view.debug_missing_translation = true config.action_view.debug_missing_translation = true
config.action_view.default_enforce_utf8 = nil 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 config.eager_load_namespaces << ActionView
...@@ -48,8 +50,11 @@ class Railtie < Rails::Engine # :nodoc: ...@@ -48,8 +50,11 @@ class Railtie < Rails::Engine # :nodoc:
initializer "action_view.finalize_compiled_template_methods" do |app| initializer "action_view.finalize_compiled_template_methods" do |app|
ActiveSupport.on_load(:action_view) do ActiveSupport.on_load(:action_view) do
ActionView::Template.finalize_compiled_template_methods = option = app.config.action_view.delete(:finalize_compiled_template_methods)
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
end end
......
...@@ -35,24 +35,36 @@ def process(*) #:nodoc: ...@@ -35,24 +35,36 @@ def process(*) #:nodoc:
end end
module ClassMethods module ClassMethods
def view_context_class def _routes
@view_context_class ||= begin end
supports_path = supports_path?
routes = respond_to?(:_routes) && _routes def _helpers
helpers = respond_to?(:_helpers) && _helpers end
Class.new(ActionView::Base) do def build_view_context_class(klass, supports_path, routes, helpers)
if routes Class.new(klass) do
include routes.url_helpers(supports_path) if routes
include routes.mounted_helpers include routes.url_helpers(supports_path)
end include routes.mounted_helpers
end
if helpers
include helpers if helpers
end include helpers
end end
end 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 end
def view_context_class def view_context_class
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
require "active_support/core_ext/object/try" require "active_support/core_ext/object/try"
require "active_support/core_ext/kernel/singleton_class" require "active_support/core_ext/kernel/singleton_class"
require "active_support/deprecation"
require "thread" require "thread"
require "delegate" require "delegate"
...@@ -10,7 +11,13 @@ module ActionView ...@@ -10,7 +11,13 @@ module ActionView
class Template class Template
extend ActiveSupport::Autoload 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 # === Encodings in ActionView::Template
# #
...@@ -118,16 +125,6 @@ class Template ...@@ -118,16 +125,6 @@ class Template
attr_reader :source, :identifier, :handler, :original_encoding, :updated_at 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 attr_reader :variable
def initialize(source, identifier, handler, details) def initialize(source, identifier, handler, details)
...@@ -337,9 +334,6 @@ def #{method_name}(local_assigns, output_buffer) ...@@ -337,9 +334,6 @@ def #{method_name}(local_assigns, output_buffer)
end end
mod.module_eval(source, identifier, 0) mod.module_eval(source, identifier, 0)
if finalize_compiled_template_methods
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
end
end end
def handle_render_error(view, e) def handle_render_error(view, e)
......
...@@ -48,7 +48,8 @@ def view ...@@ -48,7 +48,8 @@ def view
@view ||= begin @view ||= begin
path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH) path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::PathSet.new([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
end end
...@@ -61,7 +62,8 @@ def render_erb(string) ...@@ -61,7 +62,8 @@ def render_erb(string)
ActionView::Template.handler_for_extension(:erb), 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
end end
......
...@@ -10,8 +10,10 @@ class MultifetchCacheTest < ActiveRecordTestCase ...@@ -10,8 +10,10 @@ class MultifetchCacheTest < ActiveRecordTestCase
def setup def setup
view_paths = ActionController::Base.view_paths 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 def view_cache_dependencies
[] []
end end
......
...@@ -3,7 +3,18 @@ ...@@ -3,7 +3,18 @@
require "abstract_unit" require "abstract_unit"
class CompiledTemplatesTest < ActiveSupport::TestCase 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 ActionView::LookupContext::DetailsKey.clear
end end
...@@ -72,13 +83,13 @@ def render(*args) ...@@ -72,13 +83,13 @@ def render(*args)
def render_with_cache(*args) def render_with_cache(*args)
view_paths = ActionController::Base.view_paths 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 end
def render_without_cache(*args) def render_without_cache(*args)
path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH) path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::PathSet.new([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 end
def modify_template(template, content) def modify_template(template, content)
......
...@@ -11,10 +11,13 @@ class AVLogSubscriberTest < ActiveSupport::TestCase ...@@ -11,10 +11,13 @@ class AVLogSubscriberTest < ActiveSupport::TestCase
def setup def setup
super 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"]) lookup_context = ActionView::LookupContext.new(view_paths, {}, ["test"])
renderer = ActionView::Renderer.new(lookup_context) 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 ActionView::LogSubscriber.attach_to :action_view
......
...@@ -9,7 +9,8 @@ class TestController < ActionController::Base ...@@ -9,7 +9,8 @@ class TestController < ActionController::Base
module RenderTestCases module RenderTestCases
def setup_view(paths) def setup_view(paths)
@assigns = { secret: "in the sauce" } @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 view_cache_dependencies; []; end
def combined_fragment_cache_key(key) def combined_fragment_cache_key(key)
...@@ -17,7 +18,9 @@ def combined_fragment_cache_key(key) ...@@ -17,7 +18,9 @@ def combined_fragment_cache_key(key)
end end
end.with_view_paths(paths, @assigns) 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 # Reload and register danish language for testing
I18n.backend.store_translations "da", {} I18n.backend.store_translations "da", {}
...@@ -629,6 +632,8 @@ class CachedViewRenderTest < ActiveSupport::TestCase ...@@ -629,6 +632,8 @@ class CachedViewRenderTest < ActiveSupport::TestCase
# Ensure view path cache is primed # Ensure view path cache is primed
def setup def setup
view_paths = ActionController::Base.view_paths 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 assert_equal ActionView::OptimizedFileSystemResolver, view_paths.first.class
setup_view(view_paths) setup_view(view_paths)
end end
...@@ -645,6 +650,9 @@ class LazyViewRenderTest < ActiveSupport::TestCase ...@@ -645,6 +650,9 @@ class LazyViewRenderTest < ActiveSupport::TestCase
# Test the same thing as above, but make sure the view path # Test the same thing as above, but make sure the view path
# is not eager loaded # is not eager loaded
def setup 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) path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH)
view_paths = ActionView::PathSet.new([path]) view_paths = ActionView::PathSet.new([path])
assert_equal ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH), view_paths.first assert_equal ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH), view_paths.first
...@@ -704,6 +712,8 @@ class CachedCustomer < Customer; end ...@@ -704,6 +712,8 @@ class CachedCustomer < Customer; end
setup do setup do
view_paths = ActionController::Base.view_paths view_paths = ActionController::Base.view_paths
assert_equal ActionView::OptimizedFileSystemResolver, view_paths.first.class 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 ActionView::PartialRenderer.collection_cache = ActiveSupport::Cache::MemoryStore.new
......
...@@ -8,8 +8,11 @@ class TestController < ActionController::Base ...@@ -8,8 +8,11 @@ class TestController < ActionController::Base
class SetupFiberedBase < ActiveSupport::TestCase class SetupFiberedBase < ActiveSupport::TestCase
def setup def setup
view_paths = ActionController::Base.view_paths view_paths = ActionController::Base.view_paths
view_paths.each(&:clear_cache)
ActionView::LookupContext.fallbacks.each(&:clear_cache)
@assigns = { secret: "in the sauce", name: nil } @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 @controller_view = TestController.new.view_context
end end
......
...@@ -19,7 +19,8 @@ def find_template(*args) ...@@ -19,7 +19,8 @@ def find_template(*args)
end end
class Context < ActionView::Base class Context < ActionView::Base
def initialize def initialize(*)
super
@output_buffer = "original" @output_buffer = "original"
@virtual_path = nil @virtual_path = nil
end end
...@@ -63,7 +64,8 @@ def render(locals = {}) ...@@ -63,7 +64,8 @@ def render(locals = {})
end end
def setup def setup
@context = Context.new @context = Context.with_empty_template_cache.empty
super
end end
def test_basic_template def test_basic_template
......
...@@ -36,7 +36,10 @@ class TranslationHelperTest < ActiveSupport::TestCase ...@@ -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 end
teardown do teardown do
......
...@@ -590,13 +590,6 @@ Defaults to `'signed cookie'`. ...@@ -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.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 ### Configuring Action Mailbox
......
...@@ -48,7 +48,4 @@ Rails.application.configure do ...@@ -48,7 +48,4 @@ Rails.application.configure do
# Raises error for missing translations. # Raises error for missing translations.
# config.action_view.raise_on_missing_translations = true # 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 end
...@@ -2094,7 +2094,9 @@ class ::DummySerializer < ActiveJob::Serializers::ObjectSerializer; end ...@@ -2094,7 +2094,9 @@ class ::DummySerializer < ActiveJob::Serializers::ObjectSerializer; end
test "ActionView::Template.finalize_compiled_template_methods is true by default" do test "ActionView::Template.finalize_compiled_template_methods is true by default" do
app "test" app "test"
assert_equal true, ActionView::Template.finalize_compiled_template_methods assert_deprecated do
ActionView::Template.finalize_compiled_template_methods
end
end end
test "ActionView::Template.finalize_compiled_template_methods can be configured via config.action_view.finalize_compiled_template_methods" do test "ActionView::Template.finalize_compiled_template_methods can be configured via config.action_view.finalize_compiled_template_methods" do
...@@ -2106,7 +2108,9 @@ class ::DummySerializer < ActiveJob::Serializers::ObjectSerializer; end ...@@ -2106,7 +2108,9 @@ class ::DummySerializer < ActiveJob::Serializers::ObjectSerializer; end
app "test" app "test"
assert_equal false, ActionView::Template.finalize_compiled_template_methods assert_deprecated do
ActionView::Template.finalize_compiled_template_methods
end
end end
test "ActiveJob::Base.return_false_on_aborted_enqueue is true by default" do test "ActiveJob::Base.return_false_on_aborted_enqueue is true by default" do
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册