actionview 4.2.11.1 → 6.0.4

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.

Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +201 -192
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +144 -37
  6. data/lib/action_view/buffers.rb +18 -1
  7. data/lib/action_view/cache_expiry.rb +53 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +54 -20
  10. data/lib/action_view/digestor.rb +88 -85
  11. data/lib/action_view/flows.rb +11 -12
  12. data/lib/action_view/gem_version.rb +6 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +241 -82
  15. data/lib/action_view/helpers/asset_url_helper.rb +171 -67
  16. data/lib/action_view/helpers/atom_feed_helper.rb +19 -17
  17. data/lib/action_view/helpers/cache_helper.rb +112 -42
  18. data/lib/action_view/helpers/capture_helper.rb +20 -13
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +230 -129
  23. data/lib/action_view/helpers/debug_helper.rb +7 -6
  24. data/lib/action_view/helpers/form_helper.rb +755 -129
  25. data/lib/action_view/helpers/form_options_helper.rb +130 -75
  26. data/lib/action_view/helpers/form_tag_helper.rb +116 -71
  27. data/lib/action_view/helpers/javascript_helper.rb +30 -14
  28. data/lib/action_view/helpers/number_helper.rb +84 -59
  29. data/lib/action_view/helpers/output_safety_helper.rb +36 -4
  30. data/lib/action_view/helpers/rendering_helper.rb +11 -8
  31. data/lib/action_view/helpers/sanitize_helper.rb +30 -31
  32. data/lib/action_view/helpers/tag_helper.rb +201 -75
  33. data/lib/action_view/helpers/tags/base.rb +138 -98
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +2 -1
  42. data/lib/action_view/helpers/tags/date_select.rb +37 -36
  43. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +2 -1
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
  50. data/lib/action_view/helpers/tags/label.rb +3 -2
  51. data/lib/action_view/helpers/tags/month_field.rb +2 -1
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  61. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  62. data/lib/action_view/helpers/tags/time_field.rb +2 -1
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +15 -16
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +2 -1
  68. data/lib/action_view/helpers/tags.rb +3 -1
  69. data/lib/action_view/helpers/text_helper.rb +56 -38
  70. data/lib/action_view/helpers/translation_helper.rb +91 -47
  71. data/lib/action_view/helpers/url_helper.rb +160 -105
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +65 -61
  74. data/lib/action_view/log_subscriber.rb +61 -10
  75. data/lib/action_view/lookup_context.rb +147 -89
  76. data/lib/action_view/model_naming.rb +3 -1
  77. data/lib/action_view/path_set.rb +28 -23
  78. data/lib/action_view/railtie.rb +62 -6
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +71 -13
  81. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +103 -0
  82. data/lib/action_view/renderer/partial_renderer.rb +239 -225
  83. data/lib/action_view/renderer/renderer.rb +22 -8
  84. data/lib/action_view/renderer/streaming_template_renderer.rb +54 -54
  85. data/lib/action_view/renderer/template_renderer.rb +79 -73
  86. data/lib/action_view/rendering.rb +68 -44
  87. data/lib/action_view/routing_url_for.rb +33 -22
  88. data/lib/action_view/tasks/cache_digests.rake +25 -0
  89. data/lib/action_view/template/error.rb +44 -29
  90. data/lib/action_view/template/handlers/builder.rb +12 -13
  91. data/lib/action_view/template/handlers/erb/erubi.rb +87 -0
  92. data/lib/action_view/template/handlers/erb.rb +24 -86
  93. data/lib/action_view/template/handlers/html.rb +11 -0
  94. data/lib/action_view/template/handlers/raw.rb +4 -4
  95. data/lib/action_view/template/handlers.rb +38 -8
  96. data/lib/action_view/template/html.rb +19 -10
  97. data/lib/action_view/template/inline.rb +22 -0
  98. data/lib/action_view/template/raw_file.rb +28 -0
  99. data/lib/action_view/template/resolver.rb +217 -193
  100. data/lib/action_view/template/sources/file.rb +17 -0
  101. data/lib/action_view/template/sources.rb +13 -0
  102. data/lib/action_view/template/text.rb +11 -10
  103. data/lib/action_view/template/types.rb +18 -18
  104. data/lib/action_view/template.rb +146 -90
  105. data/lib/action_view/test_case.rb +52 -32
  106. data/lib/action_view/testing/resolvers.rb +46 -34
  107. data/lib/action_view/unbound_template.rb +31 -0
  108. data/lib/action_view/version.rb +3 -1
  109. data/lib/action_view/view_paths.rb +48 -31
  110. data/lib/action_view.rb +11 -8
  111. data/lib/assets/compiled/rails-ujs.js +746 -0
  112. metadata +38 -29
  113. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  114. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,23 +1,26 @@
