actionview 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +271 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +40 -0
- data/lib/action_view.rb +98 -0
- data/lib/action_view/base.rb +312 -0
- data/lib/action_view/buffers.rb +67 -0
- data/lib/action_view/cache_expiry.rb +54 -0
- data/lib/action_view/context.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +175 -0
- data/lib/action_view/digestor.rb +126 -0
- data/lib/action_view/flows.rb +76 -0
- data/lib/action_view/gem_version.rb +17 -0
- data/lib/action_view/helpers.rb +66 -0
- data/lib/action_view/helpers/active_model_helper.rb +55 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +488 -0
- data/lib/action_view/helpers/asset_url_helper.rb +470 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
- data/lib/action_view/helpers/cache_helper.rb +271 -0
- data/lib/action_view/helpers/capture_helper.rb +216 -0
- data/lib/action_view/helpers/controller_helper.rb +36 -0
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +35 -0
- data/lib/action_view/helpers/date_helper.rb +1200 -0
- data/lib/action_view/helpers/debug_helper.rb +36 -0
- data/lib/action_view/helpers/form_helper.rb +2569 -0
- data/lib/action_view/helpers/form_options_helper.rb +896 -0
- data/lib/action_view/helpers/form_tag_helper.rb +920 -0
- data/lib/action_view/helpers/javascript_helper.rb +95 -0
- data/lib/action_view/helpers/number_helper.rb +456 -0
- data/lib/action_view/helpers/output_safety_helper.rb +70 -0
- data/lib/action_view/helpers/rendering_helper.rb +101 -0
- data/lib/action_view/helpers/sanitize_helper.rb +171 -0
- data/lib/action_view/helpers/tag_helper.rb +314 -0
- data/lib/action_view/helpers/tags.rb +44 -0
- data/lib/action_view/helpers/tags/base.rb +196 -0
- data/lib/action_view/helpers/tags/check_box.rb +66 -0
- data/lib/action_view/helpers/tags/checkable.rb +18 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +36 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +119 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
- data/lib/action_view/helpers/tags/collection_select.rb +30 -0
- data/lib/action_view/helpers/tags/color_field.rb +27 -0
- data/lib/action_view/helpers/tags/date_field.rb +15 -0
- data/lib/action_view/helpers/tags/date_select.rb +74 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +32 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +21 -0
- data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
- data/lib/action_view/helpers/tags/email_field.rb +10 -0
- data/lib/action_view/helpers/tags/file_field.rb +10 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +31 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +10 -0
- data/lib/action_view/helpers/tags/label.rb +81 -0
- data/lib/action_view/helpers/tags/month_field.rb +15 -0
- data/lib/action_view/helpers/tags/number_field.rb +20 -0
- data/lib/action_view/helpers/tags/password_field.rb +14 -0
- data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
- data/lib/action_view/helpers/tags/radio_button.rb +33 -0
- data/lib/action_view/helpers/tags/range_field.rb +10 -0
- data/lib/action_view/helpers/tags/search_field.rb +27 -0
- data/lib/action_view/helpers/tags/select.rb +43 -0
- data/lib/action_view/helpers/tags/tel_field.rb +10 -0
- data/lib/action_view/helpers/tags/text_area.rb +24 -0
- data/lib/action_view/helpers/tags/text_field.rb +34 -0
- data/lib/action_view/helpers/tags/time_field.rb +15 -0
- data/lib/action_view/helpers/tags/time_select.rb +10 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +22 -0
- data/lib/action_view/helpers/tags/translator.rb +39 -0
- data/lib/action_view/helpers/tags/url_field.rb +10 -0
- data/lib/action_view/helpers/tags/week_field.rb +15 -0
- data/lib/action_view/helpers/text_helper.rb +486 -0
- data/lib/action_view/helpers/translation_helper.rb +145 -0
- data/lib/action_view/helpers/url_helper.rb +676 -0
- data/lib/action_view/layouts.rb +433 -0
- data/lib/action_view/locale/en.yml +56 -0
- data/lib/action_view/log_subscriber.rb +96 -0
- data/lib/action_view/lookup_context.rb +316 -0
- data/lib/action_view/model_naming.rb +14 -0
- data/lib/action_view/path_set.rb +95 -0
- data/lib/action_view/railtie.rb +105 -0
- data/lib/action_view/record_identifier.rb +112 -0
- data/lib/action_view/renderer/abstract_renderer.rb +108 -0
- data/lib/action_view/renderer/partial_renderer.rb +563 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +103 -0
- data/lib/action_view/renderer/renderer.rb +68 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +105 -0
- data/lib/action_view/renderer/template_renderer.rb +108 -0
- data/lib/action_view/rendering.rb +171 -0
- data/lib/action_view/routing_url_for.rb +146 -0
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template.rb +393 -0
- data/lib/action_view/template/error.rb +161 -0
- data/lib/action_view/template/handlers.rb +92 -0
- data/lib/action_view/template/handlers/builder.rb +25 -0
- data/lib/action_view/template/handlers/erb.rb +84 -0
- data/lib/action_view/template/handlers/erb/erubi.rb +87 -0
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +11 -0
- data/lib/action_view/template/html.rb +43 -0
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +28 -0
- data/lib/action_view/template/resolver.rb +394 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/text.rb +35 -0
- data/lib/action_view/template/types.rb +57 -0
- data/lib/action_view/test_case.rb +300 -0
- data/lib/action_view/testing/resolvers.rb +67 -0
- data/lib/action_view/unbound_template.rb +32 -0
- data/lib/action_view/version.rb +10 -0
- data/lib/action_view/view_paths.rb +129 -0
- data/lib/assets/compiled/rails-ujs.js +746 -0
- metadata +260 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/output_safety"
|
4
|
+
|
5
|
+
module ActionView
|
6
|
+
# Used as a buffer for views
|
7
|
+
#
|
8
|
+
# The main difference between this and ActiveSupport::SafeBuffer
|
9
|
+
# is for the methods `<<` and `safe_expr_append=` the inputs are
|
10
|
+
# checked for nil before they are assigned and `to_s` is called on
|
11
|
+
# the input. For example:
|
12
|
+
#
|
13
|
+
# obuf = ActionView::OutputBuffer.new "hello"
|
14
|
+
# obuf << 5
|
15
|
+
# puts obuf # => "hello5"
|
16
|
+
#
|
17
|
+
# sbuf = ActiveSupport::SafeBuffer.new "hello"
|
18
|
+
# sbuf << 5
|
19
|
+
# puts sbuf # => "hello\u0005"
|
20
|
+
#
|
21
|
+
class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
|
22
|
+
def initialize(*)
|
23
|
+
super
|
24
|
+
encode!
|
25
|
+
end
|
26
|
+
|
27
|
+
def <<(value)
|
28
|
+
return self if value.nil?
|
29
|
+
super(value.to_s)
|
30
|
+
end
|
31
|
+
alias :append= :<<
|
32
|
+
|
33
|
+
def safe_expr_append=(val)
|
34
|
+
return self if val.nil?
|
35
|
+
safe_concat val.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
alias :safe_append= :safe_concat
|
39
|
+
end
|
40
|
+
|
41
|
+
class StreamingBuffer #:nodoc:
|
42
|
+
def initialize(block)
|
43
|
+
@block = block
|
44
|
+
end
|
45
|
+
|
46
|
+
def <<(value)
|
47
|
+
value = value.to_s
|
48
|
+
value = ERB::Util.h(value) unless value.html_safe?
|
49
|
+
@block.call(value)
|
50
|
+
end
|
51
|
+
alias :concat :<<
|
52
|
+
alias :append= :<<
|
53
|
+
|
54
|
+
def safe_concat(value)
|
55
|
+
@block.call(value.to_s)
|
56
|
+
end
|
57
|
+
alias :safe_append= :safe_concat
|
58
|
+
|
59
|
+
def html_safe?
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def html_safe
|
64
|
+
self
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
class CacheExpiry
|
5
|
+
class Executor
|
6
|
+
def initialize(watcher:)
|
7
|
+
@cache_expiry = CacheExpiry.new(watcher: watcher)
|
8
|
+
end
|
9
|
+
|
10
|
+
def before(target)
|
11
|
+
@cache_expiry.clear_cache_if_necessary
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(watcher:)
|
16
|
+
@watched_dirs = nil
|
17
|
+
@watcher_class = watcher
|
18
|
+
@watcher = nil
|
19
|
+
@mutex = Mutex.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def clear_cache_if_necessary
|
23
|
+
@mutex.synchronize do
|
24
|
+
watched_dirs = dirs_to_watch
|
25
|
+
return if watched_dirs.empty?
|
26
|
+
|
27
|
+
if watched_dirs != @watched_dirs
|
28
|
+
@watched_dirs = watched_dirs
|
29
|
+
@watcher = @watcher_class.new([], watched_dirs) do
|
30
|
+
clear_cache
|
31
|
+
end
|
32
|
+
@watcher.execute
|
33
|
+
else
|
34
|
+
@watcher.execute_if_updated
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def clear_cache
|
40
|
+
ActionView::LookupContext::DetailsKey.clear
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def dirs_to_watch
|
46
|
+
fs_paths = all_view_paths.grep(FileSystemResolver)
|
47
|
+
fs_paths.map(&:path).sort.uniq
|
48
|
+
end
|
49
|
+
|
50
|
+
def all_view_paths
|
51
|
+
ActionView::ViewPaths.all_view_paths.flat_map(&:paths)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
# = Action View Context
|
5
|
+
#
|
6
|
+
# Action View contexts are supplied to Action Controller to render a template.
|
7
|
+
# The default Action View context is ActionView::Base.
|
8
|
+
#
|
9
|
+
# In order to work with Action Controller, a Context must just include this
|
10
|
+
# module. The initialization of the variables used by the context
|
11
|
+
# (@output_buffer, @view_flow, and @virtual_path) is responsibility of the
|
12
|
+
# object that includes this module (although you can call _prepare_context
|
13
|
+
# defined below).
|
14
|
+
module Context
|
15
|
+
attr_accessor :output_buffer, :view_flow
|
16
|
+
|
17
|
+
# Prepares the context by setting the appropriate instance variables.
|
18
|
+
def _prepare_context
|
19
|
+
@view_flow = OutputFlow.new
|
20
|
+
@output_buffer = nil
|
21
|
+
@virtual_path = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# Encapsulates the interaction with the view flow so it
|
25
|
+
# returns the correct buffer on +yield+. This is usually
|
26
|
+
# overwritten by helpers to add more behavior.
|
27
|
+
def _layout_for(name = nil)
|
28
|
+
name ||= :layout
|
29
|
+
view_flow.get(name).html_safe
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "concurrent/map"
|
4
|
+
require "action_view/path_set"
|
5
|
+
|
6
|
+
module ActionView
|
7
|
+
class DependencyTracker # :nodoc:
|
8
|
+
@trackers = Concurrent::Map.new
|
9
|
+
|
10
|
+
def self.find_dependencies(name, template, view_paths = nil)
|
11
|
+
tracker = @trackers[template.handler]
|
12
|
+
return [] unless tracker
|
13
|
+
|
14
|
+
tracker.call(name, template, view_paths)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.register_tracker(extension, tracker)
|
18
|
+
handler = Template.handler_for_extension(extension)
|
19
|
+
if tracker.respond_to?(:supports_view_paths?)
|
20
|
+
@trackers[handler] = tracker
|
21
|
+
else
|
22
|
+
@trackers[handler] = lambda { |name, template, _|
|
23
|
+
tracker.call(name, template)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.remove_tracker(handler)
|
29
|
+
@trackers.delete(handler)
|
30
|
+
end
|
31
|
+
|
32
|
+
class ERBTracker # :nodoc:
|
33
|
+
EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
|
34
|
+
|
35
|
+
# A valid ruby identifier - suitable for class, method and specially variable names
|
36
|
+
IDENTIFIER = /
|
37
|
+
[[:alpha:]_] # at least one uppercase letter, lowercase letter or underscore
|
38
|
+
[[:word:]]* # followed by optional letters, numbers or underscores
|
39
|
+
/x
|
40
|
+
|
41
|
+
# Any kind of variable name. e.g. @instance, @@class, $global or local.
|
42
|
+
# Possibly following a method call chain
|
43
|
+
VARIABLE_OR_METHOD_CHAIN = /
|
44
|
+
(?:\$|@{1,2})? # optional global, instance or class variable indicator
|
45
|
+
(?:#{IDENTIFIER}\.)* # followed by an optional chain of zero-argument method calls
|
46
|
+
(?<dynamic>#{IDENTIFIER}) # and a final valid identifier, captured as DYNAMIC
|
47
|
+
/x
|
48
|
+
|
49
|
+
# A simple string literal. e.g. "School's out!"
|
50
|
+
STRING = /
|
51
|
+
(?<quote>['"]) # an opening quote
|
52
|
+
(?<static>.*?) # with anything inside, captured as STATIC
|
53
|
+
\k<quote> # and a matching closing quote
|
54
|
+
/x
|
55
|
+
|
56
|
+
# Part of any hash containing the :partial key
|
57
|
+
PARTIAL_HASH_KEY = /
|
58
|
+
(?:\bpartial:|:partial\s*=>) # partial key in either old or new style hash syntax
|
59
|
+
\s* # followed by optional spaces
|
60
|
+
/x
|
61
|
+
|
62
|
+
# Part of any hash containing the :layout key
|
63
|
+
LAYOUT_HASH_KEY = /
|
64
|
+
(?:\blayout:|:layout\s*=>) # layout key in either old or new style hash syntax
|
65
|
+
\s* # followed by optional spaces
|
66
|
+
/x
|
67
|
+
|
68
|
+
# Matches:
|
69
|
+
# partial: "comments/comment", collection: @all_comments => "comments/comment"
|
70
|
+
# (object: @single_comment, partial: "comments/comment") => "comments/comment"
|
71
|
+
#
|
72
|
+
# "comments/comments"
|
73
|
+
# 'comments/comments'
|
74
|
+
# ('comments/comments')
|
75
|
+
#
|
76
|
+
# (@topic) => "topics/topic"
|
77
|
+
# topics => "topics/topic"
|
78
|
+
# (message.topics) => "topics/topic"
|
79
|
+
RENDER_ARGUMENTS = /\A
|
80
|
+
(?:\s*\(?\s*) # optional opening paren surrounded by spaces
|
81
|
+
(?:.*?#{PARTIAL_HASH_KEY}|#{LAYOUT_HASH_KEY})? # optional hash, up to the partial or layout key declaration
|
82
|
+
(?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
|
83
|
+
/xm
|
84
|
+
|
85
|
+
LAYOUT_DEPENDENCY = /\A
|
86
|
+
(?:\s*\(?\s*) # optional opening paren surrounded by spaces
|
87
|
+
(?:.*?#{LAYOUT_HASH_KEY}) # check if the line has layout key declaration
|
88
|
+
(?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
|
89
|
+
/xm
|
90
|
+
|
91
|
+
def self.supports_view_paths? # :nodoc:
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.call(name, template, view_paths = nil)
|
96
|
+
new(name, template, view_paths).dependencies
|
97
|
+
end
|
98
|
+
|
99
|
+
def initialize(name, template, view_paths = nil)
|
100
|
+
@name, @template, @view_paths = name, template, view_paths
|
101
|
+
end
|
102
|
+
|
103
|
+
def dependencies
|
104
|
+
render_dependencies + explicit_dependencies
|
105
|
+
end
|
106
|
+
|
107
|
+
attr_reader :name, :template
|
108
|
+
private :name, :template
|
109
|
+
|
110
|
+
private
|
111
|
+
def source
|
112
|
+
template.source
|
113
|
+
end
|
114
|
+
|
115
|
+
def directory
|
116
|
+
name.split("/")[0..-2].join("/")
|
117
|
+
end
|
118
|
+
|
119
|
+
def render_dependencies
|
120
|
+
render_dependencies = []
|
121
|
+
render_calls = source.split(/\brender\b/).drop(1)
|
122
|
+
|
123
|
+
render_calls.each do |arguments|
|
124
|
+
add_dependencies(render_dependencies, arguments, LAYOUT_DEPENDENCY)
|
125
|
+
add_dependencies(render_dependencies, arguments, RENDER_ARGUMENTS)
|
126
|
+
end
|
127
|
+
|
128
|
+
render_dependencies.uniq
|
129
|
+
end
|
130
|
+
|
131
|
+
def add_dependencies(render_dependencies, arguments, pattern)
|
132
|
+
arguments.scan(pattern) do
|
133
|
+
add_dynamic_dependency(render_dependencies, Regexp.last_match[:dynamic])
|
134
|
+
add_static_dependency(render_dependencies, Regexp.last_match[:static])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def add_dynamic_dependency(dependencies, dependency)
|
139
|
+
if dependency
|
140
|
+
dependencies << "#{dependency.pluralize}/#{dependency.singularize}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def add_static_dependency(dependencies, dependency)
|
145
|
+
if dependency
|
146
|
+
if dependency.include?("/")
|
147
|
+
dependencies << dependency
|
148
|
+
else
|
149
|
+
dependencies << "#{directory}/#{dependency}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def resolve_directories(wildcard_dependencies)
|
155
|
+
return [] unless @view_paths
|
156
|
+
|
157
|
+
wildcard_dependencies.flat_map { |query, templates|
|
158
|
+
@view_paths.find_all_with_query(query).map do |template|
|
159
|
+
"#{File.dirname(query)}/#{File.basename(template).split('.').first}"
|
160
|
+
end
|
161
|
+
}.sort
|
162
|
+
end
|
163
|
+
|
164
|
+
def explicit_dependencies
|
165
|
+
dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
|
166
|
+
|
167
|
+
wildcards, explicits = dependencies.partition { |dependency| dependency[-1] == "*" }
|
168
|
+
|
169
|
+
(explicits + resolve_directories(wildcards)).uniq
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
register_tracker :erb, ERBTracker
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view/dependency_tracker"
|
4
|
+
|
5
|
+
module ActionView
|
6
|
+
class Digestor
|
7
|
+
@@digest_mutex = Mutex.new
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# Supported options:
|
11
|
+
#
|
12
|
+
# * <tt>name</tt> - Template name
|
13
|
+
# * <tt>format</tt> - Template format
|
14
|
+
# * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
|
15
|
+
# * <tt>dependencies</tt> - An array of dependent views
|
16
|
+
def digest(name:, format: nil, finder:, dependencies: nil)
|
17
|
+
if dependencies.nil? || dependencies.empty?
|
18
|
+
cache_key = "#{name}.#{format}"
|
19
|
+
else
|
20
|
+
cache_key = [ name, format, dependencies ].flatten.compact.join(".")
|
21
|
+
end
|
22
|
+
|
23
|
+
# this is a correctly done double-checked locking idiom
|
24
|
+
# (Concurrent::Map's lookups have volatile semantics)
|
25
|
+
finder.digest_cache[cache_key] || @@digest_mutex.synchronize do
|
26
|
+
finder.digest_cache.fetch(cache_key) do # re-check under lock
|
27
|
+
partial = name.include?("/_")
|
28
|
+
root = tree(name, finder, partial)
|
29
|
+
dependencies.each do |injected_dep|
|
30
|
+
root.children << Injected.new(injected_dep, nil, nil)
|
31
|
+
end if dependencies
|
32
|
+
finder.digest_cache[cache_key] = root.digest(finder)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def logger
|
38
|
+
ActionView::Base.logger || NullLogger
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create a dependency tree for template named +name+.
|
42
|
+
def tree(name, finder, partial = false, seen = {})
|
43
|
+
logical_name = name.gsub(%r|/_|, "/")
|
44
|
+
|
45
|
+
if template = find_template(finder, logical_name, [], partial, [])
|
46
|
+
if node = seen[template.identifier] # handle cycles in the tree
|
47
|
+
node
|
48
|
+
else
|
49
|
+
node = seen[template.identifier] = Node.create(name, logical_name, template, partial)
|
50
|
+
|
51
|
+
deps = DependencyTracker.find_dependencies(name, template, finder.view_paths)
|
52
|
+
deps.uniq { |n| n.gsub(%r|/_|, "/") }.each do |dep_file|
|
53
|
+
node.children << tree(dep_file, finder, true, seen)
|
54
|
+
end
|
55
|
+
node
|
56
|
+
end
|
57
|
+
else
|
58
|
+
unless name.include?("#") # Dynamic template partial names can never be tracked
|
59
|
+
logger.error " Couldn't find template for digesting: #{name}"
|
60
|
+
end
|
61
|
+
|
62
|
+
seen[name] ||= Missing.new(name, logical_name, nil)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def find_template(finder, name, prefixes, partial, keys)
|
68
|
+
finder.disable_cache do
|
69
|
+
finder.find_all(name, prefixes, partial, keys).first
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Node
|
75
|
+
attr_reader :name, :logical_name, :template, :children
|
76
|
+
|
77
|
+
def self.create(name, logical_name, template, partial)
|
78
|
+
klass = partial ? Partial : Node
|
79
|
+
klass.new(name, logical_name, template, [])
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize(name, logical_name, template, children = [])
|
83
|
+
@name = name
|
84
|
+
@logical_name = logical_name
|
85
|
+
@template = template
|
86
|
+
@children = children
|
87
|
+
end
|
88
|
+
|
89
|
+
def digest(finder, stack = [])
|
90
|
+
ActiveSupport::Digest.hexdigest("#{template.source}-#{dependency_digest(finder, stack)}")
|
91
|
+
end
|
92
|
+
|
93
|
+
def dependency_digest(finder, stack)
|
94
|
+
children.map do |node|
|
95
|
+
if stack.include?(node)
|
96
|
+
false
|
97
|
+
else
|
98
|
+
finder.digest_cache[node.name] ||= begin
|
99
|
+
stack.push node
|
100
|
+
node.digest(finder, stack).tap { stack.pop }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end.join("-")
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_dep_map
|
107
|
+
children.any? ? { name => children.map(&:to_dep_map) } : name
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class Partial < Node; end
|
112
|
+
|
113
|
+
class Missing < Node
|
114
|
+
def digest(finder, _ = []) "" end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Injected < Node
|
118
|
+
def digest(finder, _ = []) name end
|
119
|
+
end
|
120
|
+
|
121
|
+
class NullLogger
|
122
|
+
def self.debug(_); end
|
123
|
+
def self.error(_); end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|