actionview 4.2.11.1 → 6.1.5

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +232 -186
  3. data/MIT-LICENSE +1 -2
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +115 -39
  6. data/lib/action_view/buffers.rb +18 -1
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -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 +282 -83
  15. data/lib/action_view/helpers/asset_url_helper.rb +175 -69
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +107 -43
  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 +232 -130
  23. data/lib/action_view/helpers/debug_helper.rb +7 -6
  24. data/lib/action_view/helpers/form_helper.rb +808 -146
  25. data/lib/action_view/helpers/form_options_helper.rb +124 -78
  26. data/lib/action_view/helpers/form_tag_helper.rb +120 -74
  27. data/lib/action_view/helpers/javascript_helper.rb +33 -17
  28. data/lib/action_view/helpers/number_helper.rb +87 -62
  29. data/lib/action_view/helpers/output_safety_helper.rb +36 -4
  30. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  31. data/lib/action_view/helpers/sanitize_helper.rb +30 -31
  32. data/lib/action_view/helpers/tag_helper.rb +269 -68
  33. data/lib/action_view/helpers/tags/base.rb +141 -97
  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 +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  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 +6 -0
  50. data/lib/action_view/helpers/tags/label.rb +7 -2
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  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 +3 -2
  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 +3 -2
  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 +150 -68
  71. data/lib/action_view/helpers/url_helper.rb +284 -117
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +77 -10
  75. data/lib/action_view/lookup_context.rb +134 -91
  76. data/lib/action_view/model_naming.rb +3 -1
  77. data/lib/action_view/path_set.rb +26 -24
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +151 -14
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +55 -303
  85. data/lib/action_view/renderer/renderer.rb +66 -9
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +82 -73
  88. data/lib/action_view/rendering.rb +71 -45
  89. data/lib/action_view/routing_url_for.rb +34 -23
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +12 -8
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +263 -197
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +108 -92
  108. data/lib/action_view/test_case.rb +66 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +73 -58
  113. data/lib/action_view.rb +14 -8
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +42 -29
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
data/README.rdoc CHANGED
@@ -5,35 +5,36 @@ view helpers that assist when building HTML forms, Atom feeds and more.
5
5
  Template formats that Action View handles are ERB (embedded Ruby, typically
6
6
  used to inline short Ruby snippets inside HTML), and XML Builder.
7
7
 
