view_cache_dependency API
A declarative API for specifying dependencies that affect template cache digest computation. In your controller, specify any of said dependencies: view_cache_dependency { "phone" if using_phone? } When the block is evaluated, the resulting value is included in the cache digest calculation, allowing you to generate different digests for effectively the same template. (Mostly useful if you're mucking with template load paths.)
This commit is contained in:
parent
ac86cbec82
commit
70e684a681
|
@ -1,5 +1,10 @@
|
|||
## Rails 4.0.0 (unreleased) ##
|
||||
|
||||
* Added view_cache_dependency API for declaring dependencies that affect
|
||||
cache digest computation.
|
||||
|
||||
*Jamis Buck*
|
||||
|
||||
* `image_submit_tag` will set `alt` attribute from image source if not
|
||||
specified.
|
||||
|
||||
|
|
|
@ -70,12 +70,30 @@ module ActionController
|
|||
|
||||
config_accessor :perform_caching
|
||||
self.perform_caching = true if perform_caching.nil?
|
||||
|
||||
class_attribute :_view_cache_dependencies
|
||||
self._view_cache_dependencies = []
|
||||
helper_method :view_cache_dependencies if respond_to?(:helper_method)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def view_cache_dependency(&dependency)
|
||||
self._view_cache_dependencies += [dependency]
|
||||
end
|
||||
|
||||
def view_cache_dependencies
|
||||
_view_cache_dependencies.map { |dep| instance_exec &dep }.compact
|
||||
end
|
||||
end
|
||||
|
||||
def caching_allowed?
|
||||
request.get? && response.status == 200
|
||||
end
|
||||
|
||||
def view_cache_dependencies
|
||||
self.class.view_cache_dependencies
|
||||
end
|
||||
|
||||
protected
|
||||
# Convenience accessor.
|
||||
def cache(key, options = {}, &block)
|
||||
|
|
|
@ -24,16 +24,17 @@ module ActionView
|
|||
@@cache = ThreadSafe::Cache.new
|
||||
|
||||
def self.digest(name, format, finder, options = {})
|
||||
@@cache["#{name}.#{format}"] ||= begin
|
||||
cache_key = [name, format] + Array.wrap(options[:dependencies])
|
||||
@@cache[cache_key.join('.')] ||= begin
|
||||
klass = options[:partial] || name.include?("/_") ? PartialDigestor : Digestor
|
||||
klass.new(name, format, finder).digest
|
||||
klass.new(name, format, finder, options).digest
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :name, :format, :finder
|
||||
attr_reader :name, :format, :finder, :options
|
||||
|
||||
def initialize(name, format, finder)
|
||||
@name, @format, @finder = name, format, finder
|
||||
def initialize(name, format, finder, options={})
|
||||
@name, @format, @finder, @options = name, format, finder, options
|
||||
end
|
||||
|
||||
def digest
|
||||
|
@ -81,9 +82,11 @@ module ActionView
|
|||
end
|
||||
|
||||
def dependency_digest
|
||||
dependencies.collect do |template_name|
|
||||
template_digests = dependencies.collect do |template_name|
|
||||
Digestor.digest(template_name, format, finder, partial: true)
|
||||
end.join("-")
|
||||
end
|
||||
|
||||
(template_digests + injected_dependencies).join("-")
|
||||
end
|
||||
|
||||
def render_dependencies
|
||||
|
@ -105,6 +108,10 @@ module ActionView
|
|||
def explicit_dependencies
|
||||
source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
|
||||
end
|
||||
|
||||
def injected_dependencies
|
||||
Array.wrap(options[:dependencies])
|
||||
end
|
||||
end
|
||||
|
||||
class PartialDigestor < Digestor # :nodoc:
|
||||
|
|
|
@ -167,7 +167,7 @@ module ActionView
|
|||
if @virtual_path
|
||||
[
|
||||
*Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
|
||||
Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context)
|
||||
Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context, dependencies: view_cache_dependencies)
|
||||
]
|
||||
else
|
||||
name
|
||||
|
|
|
@ -296,6 +296,24 @@ class CacheHelperOutputBufferTest < ActionController::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
class ViewCacheDependencyTest < ActionController::TestCase
|
||||
class NoDependenciesController < ActionController::Base
|
||||
end
|
||||
|
||||
class HasDependenciesController < ActionController::Base
|
||||
view_cache_dependency { "trombone" }
|
||||
view_cache_dependency { "flute" }
|
||||
end
|
||||
|
||||
def test_view_cache_dependencies_are_empty_by_default
|
||||
assert NoDependenciesController.view_cache_dependencies.empty?
|
||||
end
|
||||
|
||||
def test_view_cache_dependencies_are_listed_in_declaration_order
|
||||
assert_equal %w(trombone flute), HasDependenciesController.view_cache_dependencies
|
||||
end
|
||||
end
|
||||
|
||||
class DeprecatedPageCacheExtensionTest < ActiveSupport::TestCase
|
||||
def test_page_cache_extension_binds_default_static_extension
|
||||
deprecation_behavior = ActiveSupport::Deprecation.behavior
|
||||
|
|
|
@ -138,6 +138,20 @@ class TemplateDigestorTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_dependencies_via_options_results_in_different_digest
|
||||
digest_plain = digest("comments/_comment")
|
||||
digest_fridge = digest("comments/_comment", dependencies: ["fridge"])
|
||||
digest_phone = digest("comments/_comment", dependencies: ["phone"])
|
||||
digest_fridge_phone = digest("comments/_comment", dependencies: ["fridge", "phone"])
|
||||
|
||||
assert_not_equal digest_plain, digest_fridge
|
||||
assert_not_equal digest_plain, digest_phone
|
||||
assert_not_equal digest_plain, digest_fridge_phone
|
||||
assert_not_equal digest_fridge, digest_phone
|
||||
assert_not_equal digest_fridge, digest_fridge_phone
|
||||
assert_not_equal digest_phone, digest_fridge_phone
|
||||
end
|
||||
|
||||
private
|
||||
def assert_logged(message)
|
||||
old_logger = ActionView::Base.logger
|
||||
|
@ -164,8 +178,8 @@ class TemplateDigestorTest < ActionView::TestCase
|
|||
ActionView::Digestor.cache.clear
|
||||
end
|
||||
|
||||
def digest(template_name)
|
||||
ActionView::Digestor.digest(template_name, :html, FixtureFinder.new)
|
||||
def digest(template_name, options={})
|
||||
ActionView::Digestor.digest(template_name, :html, FixtureFinder.new, options)
|
||||
end
|
||||
|
||||
def change_template(template_name)
|
||||
|
|
Loading…
Reference in New Issue