Merge pull request #20904 from kaspth/wildcard-template-dependencies

Add wildcard template dependencies.
This commit is contained in:
Kasper Timm Hansen 2015-07-27 20:27:57 +02:00
commit c5583cd79c
9 changed files with 121 additions and 12 deletions

View File

@ -1,3 +1,21 @@
* Add wildcard matching to explicit dependencies.
Turns:
```erb
<% # Template Dependency: recordings/threads/events/subscribers_changed %>
<% # Template Dependency: recordings/threads/events/completed %>
<% # Template Dependency: recordings/threads/events/uncompleted %>
```
Into:
```erb
<% # Template Dependency: recordings/threads/events/* %>
```
*Kasper Timm Hansen*
* Allow defining explicit collection caching using a `# Template Collection: ...`
directive inside templates.

View File

@ -1,16 +1,18 @@
require 'thread_safe'
require 'action_view/path_set'
module ActionView
class DependencyTracker # :nodoc:
@trackers = ThreadSafe::Cache.new
def self.find_dependencies(name, template)
def self.find_dependencies(name, template, view_paths = nil)
tracker = @trackers[template.handler]
return [] unless tracker.present?
if tracker.present?
tracker.call(name, template)
if tracker.respond_to?(:supports_view_paths?) && tracker.supports_view_paths?
tracker.call(name, template, view_paths)
else
[]
tracker.call(name, template)
end
end
@ -82,12 +84,16 @@ module ActionView
(?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
/xm
def self.call(name, template)
new(name, template).dependencies
def self.supports_view_paths? # :nodoc:
true
end
def initialize(name, template)
@name, @template = name, template
def self.call(name, template, view_paths = nil)
new(name, template, view_paths).dependencies
end
def initialize(name, template, view_paths = nil)
@name, @template, @view_paths = name, template, view_paths
end
def dependencies
@ -142,8 +148,22 @@ module ActionView
end
end
def resolve_directories(wildcard_dependencies)
return [] unless @view_paths
wildcard_dependencies.each_with_object([]) do |query, templates|
@view_paths.find_all_with_query(query).each do |template|
templates << "#{File.dirname(query)}/#{File.basename(template).split('.').first}"
end
end
end
def explicit_dependencies
source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
wildcards, explicits = dependencies.partition { |dependency| dependency[-1] == '*' }
(explicits + resolve_directories(wildcards)).uniq
end
end

View File

@ -72,7 +72,7 @@ module ActionView
end
def dependencies
DependencyTracker.find_dependencies(name, template)
DependencyTracker.find_dependencies(name, template, finder.view_paths)
rescue ActionView::MissingTemplate
logger.try :error, " '#{name}' file doesn't exist, so no dependencies"
[]

View File

@ -98,7 +98,19 @@ module ActionView
# <%# Template Dependency: todolists/todolist %>
# <%= render_sortable_todolists @project.todolists %>
#
# The pattern used to match these is <tt>/# Template Dependency: (\S+)/</tt>,
# In some cases, like a single table inheritance setup, you might have
# a bunch of explicit dependencies. Instead of writing every template out,
# you can use a wildcard to match any template in a directory:
#
# <%# Template Dependency: events/* %>
# <%= render_categorizable_events @person.events %>
#
# This marks every template in the directory as a dependency. To find those
# templates, the wildcard path must be absolutely defined from app/views or paths
# otherwise added with +prepend_view_path+ or +append_view_path+.
# This way the wildcard for `app/views/recordings/events` would be `recordings/events/*` etc.
#
# The pattern used to match explicit dependencies is <tt>/# Template Dependency: (\S+)/</tt>,
# so it's important that you type it out just so.
# You can only declare one template dependency per line.
#

View File

@ -61,6 +61,15 @@ module ActionView #:nodoc:
find_all(path, prefixes, *args).any?
end
def find_all_with_query(query) # :nodoc:
paths.each do |resolver|
templates = resolver.find_all_with_query(query)
return templates unless templates.empty?
end
[]
end
private
def typecast(paths)

View File

@ -52,6 +52,7 @@ module ActionView
def initialize
@data = SmallCache.new(&KEY_BLOCK)
@query_cache = SmallCache.new
end
# Cache the templates returned by the block
@ -70,8 +71,17 @@ module ActionView
end
end
def cache_query(query) # :nodoc:
if Resolver.caching?
@query_cache[query] ||= canonical_no_templates(yield)
else
yield
end
end
def clear
@data.clear
@query_cache.clear
end
private
@ -116,6 +126,10 @@ module ActionView
end
end
def find_all_with_query(query) # :nodoc:
@cache.cache_query(query) { find_template_paths(File.join(@path, query)) }
end
private
delegate :caching?, to: :class

View File

@ -0,0 +1 @@
<% # Template Dependency: events/* %>

View File

@ -1,5 +1,6 @@
require 'abstract_unit'
require 'fileutils'
require 'action_view/dependency_tracker'
class FixtureTemplate
attr_reader :source, :handler
@ -15,12 +16,13 @@ end
class FixtureFinder
FIXTURES_DIR = "#{File.dirname(__FILE__)}/../fixtures/digestor"
attr_reader :details
attr_reader :details, :view_paths
attr_accessor :formats
attr_accessor :variants
def initialize
@details = {}
@view_paths = ActionView::PathSet.new(['digestor'])
@formats = []
@variants = []
end
@ -75,6 +77,34 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
def test_explicit_dependency_wildcard
assert_digest_difference("events/index") do
change_template("events/_completed")
end
end
def test_explicit_dependency_wildcard_picks_up_added_file
old_caching, ActionView::Resolver.caching = ActionView::Resolver.caching, false
assert_digest_difference("events/index") do
add_template("events/_uncompleted")
end
ensure
remove_template("events/_uncompleted")
ActionView::Resolver.caching = old_caching
end
def test_explicit_dependency_wildcard_picks_up_removed_file
old_caching, ActionView::Resolver.caching = ActionView::Resolver.caching, false
add_template("events/_subscribers_changed")
assert_digest_difference("events/index") do
remove_template("events/_subscribers_changed")
end
ensure
ActionView::Resolver.caching = old_caching
end
def test_second_level_dependency
assert_digest_difference("messages/show") do
change_template("comments/_comments")
@ -319,4 +349,9 @@ class TemplateDigestorTest < ActionView::TestCase
f.write "\nTHIS WAS CHANGED!"
end
end
alias_method :add_template, :change_template
def remove_template(template_name)
File.delete("digestor/#{template_name}.html.erb")
end
end