1
- require 'active_support/core_ext/module/attr_internal'
2
- require 'active_support/core_ext/module/attribute_accessors'
3
- require 'active_support/ordered_options'
4
- require 'action_view/log_subscriber'
5
- require 'action_view/helpers'
6
- require 'action_view/context'
7
- require 'action_view/template'
8
- require 'action_view/lookup_context'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attr_internal"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+ require "active_support/ordered_options"
6
+ require "active_support/deprecation"
7
+ require "action_view/log_subscriber"
8
+ require "action_view/helpers"
9
+ require "action_view/context"
10
+ require "action_view/template"
11
+ require "action_view/lookup_context"
9
12
 
10
13
  module ActionView #:nodoc:
11
14
  # = Action View Base
12
15
  #
13
16
  # Action View templates can be written in several ways.
14
- # If the template file has a <tt>.erb</tt> extension, then it uses the erubis[https://rubygems.org/gems/erubis]
17
+ # If the template file has a <tt>.erb</tt> extension, then it uses the erubi[https://rubygems.org/gems/erubi]
15
18
  # template system which can embed Ruby into an HTML document.
16
19
  # If the template file has a <tt>.builder</tt> extension, then Jim Weirich's Builder::XmlMarkup library is used.
17
20
  #
18
21
  # == ERB
19
22
  #
20
- # You trigger ERB by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
23
+ # You trigger ERB by using embeddings such as <tt><% %></tt>, <tt><% -%></tt>, and <tt><%= %></tt>. The <tt><%= %></tt> tag set is used when you want output. Consider the
21
24
  # following loop for names:
22
25
  #
23
26
  # <b>Names of all the people</b>
@@ -25,7 +28,7 @@ module ActionView #:nodoc:
25
28
  # Name: <%= person.name %><br/>
26
29
  # <% end %>
27
30
  #
28
- # The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
31
+ # The loop is setup in regular embedding tags <tt><% %></tt>, and the name is written using the output embedding tag <tt><%= %></tt>. Note that this
29
32
  # is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong:
30
33
  #
31
34
  # <%# WRONG %>
@@ -33,9 +36,9 @@ module ActionView #:nodoc:
33
36
  #
34
37
  # If you absolutely must write from within a function use +concat+.
35
38
  #
36
- # When on a line that only contains whitespaces except for the tag, <% %> suppress leading and trailing whitespace,
37
- # including the trailing newline. <% %> and <%- -%> are the same.
38
- # Note however that <%= %> and <%= -%> are different: only the latter removes trailing whitespaces.
39
+ # When on a line that only contains whitespaces except for the tag, <tt><% %></tt> suppresses leading and trailing whitespace,
40
+ # including the trailing newline. <tt><% %></tt> and <tt><%- -%></tt> are the same.
41
+ # Note however that <tt><%= %></tt> and <tt><%= -%></tt> are different: only the latter removes trailing whitespaces.
39
42
  #
40
43
  # === Using sub templates
41
44
  #
@@ -70,6 +73,14 @@ module ActionView #:nodoc:
70
73
  # Headline: <%= headline %>
71
74
  # First name: <%= person.first_name %>
72
75
  #
76
+ # The local variables passed to sub templates can be accessed as a hash using the <tt>local_assigns</tt> hash. This lets you access the
77
+ # variables as:
78
+ #
79
+ # Headline: <%= local_assigns[:headline] %>
80
+ #
81
+ # This is useful in cases where you aren't sure if the local variable has been assigned. Alternatively, you could also use
82
+ # <tt>defined? headline</tt> to first check if the variable has been assigned before using it.
83
+ #
73
84
  # === Template caching
74
85
  #
75
86
  # By default, Rails will compile each template to a method in order to render it. When you alter a template,
@@ -102,7 +113,7 @@ module ActionView #:nodoc:
102
113
  # <p>A product of Danish Design during the Winter of '79...</p>
103
114
  # </div>
104
115
  #
105
- # A full-length RSS example actually used on Basecamp:
116
+ # Here is a full-length RSS example actually used on Basecamp:
106
117
  #
