mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #20904 from kaspth/wildcard-template-dependencies
Add wildcard template dependencies.
This commit is contained in:
commit
c5583cd79c
9 changed files with 121 additions and 12 deletions
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
[]
|
||||
|
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
0
actionview/test/fixtures/digestor/events/_completed.html.erb
vendored
Normal file
0
actionview/test/fixtures/digestor/events/_completed.html.erb
vendored
Normal file
1
actionview/test/fixtures/digestor/events/index.html.erb
vendored
Normal file
1
actionview/test/fixtures/digestor/events/index.html.erb
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<% # Template Dependency: events/* %>
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue