From 8dd731bc502a07f4fb76eb2706a1f3bca479ef63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 16 Mar 2010 02:08:34 +0100 Subject: Move more normalization up to the lookup context, so it does not have to repeat in every resolver. --- actionpack/lib/action_view/lookup_context.rb | 36 +++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'actionpack/lib/action_view/lookup_context.rb') diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index 8eb17bf8f1..598007c7a4 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -15,12 +15,10 @@ module ActionView def self.register_detail(name, options = {}, &block) self.registered_details << name - Setters.send :define_method, :"_#{name}_defaults", &block Setters.module_eval <<-METHOD, __FILE__, __LINE__ + 1 def #{name}=(value) value = Array(value.presence || _#{name}_defaults) - #{"value << nil unless value.include?(nil)" unless options[:allow_nil] == false} unless value == @details[:#{name}] @details_key, @details = nil, @details.merge(:#{name} => value) @@ -69,16 +67,16 @@ module ActionView end def find(name, prefix = nil, partial = false) - @view_paths.find(name, prefix, partial, details, details_key) + @view_paths.find(*args_for_lookup(name, prefix, partial)) end alias :find_template :find def find_all(name, prefix = nil, partial = false) - @view_paths.find_all(name, prefix, partial, details, details_key) + @view_paths.find_all(*args_for_lookup(name, prefix, partial)) end def exists?(name, prefix = nil, partial = false) - @view_paths.exists?(name, prefix, partial, details, details_key) + @view_paths.exists?(*args_for_lookup(name, prefix, partial)) end alias :template_exists? :exists? @@ -94,6 +92,32 @@ module ActionView ensure added_resolvers.times { view_paths.pop } end + + protected + + def args_for_lookup(name, prefix, partial) #:nodoc: + name, prefix = normalize_name(name, prefix) + details_key = self.details_key + details = self.details.merge(:handlers => default_handlers) + [name, prefix, partial || false, details, details_key] + end + + # Support legacy foo.erb names even though we now ignore .erb + # as well as incorrectly putting part of the path in the template + # name instead of the prefix. + def normalize_name(name, prefix) #:nodoc: + name = name.to_s.gsub(handlers_regexp, '') + parts = name.split('/') + return parts.pop, [prefix, *parts].compact.join("/") + end + + def default_handlers #:nodoc: + @detault_handlers ||= Template::Handlers.extensions + end + + def handlers_regexp #:nodoc: + @handlers_regexp ||= /\.(?:#{default_handlers.join('|')})$/ + end end module Details @@ -113,7 +137,7 @@ module ActionView end # Overload formats= to reject [:"*/*"] values. - def formats=(value, freeze=true) + def formats=(value) value = nil if value == [:"*/*"] super(value) end -- cgit v1.2.3 From f28d856cece877d1b2f306f54aeb12cce1db1023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 19 Mar 2010 17:20:15 +0100 Subject: Improve performance of the rendering stack by freezing formats as a sign that they shouldn't be further modified. --- actionpack/lib/action_view/lookup_context.rb | 72 +++++++++++++--------------- 1 file changed, 34 insertions(+), 38 deletions(-) (limited to 'actionpack/lib/action_view/lookup_context.rb') diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index 598007c7a4..cf28772f12 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/object/try' +require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/object/blank' module ActionView @@ -15,21 +15,23 @@ module ActionView def self.register_detail(name, options = {}, &block) self.registered_details << name - Setters.send :define_method, :"_#{name}_defaults", &block - Setters.module_eval <<-METHOD, __FILE__, __LINE__ + 1 - def #{name}=(value) - value = Array(value.presence || _#{name}_defaults) + Accessors.send :define_method, :"_#{name}_defaults", &block + Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1 + def #{name} + @details[:#{name}] + end - unless value == @details[:#{name}] - @details_key, @details = nil, @details.merge(:#{name} => value) - @details.freeze - end + def #{name}=(value) + value = Array.wrap(value.presence || _#{name}_defaults) + @details_key = nil unless value == @details[:#{name}] + # Always set the value to handle frozen arrays + @details[:#{name}] = value end METHOD end - # Holds raw setters for the registered details. - module Setters #:nodoc: + # Holds accessors for the registered details. + module Accessors #:nodoc: end register_detail(:formats) { Mime::SET.symbols } @@ -52,9 +54,9 @@ module ActionView end def initialize(view_paths, details = {}) - @details, @details_key = {}, nil + @details, @details_key = { :handlers => default_handlers }, nil self.view_paths = view_paths - self.details = details + self.update_details(details, true) end module ViewPaths @@ -97,9 +99,7 @@ module ActionView def args_for_lookup(name, prefix, partial) #:nodoc: name, prefix = normalize_name(name, prefix) - details_key = self.details_key - details = self.details.merge(:handlers => default_handlers) - [name, prefix, partial || false, details, details_key] + [name, prefix, partial || false, @details, details_key] end # Support legacy foo.erb names even though we now ignore .erb @@ -121,48 +121,44 @@ module ActionView end module Details - attr_reader :details - - def details=(given_details) - registered_details.each { |key| send(:"#{key}=", given_details[key]) } - end - - def details_key + # 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.get(@details) end - # Shortcut to read formats from details. - def formats - @details[:formats].compact - end - # Overload formats= to reject [:"*/*"] values. def formats=(value) - value = nil if value == [:"*/*"] + value = nil if value == [:"*/*"] + value << :html if value == [:js] super(value) end - # Shortcut to read locale. + # Overload locale to return a symbol instead of array def locale - I18n.locale + @details[:locale].first end # Overload locale= to also set the I18n.locale. If the current I18n.config object responds # to i18n_config, it means that it's has a copy of the original I18n configuration and it's # acting as proxy, which we need to skip. def locale=(value) - value = value.first if value.is_a?(Array) - config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config - config.locale = value if value + if value + config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config + config.locale = value + end super(I18n.locale) end # Update the details keys by merging the given hash into the current # details hash. If a block is given, the details are modified just during # the execution of the block and reverted to the previous value after. - def update_details(new_details) - old_details = @details - self.details = old_details.merge(new_details) + def update_details(new_details, force=false) + old_details = @details.dup + + registered_details.each do |key| + send(:"#{key}=", new_details[key]) if force || new_details.key?(key) + end if block_given? begin @@ -174,7 +170,7 @@ module ActionView end end - include Setters + include Accessors include Details include ViewPaths end -- cgit v1.2.3 From 4c7c4061558bb8781da0d54159e3cebcb0a8c07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 26 Mar 2010 01:12:24 +0100 Subject: Remove reference to unexistent methods and fix typo. --- actionpack/lib/action_view/lookup_context.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack/lib/action_view/lookup_context.rb') diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index cf28772f12..10f1911e31 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -112,7 +112,7 @@ module ActionView end def default_handlers #:nodoc: - @detault_handlers ||= Template::Handlers.extensions + @default_handlers ||= Template::Handlers.extensions end def handlers_regexp #:nodoc: -- cgit v1.2.3 From a09e99259c688a839bd5b4635da6f119dd1e0e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 27 Mar 2010 20:51:25 +0100 Subject: Ensure details are frozen after @details_keys lookup. The implementation waits to freeze until the last required moment, to avoid duping hashes. --- actionpack/lib/action_view/lookup_context.rb | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'actionpack/lib/action_view/lookup_context.rb') diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index 10f1911e31..9b59aac0eb 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -23,9 +23,12 @@ module ActionView def #{name}=(value) value = Array.wrap(value.presence || _#{name}_defaults) - @details_key = nil unless value == @details[:#{name}] - # Always set the value to handle frozen arrays - @details[:#{name}] = value + + if value != @details[:#{name}] + @details_key = nil + @details = @details.dup if @details.frozen? + @details[:#{name}] = value.freeze + end end METHOD end @@ -45,7 +48,7 @@ module ActionView @details_keys = Hash.new def self.get(details) - @details_keys[details] ||= new + @details_keys[details.freeze] ||= new end def initialize @@ -55,6 +58,7 @@ module ActionView def initialize(view_paths, details = {}) @details, @details_key = { :handlers => default_handlers }, nil + @frozen_formats = false self.view_paths = view_paths self.update_details(details, true) end @@ -127,6 +131,15 @@ module ActionView @details_key ||= DetailsKey.get(@details) end + # Freeze the current formats in the lookup context. By freezing them, you are guaranteeing + # that next template lookups are not going to modify the formats. The controller can also + # use this, to ensure that formats won't be further modified (as it does in respond_to blocks). + def freeze_formats(formats, unless_frozen=false) #:nodoc: + return if unless_frozen && @frozen_formats + self.formats = formats + @frozen_formats = true + end + # Overload formats= to reject [:"*/*"] values. def formats=(value) value = nil if value == [:"*/*"] -- cgit v1.2.3