107
118
  # xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
108
119
  # xml.channel do
@@ -126,38 +137,37 @@ module ActionView #:nodoc:
126
137
  # end
127
138
  # end
128
139
  #
129
- # For more information on Builder please consult the [source
130
- # code](https://github.com/jimweirich/builder).
140
+ # For more information on Builder please consult the {source
141
+ # code}[https://github.com/jimweirich/builder].
131
142
  class Base
132
143
  include Helpers, ::ERB::Util, Context
133
144
 
134
145
  # Specify the proc used to decorate input tags that refer to attributes with errors.
135
- cattr_accessor :field_error_proc
136
- @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
146
+ cattr_accessor :field_error_proc, default: Proc.new { |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
137
147
 
138
148
  # How to complete the streaming when an exception occurs.
139
149
  # This is our best guess: first try to close the attribute, then the tag.
140
- cattr_accessor :streaming_completion_on_exception
141
- @@streaming_completion_on_exception = %("><script>window.location = "/500.html"</script></html>)
150
+ cattr_accessor :streaming_completion_on_exception, default: %("><script>window.location = "/500.html"</script></html>)
142
151
 
143
152
  # Specify whether rendering within namespaced controllers should prefix
144
153
  # the partial paths for ActiveModel objects with the namespace.
145
154
  # (e.g., an Admin::PostsController would render @post using /admin/posts/_post.erb)
146
- cattr_accessor :prefix_partial_path_with_controller_namespace
147
- @@prefix_partial_path_with_controller_namespace = true
155
+ cattr_accessor :prefix_partial_path_with_controller_namespace, default: true
148
156
 
149
157
  # Specify default_formats that can be rendered.
150
158
  cattr_accessor :default_formats
151
159
 
152
160
  # Specify whether an error should be raised for missing translations
153
- cattr_accessor :raise_on_missing_translations
154
- @@raise_on_missing_translations = false
161
+ cattr_accessor :raise_on_missing_translations, default: false
162
+
163
+ # Specify whether submit_tag should automatically disable on click
164
+ cattr_accessor :automatically_disable_submit_tag, default: true
155
165
 
156
166
  class_attribute :_routes
157
167
  class_attribute :logger
158
168
 
159
169
  class << self
160
- delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
170
+ delegate :erb_trim_mode=, to: "ActionView::Template::Handlers::ERB"
161
171
 
162
172
  def cache_template_loading
163
173
  ActionView::Resolver.caching?
@@ -170,36 +180,133 @@ module ActionView #:nodoc:
170
180
  def xss_safe? #:nodoc:
171
181
  true
172
182
  end
183
+
184
+ def with_empty_template_cache # :nodoc:
185
+ subclass = Class.new(self) {
186
+ # We can't implement these as self.class because subclasses will
187
+ # share the same template cache as superclasses, so "changed?" won't work
188
+ # correctly.
189
+ define_method(:compiled_method_container) { subclass }
190
+ define_singleton_method(:compiled_method_container) { subclass }
191
+ }
192
+ end
193
+
194
+ def changed?(other) # :nodoc:
195
+ compiled_method_container != other.compiled_method_container
196
+ end
173
197
  end
174
198
 
175
- attr_accessor :view_renderer
199
+ attr_reader :view_renderer, :lookup_context
176
200
  attr_internal :config, :assigns
177
201
 
178
- delegate :lookup_context, :to => :view_renderer
179
- delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
202
+ delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, to: :lookup_context
180
203
 
181
204
  def assign(new_assigns) # :nodoc:
182
205
  @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
183
206
  end
184
207
 
185
- def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
208
+ # :stopdoc:
209
+
210
+ def self.build_lookup_context(context)
211
+ case context
212
+ when ActionView::Renderer
213
+ context.lookup_context
214
+ when Array
215
+ ActionView::LookupContext.new(context)
216
+ when ActionView::PathSet
217
+ ActionView::LookupContext.new(context)
218
+ when nil
219
+ ActionView::LookupContext.new([])
220
+ else
221
+ raise NotImplementedError, context.class.name
222
+ end
223
+ end
224
+
225
+ def self.empty
226
+ with_view_paths([])
227
+ end
228
+
229
+ def self.with_view_paths(view_paths, assigns = {}, controller = nil)
230
+ with_context ActionView::LookupContext.new(view_paths), assigns, controller
231
+ end
232
+
233
+ def self.with_context(context, assigns = {}, controller = nil)
234
+ new context, assigns, controller
235
+ end
236
+
237
+ NULL = Object.new
238
+
239
+ # :startdoc:
240
+
241
+ def initialize(lookup_context = nil, assigns = {}, controller = nil, formats = NULL) #:nodoc:
186
242
  @_config = ActiveSupport::InheritableOptions.new
187
243
 
188
- if context.is_a?(ActionView::Renderer)
189
- @view_renderer = context
244
+ unless formats == NULL
245
+ ActiveSupport::Deprecation.warn <<~eowarn.squish
246
+ Passing formats to ActionView::Base.new is deprecated
247
+ eowarn
248
+ end
249
+
250
+ case lookup_context
251
+ when ActionView::LookupContext
252
+ @lookup_context = lookup_context
190
253
  else
191
- lookup_context = context.is_a?(ActionView::LookupContext) ?
192
- context : ActionView::LookupContext.new(context)
193
- lookup_context.formats = formats if formats
194
- lookup_context.prefixes = controller._prefixes if controller
195
- @view_renderer = ActionView::Renderer.new(lookup_context)
254
+ ActiveSupport::Deprecation.warn <<~eowarn.squish
255
+ ActionView::Base instances should be constructed with a lookup context,
256
+ assignments, and a controller.
257
+ eowarn
258
+ @lookup_context = self.class.build_lookup_context(lookup_context)
196
259
  end
197
260
 
261
+ @view_renderer = ActionView::Renderer.new @lookup_context
262
+ @current_template = nil
263
+
264
+ @cache_hit = {}
198
265
  assign(assigns)
199
266
  assign_controller(controller)
200
267
  _prepare_context
201
268
  end
202
269
 
270
+ def _run(method, template, locals, buffer, &block)
271
+ _old_output_buffer, _old_virtual_path, _old_template = @output_buffer, @virtual_path, @current_template
272
+ @current_template = template
273
+ @output_buffer = buffer
274
+ send(method, locals, buffer, &block)
275
+ ensure
276
+ @output_buffer, @virtual_path, @current_template = _old_output_buffer, _old_virtual_path, _old_template
277
+ end
278
+
279
+ def compiled_method_container
280
+ if self.class == ActionView::Base
281
+ ActiveSupport::Deprecation.warn <<~eowarn.squish
282
+ ActionView::Base instances must implement `compiled_method_container`
283
+ or use the class method `with_empty_template_cache` for constructing
284
+ an ActionView::Base instance that has an empty cache.
285
+ eowarn
286
+ end
287
+
288
+ self.class
289
+ end
290
+
291
+ def in_rendering_context(options)
292
+ old_view_renderer = @view_renderer
293
+ old_lookup_context = @lookup_context
294
+
295
+ if !lookup_context.html_fallback_for_js && options[:formats]
296
+ formats = Array(options[:formats])
297
+ if formats == [:js]
298
+ formats << :html
299
+ end
300
+ @lookup_context = lookup_context.with_prepended_formats(formats)
301
+ @view_renderer = ActionView::Renderer.new @lookup_context
302
+ end
303
+
304
+ yield @view_renderer
305
+ ensure
306
+ @view_renderer = old_view_renderer
307
+ @lookup_context = old_lookup_context
308
+ end
309
+
203
310
  ActiveSupport.run_load_hooks(:action_view, self)
204
311
  end
205
312
  end
@@ -1,6 +1,23 @@
1
- require 'active_support/core_ext/string/output_safety'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/output_safety"
2
4
 
3
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
+ #
4
21
  class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
5
22
  def initialize(*)
6
23
  super
@@ -0,0 +1,53 @@
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
+ def dirs_to_watch
45
+ fs_paths = all_view_paths.grep(FileSystemResolver)
46
+ fs_paths.map(&:path).sort.uniq
47
+ end
48
+
49
+ def all_view_paths
50
+ ActionView::ViewPaths.all_view_paths.flat_map(&:paths)
51
+ end
52
+ end
53
+ end
@@ -1,23 +1,20 @@
1
- module ActionView
2
- module CompiledTemplates #:nodoc:
3
- # holds compiled template code
4
- end
1
+ # frozen_string_literal: true
5
2
 
3
+ module ActionView
6
4
  # = Action View Context
7
5
  #
8
6
  # Action View contexts are supplied to Action Controller to render a template.
9
7
  # The default Action View context is ActionView::Base.
10
8
  #
11
- # In order to work with ActionController, a Context must just include this module.
12
- # The initialization of the variables used by the context (@output_buffer, @view_flow,
13
- # and @virtual_path) is responsibility of the object that includes this module
14
- # (although you can call _prepare_context defined below).
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).
15
14
  module Context
16
- include CompiledTemplates
17
15
  attr_accessor :output_buffer, :view_flow
18
16
 
19
17
  # Prepares the context by setting the appropriate instance variables.
20
- # :api: plugin
21
18
  def _prepare_context
22
19
  @view_flow = OutputFlow.new
23
20
  @output_buffer = nil
@@ -27,8 +24,7 @@ module ActionView
27
24
  # Encapsulates the interaction with the view flow so it
28
25
  # returns the correct buffer on +yield+. This is usually
29
26
  # overwritten by helpers to add more behavior.
30
- # :api: plugin
31
- def _layout_for(name=nil)
27
+ def _layout_for(name = nil)
32
28
  name ||= :layout
33
29
  view_flow.get(name).html_safe
34
30
  end
@@ -1,22 +1,28 @@
1
- require 'thread_safe'
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+ require "action_view/path_set"
2
5
 
3
6
  module ActionView
4
7
  class DependencyTracker # :nodoc:
5
- @trackers = ThreadSafe::Cache.new
8
+ @trackers = Concurrent::Map.new
6
9
 
7
- def self.find_dependencies(name, template)
10
+ def self.find_dependencies(name, template, view_paths = nil)
8
11
  tracker = @trackers[template.handler]
12
+ return [] unless tracker
9
13
 
10
- if tracker.present?
11
- tracker.call(name, template)
12
- else
13
- []
14
- end
14
+ tracker.call(name, template, view_paths)
15
15
  end
16
16
 
17
17
  def self.register_tracker(extension, tracker)
18
18
  handler = Template.handler_for_extension(extension)
19
- @trackers[handler] = tracker
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
20
26
  end
21
27
 
22
28
  def self.remove_tracker(handler)
@@ -76,12 +82,22 @@ module ActionView
76
82
  (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
77
83
  /xm
78
84
 
79
- def self.call(name, template)
80
- new(name, template).dependencies
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
81
97
  end
82
98
 
83
- def initialize(name, template)
84
- @name, @template = name, template
99
+ def initialize(name, template, view_paths = nil)
100
+ @name, @template, @view_paths = name, template, view_paths
85
101
  end
86
102
 
87
103
  def dependencies
@@ -91,7 +107,6 @@ module ActionView
91
107
  attr_reader :name, :template
92
108
  private :name, :template
93
109
 
94
-
95
110
  private
96
111
  def source
97
112
  template.source
@@ -106,15 +121,20 @@ module ActionView
106
121
  render_calls = source.split(/\brender\b/).drop(1)
107
122
 
108
123
  render_calls.each do |arguments|
109
- arguments.scan(RENDER_ARGUMENTS) do
110
- add_dynamic_dependency(render_dependencies, Regexp.last_match[:dynamic])
111
- add_static_dependency(render_dependencies, Regexp.last_match[:static])
112
- end
124
+ add_dependencies(render_dependencies, arguments, LAYOUT_DEPENDENCY)
125
+ add_dependencies(render_dependencies, arguments, RENDER_ARGUMENTS)
113
126
  end
114
127
 
115
128
  render_dependencies.uniq
116
129
  end
117
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
+
118
138
  def add_dynamic_dependency(dependencies, dependency)
119
139
  if dependency
120
140
  dependencies << "#{dependency.pluralize}/#{dependency.singularize}"
@@ -123,7 +143,7 @@ module ActionView
123
143
 
124
144
  def add_static_dependency(dependencies, dependency)
125
145
  if dependency
126
- if dependency.include?('/')
146
+ if dependency.include?("/")
127
147
  dependencies << dependency
128
148
  else
129
149
  dependencies << "#{directory}/#{dependency}"
@@ -131,8 +151,22 @@ module ActionView
131
151
  end
132
152
  end
133
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
+
134
164
  def explicit_dependencies
135
- source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
165
+ dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
166
+
167
+ wildcards, explicits = dependencies.partition { |dependency| dependency[-1] == "*" }
168
+
169
+ (explicits + resolve_directories(wildcards)).uniq
136
170
  end
137
171
  end
138
172