8
+ You can read more about Action View in the {Action View Overview}[https://edgeguides.rubyonrails.org/action_view_overview.html] guide.
9
+
8
10
  == Download and installation
9
11
 
10
12
  The latest version of Action View can be installed with RubyGems:
11
13
 
12
- % [sudo] gem install actionview
14
+ $ gem install actionview
13
15
 
14
- Source code can be downloaded as part of the Rails project on GitHub
16
+ Source code can be downloaded as part of the Rails project on GitHub:
15
17
 
16
- * https://github.com/rails/rails/tree/4-2-stable/actionview
18
+ * https://github.com/rails/rails/tree/main/actionview
17
19
 
18
20
 
19
21
  == License
20
22
 
21
23
  Action View is released under the MIT license:
22
24
 
23
- * http://www.opensource.org/licenses/MIT
25
+ * https://opensource.org/licenses/MIT
24
26
 
25
27
 
26
28
  == Support
27
29
 
28
30
  API documentation is at
29
31
 
30
- * http://api.rubyonrails.org
32
+ * https://api.rubyonrails.org
31
33
 
32
- Bug reports can be filed for the Ruby on Rails project here:
34
+ Bug reports for the Ruby on Rails project can be filed here:
33
35
 
34
36
  * https://github.com/rails/rails/issues
35
37
 
36
38
  Feature requests should be discussed on the rails-core mailing list here:
37
39
 
38
- * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
39
-
40
+ * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -1,23 +1,25 @@
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 "action_view/log_subscriber"
7
+ require "action_view/helpers"
8
+ require "action_view/context"
9
+ require "action_view/template"
10
+ require "action_view/lookup_context"
9
11
 
10
12
  module ActionView #:nodoc:
11
13
  # = Action View Base
12
14
  #
13
15
  # 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]
16
+ # If the template file has a <tt>.erb</tt> extension, then it uses the erubi[https://rubygems.org/gems/erubi]
15
17
  # template system which can embed Ruby into an HTML document.
16
18
  # If the template file has a <tt>.builder</tt> extension, then Jim Weirich's Builder::XmlMarkup library is used.
17
19
  #
18
20
  # == ERB
19
21
  #
20
- # You trigger ERB by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
22
+ # 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
23
  # following loop for names:
22
24
  #
23
25
  # <b>Names of all the people</b>
@@ -25,7 +27,7 @@ module ActionView #:nodoc:
25
27
  # Name: <%= person.name %><br/>
26
28
  # <% end %>
27
29
  #
28
- # The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
30
+ # The loop is set up in regular embedding tags <tt><% %></tt>, and the name is written using the output embedding tag <tt><%= %></tt>. Note that this
29
31
  # 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
32
  #
31
33
  # <%# WRONG %>
@@ -33,9 +35,9 @@ module ActionView #:nodoc:
33
35
  #
34
36
  # If you absolutely must write from within a function use +concat+.
35
37
  #
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.
38
+ # When on a line that only contains whitespaces except for the tag, <tt><% %></tt> suppresses leading and trailing whitespace,
39
+ # including the trailing newline. <tt><% %></tt> and <tt><%- -%></tt> are the same.
40
+ # Note however that <tt><%= %></tt> and <tt><%= -%></tt> are different: only the latter removes trailing whitespaces.
39
41
  #
40
42
  # === Using sub templates
41
43
  #
@@ -70,6 +72,14 @@ module ActionView #:nodoc:
70
72
  # Headline: <%= headline %>
71
73
  # First name: <%= person.first_name %>
72
74
  #
75
+ # 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
76
+ # variables as:
77
+ #
78
+ # Headline: <%= local_assigns[:headline] %>
79
+ #
80
+ # This is useful in cases where you aren't sure if the local variable has been assigned. Alternatively, you could also use
81
+ # <tt>defined? headline</tt> to first check if the variable has been assigned before using it.
82
+ #
73
83
  # === Template caching
74
84
  #
75
85
  # By default, Rails will compile each template to a method in order to render it. When you alter a template,
@@ -102,7 +112,7 @@ module ActionView #:nodoc:
102
112
  # <p>A product of Danish Design during the Winter of '79...</p>
103
113
  # </div>
104
114
  #
105
- # A full-length RSS example actually used on Basecamp:
115
+ # Here is a full-length RSS example actually used on Basecamp:
106
116
  #
107
117
  # xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
108
118
  # xml.channel do
@@ -126,38 +136,40 @@ module ActionView #:nodoc:
126
136
  # end
127
137
  # end
128
138
  #
129
- # For more information on Builder please consult the [source
130
- # code](https://github.com/jimweirich/builder).
139
+ # For more information on Builder please consult the {source
140
+ # code}[https://github.com/jimweirich/builder].
131
141
  class Base
132
142
  include Helpers, ::ERB::Util, Context
133
143
 
134
144
  # 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 }
145
+ cattr_accessor :field_error_proc, default: Proc.new { |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
137
146
 
138
147
  # How to complete the streaming when an exception occurs.
139
148
  # 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>)
149
+ cattr_accessor :streaming_completion_on_exception, default: %("><script>window.location = "/500.html"</script></html>)
142
150
 
143
151
  # Specify whether rendering within namespaced controllers should prefix
144
152
  # the partial paths for ActiveModel objects with the namespace.
145
153
  # (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
154
+ class_attribute :prefix_partial_path_with_controller_namespace, default: true
148
155
 
149
156
  # Specify default_formats that can be rendered.
150
157
  cattr_accessor :default_formats
151
158
 
152
159
  # Specify whether an error should be raised for missing translations
153
- cattr_accessor :raise_on_missing_translations
154
- @@raise_on_missing_translations = false
160
+ cattr_accessor :raise_on_missing_translations, default: false
161
+
162
+ # Specify whether submit_tag should automatically disable on click
163
+ cattr_accessor :automatically_disable_submit_tag, default: true
164
+
165
+ # Annotate rendered view with file names
166
+ cattr_accessor :annotate_rendered_view_with_filenames, default: false
155
167
 
156
168
  class_attribute :_routes
157
169
  class_attribute :logger
158
170
 
159
171
  class << self
160
- delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
172
+ delegate :erb_trim_mode=, to: "ActionView::Template::Handlers::ERB"
161
173
 
162
174
  def cache_template_loading
163
175
  ActionView::Resolver.caching?
@@ -170,36 +182,100 @@ module ActionView #:nodoc:
170
182
  def xss_safe? #:nodoc:
171
183
  true
172
184
  end
185
+
186
+ def with_empty_template_cache # :nodoc:
187
+ subclass = Class.new(self) {
188
+ # We can't implement these as self.class because subclasses will
189
+ # share the same template cache as superclasses, so "changed?" won't work
190
+ # correctly.
191
+ define_method(:compiled_method_container) { subclass }
192
+ define_singleton_method(:compiled_method_container) { subclass }
193
+
194
+ def inspect
195
+ "#<ActionView::Base:#{'%#016x' % (object_id << 1)}>"
196
+ end
197
+ }
198
+ end
199
+
200
+ def changed?(other) # :nodoc:
201
+ compiled_method_container != other.compiled_method_container
202
+ end
173
203
  end
174
204
 
175
- attr_accessor :view_renderer
205
+ attr_reader :view_renderer, :lookup_context
176
206
  attr_internal :config, :assigns
177
207
 
178
- delegate :lookup_context, :to => :view_renderer
179
- delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
208
+ delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, to: :lookup_context
180
209
 
181
210
  def assign(new_assigns) # :nodoc:
182
211
  @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
183
212
  end
184
213
 
185
- def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
214
+ # :stopdoc:
215
+
216
+ def self.empty
217
+ with_view_paths([])
218
+ end
219
+
220
+ def self.with_view_paths(view_paths, assigns = {}, controller = nil)
221
+ with_context ActionView::LookupContext.new(view_paths), assigns, controller
222
+ end
223
+
224
+ def self.with_context(context, assigns = {}, controller = nil)
225
+ new context, assigns, controller
226
+ end
227
+
228
+ # :startdoc:
229
+
230
+ def initialize(lookup_context, assigns, controller) #:nodoc:
186
231
  @_config = ActiveSupport::InheritableOptions.new
187
232
 
188
- if context.is_a?(ActionView::Renderer)
189
- @view_renderer = context
190
- 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)
196
- end
233
+ @lookup_context = lookup_context
234
+
235
+ @view_renderer = ActionView::Renderer.new @lookup_context
236
+ @current_template = nil
197
237
 
198
238
  assign(assigns)
199
239
  assign_controller(controller)
200
240
  _prepare_context
201
241
  end
202
242
 
243
+ def _run(method, template, locals, buffer, add_to_stack: true, &block)
244
+ _old_output_buffer, _old_virtual_path, _old_template = @output_buffer, @virtual_path, @current_template
245
+ @current_template = template if add_to_stack
246
+ @output_buffer = buffer
247
+ public_send(method, locals, buffer, &block)
248
+ ensure
249
+ @output_buffer, @virtual_path, @current_template = _old_output_buffer, _old_virtual_path, _old_template
250
+ end
251
+
252
+ def compiled_method_container
253
+ raise NotImplementedError, <<~msg.squish
254
+ Subclasses of ActionView::Base must implement `compiled_method_container`
255
+ or use the class method `with_empty_template_cache` for constructing
256
+ an ActionView::Base subclass that has an empty cache.
257
+ msg
258
+ end
259
+
260
+ def in_rendering_context(options)
261
+ old_view_renderer = @view_renderer
262
+ old_lookup_context = @lookup_context
263
+
264
+ if !lookup_context.html_fallback_for_js && options[:formats]
265
+ formats = Array(options[:formats])
266
+ if formats == [:js]
267
+ formats << :html
268
+ end
269
+ @lookup_context = lookup_context.with_prepended_formats(formats)
270
+ @view_renderer = ActionView::Renderer.new @lookup_context
271
+ end
272
+
273
+ yield @view_renderer
274
+ ensure
275
+ @view_renderer = old_view_renderer
276
+ @lookup_context = old_lookup_context
277
+ end
278
+
203
279
  ActiveSupport.run_load_hooks(:action_view, self)
204
280
  end
205
281
  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,52 @@
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
+ all_view_paths.grep(FileSystemResolver).map!(&:path).tap(&:uniq!).sort!
46
+ end
47
+
48
+ def all_view_paths
49
+ ActionView::ViewPaths.all_view_paths.flat_map(&:paths)
50
+ end
51
+ end
52
+ 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
81
93
  end
82
94
 
83
- def initialize(name, template)
84
- @name, @template = name, template
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
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,24 +121,35 @@ 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
+ match = Regexp.last_match
134
+ add_dynamic_dependency(render_dependencies, match[:dynamic])
135
+ add_static_dependency(render_dependencies, match[:static], match[:quote])
136
+ end
137
+ end
138
+
118
139
  def add_dynamic_dependency(dependencies, dependency)
119
140
  if dependency
120
141
  dependencies << "#{dependency.pluralize}/#{dependency.singularize}"
121
142
  end
122
143
  end
123
144
 
124
- def add_static_dependency(dependencies, dependency)
145
+ def add_static_dependency(dependencies, dependency, quote_type)
146
+ if quote_type == '"'
147
+ # Ignore if there is interpolation
148
+ return if dependency.include?('#{')
149
+ end
150
+
125
151
  if dependency
126
- if dependency.include?('/')
152
+ if dependency.include?("/")
127
153
  dependencies << dependency
128
154
  else
129
155
  dependencies << "#{directory}/#{dependency}"
@@ -131,8 +157,22 @@ module ActionView
131
157
  end
132
158
  end
133
159
 
160
+ def resolve_directories(wildcard_dependencies)
161
+ return [] unless @view_paths
162
+
163
+ wildcard_dependencies.flat_map { |query, templates|
164
+ @view_paths.find_all_with_query(query).map do |template|
165
+ "#{File.dirname(query)}/#{File.basename(template).split('.').first}"
166
+ end
167
+ }.sort
168
+ end
169
+
134
170
  def explicit_dependencies
135
- source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
171
+ dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
172
+
173
+ wildcards, explicits = dependencies.partition { |dependency| dependency.end_with?("*") }
174
+
175
+ (explicits + resolve_directories(wildcards)).uniq
136
176
  end
137
177
  end
138
178