aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJamis Buck <jamis@37signals.com>2013-01-08 11:20:47 -0700
committerJamis Buck <jamis@37signals.com>2013-01-08 11:20:47 -0700
commit70e684a681352e95fb990747ef6dd7183da333a8 (patch)
tree59ea8b888d7bc9c99cdcfab3ad4bf0f346244054
parentac86cbec82acee18a5066e00b98d1c20fc677a15 (diff)
downloadrails-70e684a681352e95fb990747ef6dd7183da333a8.tar.gz
rails-70e684a681352e95fb990747ef6dd7183da333a8.tar.bz2
rails-70e684a681352e95fb990747ef6dd7183da333a8.zip
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.)
-rw-r--r--actionpack/CHANGELOG.md5
-rw-r--r--actionpack/lib/action_controller/caching.rb18
-rw-r--r--actionpack/lib/action_view/digestor.rb21
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb2
-rw-r--r--actionpack/test/controller/caching_test.rb18
-rw-r--r--actionpack/test/template/digestor_test.rb18
6 files changed, 72 insertions, 10 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 6515a7b392..1a8f187979 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -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.
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 2892e093af..e31743bcc7 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -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)
diff --git a/actionpack/lib/action_view/digestor.rb b/actionpack/lib/action_view/digestor.rb
index f3f6b425a8..4507861dcc 100644
--- a/actionpack/lib/action_view/digestor.rb
+++ b/actionpack/lib/action_view/digestor.rb
@@ -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:
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index 995aa10afb..8fc78ea7fb 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -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
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 2428cd7433..eb5eeb0423 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -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
diff --git a/actionpack/test/template/digestor_test.rb b/actionpack/test/template/digestor_test.rb
index 02b1fd87a8..849e2981a6 100644
--- a/actionpack/test/template/digestor_test.rb
+++ b/actionpack/test/template/digestor_test.rb
@@ -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)