Slightly reorganize lookup context modules in order to better isolate cache responsibilities.

This commit is contained in:
José Valim
2011-09-21 15:28:05 -07:00
parent 8aa537c945
commit f373f296d0

View File

@@ -1,5 +1,6 @@
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/module/remove_method'
module ActionView
# = Action View Lookup Context
@@ -17,12 +18,11 @@ module ActionView
mattr_accessor :registered_details
self.registered_details = []
mattr_accessor :registered_detail_setters
self.registered_detail_setters = []
def self.register_detail(name, options = {}, &block)
self.registered_details << name
self.registered_detail_setters << [name, "#{name}="]
initialize = registered_details.map { |n| "self.#{n} = details[:#{n}]" }
update = registered_details.map { |n| "self.#{n} = details[:#{n}] if details.key?(:#{n})" }
Accessors.send :define_method, :"_#{name}_defaults", &block
Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -34,6 +34,16 @@ module ActionView
value = Array.wrap(value.presence || _#{name}_defaults)
_set_detail(:#{name}, value) if value != @details[:#{name}]
end
remove_possible_method :initialize_details
def initialize_details(details)
#{initialize.join("\n")}
end
remove_possible_method :update_details
def update_details(details)
#{update.join("\n")}
end
METHOD
end
@@ -60,18 +70,53 @@ module ActionView
end
end
def initialize(view_paths, details = {}, prefixes = [])
@details, @details_key = { :handlers => default_handlers }, nil
@frozen_formats, @skip_default_locale = false, false
@cache = true
@prefixes = prefixes
# Add caching behavior on top of Details.
module DetailsCache
attr_accessor :cache
self.view_paths = view_paths
self.registered_detail_setters.each do |key, setter|
send(setter, 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) if @cache
end
# Temporary skip passing the details_key forward.
def disable_cache
old_value, @cache = @cache, false
yield
ensure
@cache = old_value
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)
if block_given?
old_details = @details.dup
super
begin
yield
ensure
@details_key = nil
@details = old_details
end
else
super
end
end
protected
def _set_detail(key, value)
@details_key = nil
@details = @details.dup if @details.frozen?
@details[key] = value.freeze
end
end
# Helpers related to template lookup using the lookup context information.
module ViewPaths
attr_reader :view_paths
@@ -141,111 +186,77 @@ module ActionView
end
end
module Details
attr_accessor :cache
include Accessors
include DetailsCache
include ViewPaths
# 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) if @cache
def initialize(view_paths, details = {}, prefixes = [])
@details, @details_key = { :handlers => default_handlers }, nil
@frozen_formats, @skip_default_locale = false, false
@cache = true
@prefixes = prefixes
self.view_paths = view_paths
initialize_details(details)
end
# Freeze the current formats in the lookup context. By freezing them, you
# that next template lookups are not going to modify the formats. The con
# use this, to ensure that formats won't be further modified (as it does
def freeze_formats(formats, unless_frozen=false) #:nodoc:
return if unless_frozen && @frozen_formats
self.formats = formats
@frozen_formats = true
end
# Override formats= to expand ["*/*"] values and automatically
# add :html as fallback to :js.
def formats=(values)
if values
values.concat(_formats_defaults) if values.delete "*/*"
values << :html if values == [:js]
end
super(values)
end
# Do not use the default locale on template lookup.
def skip_default_locale!
@skip_default_locale = true
self.locale = nil
end
# Override locale to return a symbol instead of array.
def locale
@details[:locale].first
end
# Overload locale= to also set the I18n.locale. If the current I18n.config object responds
# to original_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)
if value
config = I18n.config.respond_to?(:original_config) ? I18n.config.original_config : I18n.config
config.locale = value
end
# Temporary skip passing the details_key forward.
def disable_cache
old_value, @cache = @cache, false
super(@skip_default_locale ? I18n.locale : _locale_defaults)
end
# A method which only uses the first format in the formats array for layout lookup.
# This method plays straight with instance variables for performance reasons.
def with_layout_format
if formats.size == 1
yield
ensure
@cache = old_value
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 expand ["*/*"] values and automatically
# add :html as fallback to :js.
def formats=(values)
if values
values.concat(_formats_defaults) if values.delete "*/*"
values << :html if values == [:js]
end
super(values)
end
# Do not use the default locale on template lookup.
def skip_default_locale!
@skip_default_locale = true
self.locale = nil
end
# Overload locale to return a symbol instead of array.
def locale
@details[:locale].first
end
# Overload locale= to also set the I18n.locale. If the current I18n.config object responds
# to original_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)
if value
config = I18n.config.respond_to?(:original_config) ? I18n.config.original_config : I18n.config
config.locale = value
end
super(@skip_default_locale ? I18n.locale : _locale_defaults)
end
# A method which only uses the first format in the formats array for layout lookup.
# This method plays straight with instance variables for performance reasons.
def with_layout_format
if formats.size == 1
yield
else
old_formats = formats
_set_detail(:formats, formats[0,1])
begin
yield
ensure
_set_detail(:formats, old_formats)
end
end
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.dup
registered_detail_setters.each do |key, setter|
send(setter, new_details[key]) if new_details.key?(key)
end
else
old_formats = formats
_set_detail(:formats, formats[0,1])
begin
yield
ensure
@details_key = nil
@details = old_details
_set_detail(:formats, old_formats)
end
end
protected
def _set_detail(key, value)
@details_key = nil
@details = @details.dup if @details.frozen?
@details[key] = value.freeze
end
end
include Accessors
include Details
include ViewPaths
end
end