diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 1ab3de192c599..66315710785bc 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -107,5 +107,5 @@ def _deliver_all_later(delivery_method, *deliveries, **options) ActiveSupport.on_load(:action_view) do ActionView::Base.default_formats ||= Mime.symbols ActionView::Template.mime_types_implementation = Mime - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index e6f5d0819e924..7d332768ee14b 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -163,5 +163,5 @@ def self.eager_load! ActiveSupport.on_load(:action_view) do ActionView::Base.default_formats ||= Mime.symbols ActionView::Template.mime_types_implementation = Mime - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb index 5a9e8f1906e18..4ff1ababeddbe 100644 --- a/actionpack/test/controller/mime/respond_to_test.rb +++ b/actionpack/test/controller/mime/respond_to_test.rb @@ -336,7 +336,7 @@ def setup Mime::Type.register("text/x-mobile", :mobile) Mime::Type.register("application/fancy-xml", :fancy_xml) Mime::Type.register("text/html; fragment", :html_fragment) - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end def teardown @@ -345,7 +345,7 @@ def teardown Mime::Type.unregister(:mobile) Mime::Type.unregister(:fancy_xml) Mime::Type.unregister(:html_fragment) - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end def test_html_fragment diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 7b37dd5d4ccbb..adffe8ef65fef 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -354,11 +354,11 @@ def modify_template(name) key = name + ".erb" original = hash[key] hash[key] = "#{original} Modified!" - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear yield ensure hash[key] = original - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end end diff --git a/actionview/lib/action_view/cache_expiry.rb b/actionview/lib/action_view/cache_expiry.rb index 488c6768ccf0e..4bdf9a0bd42ac 100644 --- a/actionview/lib/action_view/cache_expiry.rb +++ b/actionview/lib/action_view/cache_expiry.rb @@ -52,7 +52,7 @@ def deactivate private def reload! - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end def build_watcher diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb index 0d906cb9c4431..21349637416fe 100644 --- a/actionview/lib/action_view/digestor.rb +++ b/actionview/lib/action_view/digestor.rb @@ -1,12 +1,26 @@ # frozen_string_literal: true +require "concurrent/map" require "action_view/dependency_tracker" module ActionView class Digestor @@digest_mutex = Mutex.new + @cache = Concurrent::Map.new class << self + def cache(details_key) + @cache[details_key] ||= Concurrent::Map.new + end + + def digest_caches + @cache.values + end + + def clear_cache + @cache.clear + end + # Supported options: # # * name - Template name diff --git a/actionview/lib/action_view/lookup_context.rb b/actionview/lib/action_view/lookup_context.rb index 08ae118f67b76..e52d3baea2186 100644 --- a/actionview/lib/action_view/lookup_context.rb +++ b/actionview/lib/action_view/lookup_context.rb @@ -15,78 +15,151 @@ module ActionView class LookupContext # :nodoc: attr_accessor :prefixes - singleton_class.attr_accessor :default_procs - self.default_procs = {}.freeze - - def self.registered_details - self.default_procs.keys + def self.clear + ActionView::PathRegistry.all_resolvers.each(&:clear_cache) + reset_view_context_class + Digestor.clear_cache end - def self.register_detail(name, &block) - self.default_procs = self.default_procs.merge(name => block).freeze + class Details + def initialize(locale: nil, formats: nil, variants: nil, handlers: nil) + @locale = locale && Array(locale) + @formats = formats && Array(formats) + @variants = variants == :any ? :any : variants && Array(variants) + @handlers = handlers && Array(handlers) + end - Accessors.define_method(:"default_#{name}", &block) - Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1 - def #{name} - @details[:#{name}] || [] - end + attr_reader :html_fallback_for_js + + def locale + @locale ||= default_locale + end + + def locale=(value) + value = value.present? ? Array(value) : default_locale + return if value == @locale + @locale = value + @digest_cache = nil + end + + def formats + @formats ||= default_formats + end + + def formats=(values) + if values + values = values.dup + values.concat(default_formats) if values.delete "*/*" + values.uniq! - def #{name}=(value) - value = value.present? ? Array(value) : default_#{name} - _set_detail(:#{name}, value) if value != @details[:#{name}] + Template.validate_formats(values) + + if (values.length == 1) && (values[0] == :js) + values << :html + @html_fallback_for_js = true + end end - METHOD - end - # Holds accessors for the registered details. - module Accessors # :nodoc: - end + values = values.presence || default_formats + return if values == @formats + @formats = values + @digest_cache = nil + end - register_detail(:locale) do - locales = [I18n.locale] - locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks - locales << I18n.default_locale - locales.uniq! - locales - end - register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] } - register_detail(:variants) { [] } - register_detail(:handlers) { Template::Handlers.extensions } + def variants + @variants ||= default_variants + end - class DetailsKey # :nodoc: - alias :eql? :equal? + def variants=(value) + value = value.present? ? Array(value) : default_variants + return if value == @variants + @variants = value + @digest_cache = nil + end - @details_keys = Concurrent::Map.new - @digest_cache = Concurrent::Map.new + def handlers + @handlers ||= default_handlers + end - def self.digest_cache(details) - @digest_cache[details_cache_key(details)] ||= Concurrent::Map.new + def handlers=(value) + value = value.present? ? Array(value) : default_handlers + return if value == @handlers + @handlers = value + @digest_cache = nil end - def self.details_cache_key(details) - @details_keys.fetch(details) do - if formats = details[:formats] - if normalized = Template.normalized_formats(formats) - details = details.dup - details[:formats] = normalized - end - end - @details_keys[details] ||= TemplateDetails::Requested.new(**details) - end + def default_locale + locales = [I18n.locale] + locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks + locales << I18n.default_locale + locales.uniq! + locales end - def self.clear - ActionView::PathRegistry.all_resolvers.each do |resolver| - resolver.clear_cache - end - ActionView::LookupContext.reset_view_context_class - @details_keys.clear - @digest_cache.clear + def default_formats + ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] + end + + def default_variants + [] end - def self.digest_caches - @digest_cache.values + def default_handlers + Template::Handlers.extensions end + + def digest_cache + @digest_cache ||= Digestor.cache(to_cache_key) + end + + def merge(options) + return self if [:locale, :formats, :variants, :handlers].freeze.none? { |key| options[key] } + self.class.new( + locale: options[:locale] || locale, + formats: options[:formats] || formats, + variants: options[:variants] || variants, + handlers: options[:handlers] || handlers, + ) + end + + def template_rank(template) + d = template.details + format = rank(formats, d.format) or return + locale = rank(self.locale, d.locale) or return + variant = variant_rank(d.variant) or return + handler = rank(handlers, d.handler) or return + [format, locale, variant, handler] + end + + def to_h + { locale: locale, formats: formats, variants: variants, handlers: handlers } + end + + private + def initialize_copy(other) + @digest_cache = nil + super + end + + def rank(requested, value) + if requested + requested.index(value) || (requested.size if value.nil?) + elsif value.nil? + 0 + end + end + + def variant_rank(value) + if variants == :any + value.nil? ? 0 : 1 + else + rank(variants, value) + end + end + + def to_cache_key + [locale, Template.normalized_formats(formats) || formats, variants, handlers].freeze + end end def self.reset_view_context_class @@ -103,71 +176,40 @@ def self.view_context_class @view_context_mutex = Mutex.new ActiveSupport.on_load(:action_view) { ActionView::LookupContext.view_context_class } - # Add caching behavior on top of Details. - module DetailsCache - attr_accessor :cache - - # Calculate the details key. Remove the handlers from calculation to improve performance - # since the user cannot modify it explicitly. - def details_key # :nodoc: - @details_key ||= DetailsKey.details_cache_key(@details) if @cache - end - - # Temporary skip passing the details_key forward. - def disable_cache - old_value, @cache = @cache, false - yield - ensure - @cache = old_value - end - - private - def _set_detail(key, value) # :doc: - @details = @details.dup if @digest_cache || @details_key - @digest_cache = nil - @details_key = nil - @details[key] = value - end - end - # Helpers related to template lookup using the lookup context information. module ViewPaths - attr_reader :view_paths, :html_fallback_for_js + attr_reader :view_paths - def find(name, prefixes = [], partial = false, keys = [], options = {}) + def find(name, prefixes = [], partial = false, keys = [], options = nil) name, prefixes = normalize_name(name, prefixes) - details, details_key = detail_args_for(options) - @view_paths.find(name, prefixes, partial, details, details_key, keys) + @view_paths.find(name, prefixes, partial, details_for(options), @cache, keys) end - def find!(name, prefixes = [], partial = false, keys = [], options = {}) + def find!(name, prefixes = [], partial = false, keys = [], options = nil) name, prefixes = normalize_name(name, prefixes) - details, details_key = detail_args_for(options) - @view_paths.find!(name, prefixes, partial, details, details_key, keys) + @view_paths.find!(name, prefixes, partial, details_for(options), @cache, keys) end - def find_all(name, prefixes = [], partial = false, keys = [], options = {}) + def find_all(name, prefixes = [], partial = false, keys = [], options = nil) name, prefixes = normalize_name(name, prefixes) - details, details_key = detail_args_for(options) - @view_paths.find_all(name, prefixes, partial, details, details_key, keys) + @view_paths.find_all(name, prefixes, partial, details_for(options), @cache, keys) end def exists?(name, prefixes = [], partial = false, keys = [], **options) name, prefixes = normalize_name(name, prefixes) - details, details_key = detail_args_for(options) - @view_paths.exists?(name, prefixes, partial, details, details_key, keys) + @view_paths.exists?(name, prefixes, partial, details_for(options), @cache, keys) end alias :template_exists? :exists? def any?(name, prefixes = [], partial = false) name, prefixes = normalize_name(name, prefixes) - details, details_key = detail_args_for_any - @view_paths.exists?(name, prefixes, partial, details, details_key, []) + @view_paths.exists?(name, prefixes, partial, details_for_any, @cache, []) end alias :any_templates? :any? - def any_formats?(name, prefixes = [], partial = false, keys = [], options = {}) - exists?(name, prefixes, partial, keys, **options, formats: default_formats) + def any_formats?(name, prefixes = [], partial = false, keys = [], options = nil) + name, prefixes = normalize_name(name, prefixes) + @view_paths.exists?(name, prefixes, partial, details_for(options).merge(formats: default_formats), @cache, keys) end def append_view_paths(paths) @@ -178,6 +220,16 @@ def prepend_view_paths(paths) @view_paths = build_view_paths(paths + @view_paths.to_a) end + # Resolves the Details to look up with from the user options passed to + # #render (e.g. formats: :json): the context's own Details when + # there are none, an already-resolved Details (e.g. from a renderer) left + # untouched, or the context's Details with the overrides layered on. + def details_for(options) + return @details unless options + return options if Details === options + @details.merge(options) + end + private # Whenever setting view paths, makes a copy so that we can manipulate them in # instance objects as we wish. @@ -189,38 +241,8 @@ def build_view_paths(paths) end end - # Compute details hash and key according to user options (e.g. passed from #render). - def detail_args_for(options) # :doc: - return @details, details_key if options.empty? # most common path. - user_details = @details.merge(options) - - if @cache - details_key = DetailsKey.details_cache_key(user_details) - else - details_key = nil - end - - [user_details, details_key] - end - - def detail_args_for_any - @detail_args_for_any ||= begin - details = {} - - LookupContext.registered_details.each do |k| - if k == :variants - details[k] = :any - else - details[k] = LookupContext.default_procs[k].call - end - end - - if @cache - [details, DetailsKey.details_cache_key(details)] - else - [details, nil] - end - end + def details_for_any + @details_for_any ||= Details.new(variants: :any) end # Fix when prefix is specified as part of the template name @@ -243,60 +265,48 @@ def normalize_name(name, prefixes) end end - include Accessors - include DetailsCache include ViewPaths + attr_accessor :cache + def initialize(view_paths, details = {}, prefixes = []) - @details_key = nil - @digest_cache = nil @cache = true @prefixes = prefixes - @details = initialize_details({}, details) + @details = if details.is_a?(Details) + details + else + Details.new( + locale: details[:locale], + formats: details[:formats], + variants: details[:variants], + handlers: details[:handlers], + ) + end + @view_paths = build_view_paths(view_paths) end - def digest_cache - @digest_cache ||= DetailsKey.digest_cache(@details) + delegate :formats, :formats=, :variants, :variants=, :handlers, :handlers=, + :html_fallback_for_js, :default_formats, :digest_cache, to: :@details + + def disable_cache + old_value, @cache = @cache, false + yield + ensure + @cache = old_value end def with_prepended_formats(formats) details = @details.dup - details[:formats] = formats + details.formats = formats self.class.new(@view_paths, details, @prefixes) end - def initialize_details(target, details) - LookupContext.registered_details.each do |k| - target[k] = details[k] || LookupContext.default_procs[k].call - end - target - end - private :initialize_details - - # Override formats= to expand ["*/*"] values and automatically - # add :html as fallback to :js. - def formats=(values) - if values - values = values.dup - values.concat(default_formats) if values.delete "*/*" - values.uniq! - - Template.validate_formats(values) - - if (values.length == 1) && (values[0] == :js) - values << :html - @html_fallback_for_js = true - end - end - super(values) - end - # Override locale to return a symbol instead of array. def locale - @details[:locale].first + @details.locale.first end # Overload locale= to also set the I18n.locale. If the current I18n.config object responds @@ -308,7 +318,7 @@ def locale=(value) config.locale = value end - super(default_locale) + @details.locale = @details.default_locale end end end diff --git a/actionview/lib/action_view/path_set.rb b/actionview/lib/action_view/path_set.rb index 01d1ffcc46c79..ba28c4b816fce 100644 --- a/actionview/lib/action_view/path_set.rb +++ b/actionview/lib/action_view/path_set.rb @@ -37,25 +37,32 @@ def +(other) PathSet.new(paths + array) end - def find(path, prefixes, partial, details, details_key, locals) - find_all(path, prefixes, partial, details, details_key, locals).first + def find(path, prefixes, partial, details, cache, locals) + search_combinations(prefixes) do |resolver, prefix| + template = resolver.find(path, prefix, partial, details, cache, locals) + return template if template + end + nil end - def find!(path, prefixes, partial, details, details_key, locals) - find(path, prefixes, partial, details, details_key, locals) || - raise(MissingTemplate.new(self, path, prefixes, partial, details, details_key, locals)) + def find!(path, prefixes, partial, details, cache, locals) + find(path, prefixes, partial, details, cache, locals) || + raise(MissingTemplate.new(self, path, prefixes, partial, details.to_h, cache, locals)) end - def find_all(path, prefixes, partial, details, details_key, locals) + def find_all(path, prefixes, partial, details, cache, locals) search_combinations(prefixes) do |resolver, prefix| - templates = resolver.find_all(path, prefix, partial, details, details_key, locals) + templates = resolver.find_all(path, prefix, partial, details, cache, locals) return templates unless templates.empty? end [] end - def exists?(path, prefixes, partial, details, details_key, locals) - find_all(path, prefixes, partial, details, details_key, locals).any? + def exists?(path, prefixes, partial, details, cache, locals) + search_combinations(prefixes) do |resolver, prefix| + return true if resolver.find(path, prefix, partial, details, cache, locals) + end + false end private diff --git a/actionview/lib/action_view/renderer/abstract_renderer.rb b/actionview/lib/action_view/renderer/abstract_renderer.rb index e5702654e1b7a..fa328583babf7 100644 --- a/actionview/lib/action_view/renderer/abstract_renderer.rb +++ b/actionview/lib/action_view/renderer/abstract_renderer.rb @@ -154,20 +154,6 @@ def format end private - NO_DETAILS = {}.freeze - - def extract_details(options) # :doc: - details = nil - LookupContext.registered_details.each do |key| - value = options[key] - - if value - (details ||= {})[key] = Array(value) - end - end - details || NO_DETAILS - end - def prepend_formats(formats) # :doc: formats = Array(formats) return if formats.empty? || @lookup_context.html_fallback_for_js diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index 2a11dda74cdbc..189edebad3ef5 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -240,7 +240,7 @@ def initialize(lookup_context, options) super(lookup_context) @options = options @locals = @options[:locals] || {} - @details = extract_details(@options) + @details = @lookup_context.details_for(@options) end def render(partial, context, block) diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb index 1e4f77cfbb482..9ff136c1c5734 100644 --- a/actionview/lib/action_view/renderer/template_renderer.rb +++ b/actionview/lib/action_view/renderer/template_renderer.rb @@ -3,7 +3,7 @@ module ActionView class TemplateRenderer < AbstractRenderer # :nodoc: def render(context, options, &block) - @details = extract_details(options) + @details = @lookup_context.details_for(options) template = determine_template(options, &block) prepend_formats(template.format) diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index f07f97d568fdd..20bea000da0db 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -3,6 +3,7 @@ require "pathname" require "active_support/core_ext/class" require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/string/access" require "action_view/template" require "concurrent/map" @@ -57,8 +58,12 @@ def clear_cache end # Normalizes the arguments and passes it on to find_templates. - def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = []) - _find_all(name, prefix, partial, details, key, locals) + def find_all(name, prefix = nil, partial = false, details = {}, cache = false, locals = []) + _find_all(name, prefix, partial, details, cache, locals) + end + + def find(name, prefix = nil, partial = false, details = {}, cache = false, locals = []) + _find(name, prefix, partial, details, cache, locals) end def built_templates # :nodoc: @@ -72,10 +77,14 @@ def all_template_paths # :nodoc: end private - def _find_all(name, prefix, partial, details, key, locals) + def _find_all(name, prefix, partial, details, cache, locals) find_templates(name, prefix, partial, details, locals) end + def _find(name, prefix, partial, details, cache, locals) + find_all(name, prefix, partial, details, cache, locals).first + end + delegate :caching?, to: :class # This is what child classes implement. No defaults are needed @@ -128,21 +137,27 @@ def built_templates # :nodoc: end private - def _find_all(name, prefix, partial, details, key, locals) - requested_details = key || TemplateDetails::Requested.new(**details) - cache = key ? @unbound_templates : Concurrent::Map.new - - unbound_templates = - cache.compute_if_absent(TemplatePath.virtual(name, prefix, partial)) do - path = TemplatePath.build(name, prefix, partial) - unbound_templates_from_path(path) - end + def _find_all(name, prefix, partial, details, cache, locals) + unbound_templates = unbound_templates_for(name, prefix, partial, cache) - filter_and_sort_by_details(unbound_templates, requested_details).map do |unbound_template| + filter_and_sort_by_details(unbound_templates, details).map do |unbound_template| unbound_template.bind_locals(locals) end end + def _find(name, prefix, partial, details, cache, locals) + unbound_templates = unbound_templates_for(name, prefix, partial, cache) + + find_best_by_details(unbound_templates, details)&.bind_locals(locals) + end + + def unbound_templates_for(name, prefix, partial, cache) + store = cache ? @unbound_templates : Concurrent::Map.new + store.compute_if_absent(TemplatePath.virtual(name, prefix, partial)) do + unbound_templates_from_path(TemplatePath.build(name, prefix, partial)) + end + end + def source_for_template(template) Template::Sources::File.new(template) end @@ -177,18 +192,26 @@ def unbound_templates_from_path(path) end end - def filter_and_sort_by_details(templates, requested_details) - filtered_templates = templates.select do |template| - template.details.matches?(requested_details) + def filter_and_sort_by_details(templates, details) + ranked = templates.filter_map do |template| + rank = details.template_rank(template) + [rank, template] if rank end - if filtered_templates.count > 1 - filtered_templates.sort_by! do |template| - template.details.sort_key_for(requested_details) + ranked.sort_by!(&:first) if ranked.size > 1 + ranked.map!(&:last) + end + + def find_best_by_details(templates, details) + best = best_rank = nil + templates.each do |template| + rank = details.template_rank(template) or next + if best_rank.nil? || (rank <=> best_rank) < 0 + best = template + best_rank = rank end end - - filtered_templates + best end # Safe glob within @path diff --git a/actionview/lib/action_view/template_details.rb b/actionview/lib/action_view/template_details.rb index 0f74fb65b2a48..97872922e2a39 100644 --- a/actionview/lib/action_view/template_details.rb +++ b/actionview/lib/action_view/template_details.rb @@ -2,34 +2,6 @@ module ActionView class TemplateDetails # :nodoc: - class Requested - attr_reader :locale, :handlers, :formats, :variants - attr_reader :locale_idx, :handlers_idx, :formats_idx, :variants_idx - - ANY_HASH = Hash.new(1).merge(nil => 0).freeze - - def initialize(locale:, handlers:, formats:, variants:) - @locale = locale - @handlers = handlers - @formats = formats - @variants = variants - - @locale_idx = build_idx_hash(locale) - @handlers_idx = build_idx_hash(handlers) - @formats_idx = build_idx_hash(formats) - if variants == :any - @variants_idx = ANY_HASH - else - @variants_idx = build_idx_hash(variants) - end - end - - private - def build_idx_hash(arr) - [*arr, nil].each_with_index.to_h.freeze - end - end - attr_reader :locale, :handler, :format, :variant def initialize(locale, handler, format, variant) @@ -39,22 +11,6 @@ def initialize(locale, handler, format, variant) @variant = variant end - def matches?(requested) - requested.formats_idx[@format] && - requested.locale_idx[@locale] && - requested.variants_idx[@variant] && - requested.handlers_idx[@handler] - end - - def sort_key_for(requested) - [ - requested.formats_idx[@format], - requested.locale_idx[@locale], - requested.variants_idx[@variant], - requested.handlers_idx[@handler] - ] - end - def handler_class Template.handler_for_extension(handler) end diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb index 886015ebdbfee..fd4bd639befa9 100644 --- a/actionview/test/actionpack/controller/layout_test.rb +++ b/actionview/test/actionpack/controller/layout_test.rb @@ -19,12 +19,12 @@ module TemplateHandlerHelper def with_template_handler(*extensions, handler) ActionView::Template.register_template_handler(*extensions, handler) ActionController::Base.view_paths.paths.each(&:clear_cache) - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear yield ensure ActionView::Template.unregister_template_handler(*extensions) ActionController::Base.view_paths.paths.each(&:clear_cache) - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end end diff --git a/actionview/test/actionpack/controller/render_test.rb b/actionview/test/actionpack/controller/render_test.rb index 631f0a717795b..53e88d3a86254 100644 --- a/actionview/test/actionpack/controller/render_test.rb +++ b/actionview/test/actionpack/controller/render_test.rb @@ -1528,11 +1528,11 @@ def test_render_renderable def with_annotations_enabled ActionView::Base.annotate_rendered_view_with_filenames = true - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear yield ensure ActionView::Base.annotate_rendered_view_with_filenames = false - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end def test_template_annotations diff --git a/actionview/test/actionpack/controller/view_paths_test.rb b/actionview/test/actionpack/controller/view_paths_test.rb index 4e2446f724563..9bbe2dcf5f121 100644 --- a/actionview/test/actionpack/controller/view_paths_test.rb +++ b/actionview/test/actionpack/controller/view_paths_test.rb @@ -30,7 +30,7 @@ def hello_world; render(template: "test/hello_world"); end end def setup - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear @controller = TestController.new @request = ActionController::TestRequest.create(@controller.class) diff --git a/actionview/test/activerecord/render_partial_with_record_identification_test.rb b/actionview/test/activerecord/render_partial_with_record_identification_test.rb index f8a374517d5da..d8030ebbc072e 100644 --- a/actionview/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionview/test/activerecord/render_partial_with_record_identification_test.rb @@ -54,7 +54,7 @@ class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase def setup super - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end def test_rendering_partial_with_has_many_and_belongs_to_association diff --git a/actionview/test/template/compiled_templates_test.rb b/actionview/test/template/compiled_templates_test.rb index ea9646b484a5f..28b25013bfa8a 100644 --- a/actionview/test/template/compiled_templates_test.rb +++ b/actionview/test/template/compiled_templates_test.rb @@ -14,7 +14,7 @@ def setup def teardown super - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end def test_template_with_nil_erb_return diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb index 38989ef6a5760..a19881bb361b3 100644 --- a/actionview/test/template/digestor_test.rb +++ b/actionview/test/template/digestor_test.rb @@ -23,7 +23,7 @@ def setup @cwd = Dir.pwd @tmp_dir = Dir.mktmpdir - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear FileUtils.cp_r FixtureFinder::FIXTURES_DIR, @tmp_dir Dir.chdir @tmp_dir end diff --git a/actionview/test/template/log_subscriber_test.rb b/actionview/test/template/log_subscriber_test.rb index afa2479f649e9..80872f7a4f860 100644 --- a/actionview/test/template/log_subscriber_test.rb +++ b/actionview/test/template/log_subscriber_test.rb @@ -21,7 +21,7 @@ def setup @old_logger = ActionView::LogSubscriber.logger ActionView::LogSubscriber.logger = @logger - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear view_paths = ActionController::Base.view_paths diff --git a/actionview/test/template/lookup_context_test.rb b/actionview/test/template/lookup_context_test.rb index e849999c892ee..70b8790395d95 100644 --- a/actionview/test/template/lookup_context_test.rb +++ b/actionview/test/template/lookup_context_test.rb @@ -6,7 +6,7 @@ class LookupContextTest < ActiveSupport::TestCase def setup @lookup_context = build_lookup_context(FIXTURE_LOAD_PATH, {}) - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear end def build_lookup_context(paths, details) @@ -127,28 +127,6 @@ def teardown end end - test "generates a new details key for each details hash" do - keys = [] - keys << @lookup_context.details_key - assert_equal 1, keys.uniq.size - - @lookup_context.locale = :da - keys << @lookup_context.details_key - assert_equal 2, keys.uniq.size - - @lookup_context.locale = :en - keys << @lookup_context.details_key - assert_equal 2, keys.uniq.size - - @lookup_context.formats = [:html] - keys << @lookup_context.details_key - assert_equal 3, keys.uniq.size - - @lookup_context.formats = nil - keys << @lookup_context.details_key - assert_equal 3, keys.uniq.size - end - test "uses details as part of cache key" do fixtures = { "test/_foo.erb" => "Foo", @@ -236,7 +214,7 @@ def setup test "if a single prefix is passed as a string and the lookup fails, MissingTemplate accepts it" do e = assert_raise ActionView::MissingTemplate do - details = { handlers: [], formats: [], variants: [], locale: [] } + details = ActionView::LookupContext::Details.new(handlers: [], formats: [], variants: [], locale: []) @lookup_context.view_paths.find!("foo", "parent", true, details, nil, []) end assert_match %r{Missing partial parent/_foo with .*\n\nSearched in:\n \* "/Path/to/views"\n}, e.message diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb index 1aa7be3a9e13c..b4f1e939ef8a9 100644 --- a/actionview/test/template/render_test.rb +++ b/actionview/test/template/render_test.rb @@ -845,7 +845,7 @@ class FrozenStringLiteralEnabledViewRenderTest < ActiveSupport::TestCase include RenderTestCases def setup - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear @previous_frozen_literal = ActionView::Template.frozen_string_literal ActionView::Template.frozen_string_literal = true @@ -871,7 +871,7 @@ class CachedViewRenderTest < ActiveSupport::TestCase # Ensure view path cache is primed def setup - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear view_paths = ActionController::Base.view_paths assert_equal ActionView::FileSystemResolver, view_paths.first.class setup_view(view_paths) @@ -909,7 +909,7 @@ class LazyViewRenderTest < ActiveSupport::TestCase # Test the same thing as above, but make sure the view path # is not eager loaded def setup - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear path = ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH) view_paths = ActionView::PathSet.new([path]) assert_equal ActionView::FileSystemResolver.new(FIXTURE_LOAD_PATH), view_paths.first @@ -962,7 +962,7 @@ class CachedCustomer < Customer; end # Ensure view path cache is primed setup do - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear view_paths = ActionController::Base.view_paths assert_equal ActionView::FileSystemResolver, view_paths.first.class diff --git a/actionview/test/template/resolver_shared_tests.rb b/actionview/test/template/resolver_shared_tests.rb index 67bff214a1945..cc41306b1343d 100644 --- a/actionview/test/template/resolver_shared_tests.rb +++ b/actionview/test/template/resolver_shared_tests.rb @@ -19,10 +19,14 @@ def context @context ||= ActionView::LookupContext.new(resolver) end + def details(**args) + ActionView::LookupContext::Details.new(**args) + end + def test_can_find_with_no_extensions with_file "test/hello_world", "Hello default!" - templates = resolver.find_all("hello_world", "test", false, locale: [:en], formats: [:html], variants: [:phone], handlers: [:erb]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [:en], formats: [:html], variants: [:phone], handlers: [:erb])) assert_equal 1, templates.size assert_equal "Hello default!", templates[0].source assert_equal "test/hello_world", templates[0].virtual_path @@ -34,7 +38,7 @@ def test_can_find_with_no_extensions def test_can_find_with_just_handler with_file "test/hello_world.erb", "Hello erb!" - templates = resolver.find_all("hello_world", "test", false, locale: [:en], formats: [:html], variants: [:phone], handlers: [:erb]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [:en], formats: [:html], variants: [:phone], handlers: [:erb])) assert_equal 1, templates.size assert_equal "Hello erb!", templates[0].source assert_equal "test/hello_world", templates[0].virtual_path @@ -46,7 +50,7 @@ def test_can_find_with_just_handler def test_can_find_with_format_and_handler with_file "test/hello_world.text.builder", "Hello plain text!" - templates = resolver.find_all("hello_world", "test", false, locale: [:en], formats: [:html, :text], variants: [:phone], handlers: [:erb, :builder]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [:en], formats: [:html, :text], variants: [:phone], handlers: [:erb, :builder])) assert_equal 1, templates.size assert_equal "Hello plain text!", templates[0].source assert_equal "test/hello_world", templates[0].virtual_path @@ -58,7 +62,7 @@ def test_can_find_with_format_and_handler def test_can_find_with_variant_format_and_handler with_file "test/hello_world.html+phone.erb", "Hello plain text!" - templates = resolver.find_all("hello_world", "test", false, locale: [:en], formats: [:html], variants: [:phone], handlers: [:erb]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [:en], formats: [:html], variants: [:phone], handlers: [:erb])) assert_equal 1, templates.size assert_equal "Hello plain text!", templates[0].source assert_equal "test/hello_world", templates[0].virtual_path @@ -70,7 +74,7 @@ def test_can_find_with_variant_format_and_handler def test_can_find_with_any_variant_format_and_handler with_file "test/hello_world.html+phone.erb", "Hello plain text!" - templates = resolver.find_all("hello_world", "test", false, locale: [:en], formats: [:html], variants: :any, handlers: [:erb]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [:en], formats: [:html], variants: :any, handlers: [:erb])) assert_equal 1, templates.size assert_equal "Hello plain text!", templates[0].source assert_equal "test/hello_world", templates[0].virtual_path @@ -83,7 +87,7 @@ def test_can_find_when_special_chars_in_path dir = "test +()[]{}" with_file "#{dir}/hello_world", "Hello funky path!" - templates = resolver.find_all("hello_world", dir, false, locale: [:en], formats: [:html], variants: [:phone], handlers: [:erb]) + templates = resolver.find_all("hello_world", dir, false, details(locale: [:en], formats: [:html], variants: [:phone], handlers: [:erb])) assert_equal 1, templates.size assert_equal "Hello funky path!", templates[0].source assert_equal "#{dir}/hello_world", templates[0].virtual_path @@ -92,10 +96,10 @@ def test_can_find_when_special_chars_in_path def test_doesnt_find_template_with_wrong_details with_file "test/hello_world.html.erb", "Hello plain text!" - templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:xml], variants: :any, handlers: [:builder]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [], formats: [:xml], variants: :any, handlers: [:builder])) assert_equal 0, templates.size - templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:xml], variants: :any, handlers: [:erb]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [], formats: [:xml], variants: :any, handlers: [:erb])) assert_equal 0, templates.size end @@ -150,7 +154,7 @@ def test_templates_sort_by_formats_json_first with_file "test/hello_world.html.erb", "Hello HTML!" with_file "test/hello_world.json.builder", "Hello JSON!" - templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:json, :html], variants: :any, handlers: [:erb, :builder]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [], formats: [:json, :html], variants: :any, handlers: [:erb, :builder])) assert_equal 2, templates.size assert_equal "Hello JSON!", templates[0].source @@ -163,7 +167,7 @@ def test_templates_sort_by_formats_html_first with_file "test/hello_world.html.erb", "Hello HTML!" with_file "test/hello_world.json.builder", "Hello JSON!" - templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder])) assert_equal 2, templates.size assert_equal "Hello HTML!", templates[0].source @@ -175,7 +179,7 @@ def test_templates_sort_by_formats_html_first def test_templates_with_variant with_file "test/hello_world.html+mobile.erb", "Hello HTML!" - templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder])) assert_equal 1, templates.size assert_equal "Hello HTML!", templates[0].source @@ -206,7 +210,7 @@ def test_finds_variants_in_order def test_templates_no_format_with_variant with_file "test/hello_world+mobile.erb", "Hello HTML!" - templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder])) assert_equal 1, templates.size assert_equal "Hello HTML!", templates[0].source @@ -218,7 +222,7 @@ def test_templates_no_format_with_variant def test_templates_no_format_or_handler_with_variant with_file "test/hello_world+mobile", "Hello HTML!" - templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder]) + templates = resolver.find_all("hello_world", "test", false, details(locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder])) assert_equal 1, templates.size assert_equal "Hello HTML!", templates[0].source diff --git a/actionview/test/template/streaming_render_test.rb b/actionview/test/template/streaming_render_test.rb index f0e7ec1241e2a..2b5d8cdead7e8 100644 --- a/actionview/test/template/streaming_render_test.rb +++ b/actionview/test/template/streaming_render_test.rb @@ -7,7 +7,7 @@ class TestController < ActionController::Base class SetupFiberedBase < ActiveSupport::TestCase def setup - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear view_paths = ActionController::Base.view_paths diff --git a/actionview/test/template/structured_event_subscriber_test.rb b/actionview/test/template/structured_event_subscriber_test.rb index 9b0cd6457ca6d..3423699e6bd2b 100644 --- a/actionview/test/template/structured_event_subscriber_test.rb +++ b/actionview/test/template/structured_event_subscriber_test.rb @@ -12,7 +12,7 @@ class StructuredEventSubscriberTest < ActiveSupport::TestCase def setup super - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear view_paths = ActionController::Base.view_paths diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb index 21bc4a04f66c1..ff71f23cc8a15 100644 --- a/actionview/test/template/test_case_test.rb +++ b/actionview/test/template/test_case_test.rb @@ -26,7 +26,7 @@ class TestCase module SharedTests def setup - ActionView::LookupContext::DetailsKey.clear + ActionView::LookupContext.clear super end diff --git a/actionview/test/template/testing/fixture_resolver_test.rb b/actionview/test/template/testing/fixture_resolver_test.rb index 2b0136ff2224a..cc67c5559094c 100644 --- a/actionview/test/template/testing/fixture_resolver_test.rb +++ b/actionview/test/template/testing/fixture_resolver_test.rb @@ -5,13 +5,13 @@ class FixtureResolverTest < ActiveSupport::TestCase def test_should_return_empty_list_for_unknown_path resolver = ActionView::FixtureResolver.new() - templates = resolver.find_all("path", "arbitrary", false, locale: [], formats: [:html], variants: [], handlers: []) + templates = resolver.find_all("path", "arbitrary", false, ActionView::LookupContext::Details.new(locale: [], formats: [:html], variants: [], handlers: [])) assert_equal [], templates, "expected an empty list of templates" end def test_should_return_template_for_declared_path resolver = ActionView::FixtureResolver.new("arbitrary/path.erb" => "this text") - templates = resolver.find_all("path", "arbitrary", false, locale: [], formats: [:html], variants: [], handlers: [:erb]) + templates = resolver.find_all("path", "arbitrary", false, ActionView::LookupContext::Details.new(locale: [], formats: [:html], variants: [], handlers: [:erb])) assert_equal 1, templates.size, "expected one template" assert_equal "this text", templates.first.source assert_equal "arbitrary/path", templates.first.virtual_path @@ -20,7 +20,7 @@ def test_should_return_template_for_declared_path def test_should_match_templates_with_variants resolver = ActionView::FixtureResolver.new("arbitrary/path.html+variant.erb" => "this text") - templates = resolver.find_all("path", "arbitrary", false, locale: [], formats: [:html], variants: [:variant], handlers: [:erb]) + templates = resolver.find_all("path", "arbitrary", false, ActionView::LookupContext::Details.new(locale: [], formats: [:html], variants: [:variant], handlers: [:erb])) assert_equal 1, templates.size, "expected one template" assert_equal "this text", templates.first.source assert_equal "arbitrary/path", templates.first.virtual_path @@ -30,8 +30,8 @@ def test_should_match_templates_with_variants def test_should_match_locales resolver = ActionView::FixtureResolver.new("arbitrary/path.erb" => "this text", "arbitrary/path.fr.erb" => "ce texte") - en = resolver.find_all("path", "arbitrary", false, locale: [:en], formats: [:html], variants: [], handlers: [:erb]) - fr = resolver.find_all("path", "arbitrary", false, locale: [:fr], formats: [:html], variants: [], handlers: [:erb]) + en = resolver.find_all("path", "arbitrary", false, ActionView::LookupContext::Details.new(locale: [:en], formats: [:html], variants: [], handlers: [:erb])) + fr = resolver.find_all("path", "arbitrary", false, ActionView::LookupContext::Details.new(locale: [:fr], formats: [:html], variants: [], handlers: [:erb])) assert_equal 1, en.size assert_equal 2, fr.size @@ -43,10 +43,10 @@ def test_should_match_locales def test_should_return_all_variants_for_any resolver = ActionView::FixtureResolver.new("arbitrary/path.html.erb" => "this html", "arbitrary/path.html+variant.erb" => "this text") - templates = resolver.find_all("path", "arbitrary", false, locale: [], formats: [:html], variants: [], handlers: [:erb]) + templates = resolver.find_all("path", "arbitrary", false, ActionView::LookupContext::Details.new(locale: [], formats: [:html], variants: [], handlers: [:erb])) assert_equal 1, templates.size, "expected one template" assert_equal "this html", templates.first.source - templates = resolver.find_all("path", "arbitrary", false, locale: [], formats: [:html], variants: :any, handlers: [:erb]) + templates = resolver.find_all("path", "arbitrary", false, ActionView::LookupContext::Details.new(locale: [], formats: [:html], variants: :any, handlers: [:erb])) assert_equal 2, templates.size, "expected all templates" end end diff --git a/railties/test/application/per_request_digest_cache_test.rb b/railties/test/application/per_request_digest_cache_test.rb index 809f94be8dcd1..2825ce8f63941 100644 --- a/railties/test/application/per_request_digest_cache_test.rb +++ b/railties/test/application/per_request_digest_cache_test.rb @@ -56,13 +56,13 @@ def index get "/customers" assert_equal 200, last_response.status - values = ActionView::LookupContext::DetailsKey.digest_caches.first.values + values = ActionView::Digestor.digest_caches.first.values assert_equal [ "ddb451d2c1b2374caa676005893bb776" ], values assert_equal %w(david dingus), last_response.body.split.map(&:strip) end test "template digests are cleared before a request" do - assert_called(ActionView::LookupContext::DetailsKey, :clear, times: 2) do + assert_called(ActionView::LookupContext, :clear, times: 2) do get "/customers" assert_equal 200, last_response.status end