actionview 4.2.11.1 → 7.0.2.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 (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +229 -215
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +116 -43
  6. data/lib/action_view/buffers.rb +20 -3
  7. data/lib/action_view/cache_expiry.rb +66 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  10. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  11. data/lib/action_view/dependency_tracker.rb +21 -122
  12. data/lib/action_view/digestor.rb +92 -85
  13. data/lib/action_view/flows.rb +15 -16
  14. data/lib/action_view/gem_version.rb +6 -4
  15. data/lib/action_view/helpers/active_model_helper.rb +17 -12
  16. data/lib/action_view/helpers/asset_tag_helper.rb +356 -101
  17. data/lib/action_view/helpers/asset_url_helper.rb +180 -74
  18. data/lib/action_view/helpers/atom_feed_helper.rb +21 -19
  19. data/lib/action_view/helpers/cache_helper.rb +156 -43
  20. data/lib/action_view/helpers/capture_helper.rb +21 -14
  21. data/lib/action_view/helpers/controller_helper.rb +16 -5
  22. data/lib/action_view/helpers/csp_helper.rb +26 -0
  23. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  24. data/lib/action_view/helpers/date_helper.rb +288 -132
  25. data/lib/action_view/helpers/debug_helper.rb +9 -6
  26. data/lib/action_view/helpers/form_helper.rb +956 -173
  27. data/lib/action_view/helpers/form_options_helper.rb +178 -97
  28. data/lib/action_view/helpers/form_tag_helper.rb +220 -101
  29. data/lib/action_view/helpers/javascript_helper.rb +33 -19
  30. data/lib/action_view/helpers/number_helper.rb +88 -63
  31. data/lib/action_view/helpers/output_safety_helper.rb +38 -6
  32. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  33. data/lib/action_view/helpers/sanitize_helper.rb +31 -32
  34. data/lib/action_view/helpers/tag_helper.rb +332 -71
  35. data/lib/action_view/helpers/tags/base.rb +123 -99
  36. data/lib/action_view/helpers/tags/check_box.rb +21 -20
  37. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  38. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  39. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  40. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  41. data/lib/action_view/helpers/tags/collection_select.rb +5 -3
  42. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  43. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  44. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  45. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  46. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  47. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  48. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  49. data/lib/action_view/helpers/tags/file_field.rb +18 -0
  50. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  51. data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
  52. data/lib/action_view/helpers/tags/label.rb +7 -2
  53. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  54. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  55. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  56. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  57. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  58. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  59. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  60. data/lib/action_view/helpers/tags/select.rb +11 -10
  61. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  62. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  63. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  64. data/lib/action_view/helpers/tags/time_field.rb +12 -2
  65. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  66. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  67. data/lib/action_view/helpers/tags/translator.rb +15 -16
  68. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  69. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  70. data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
  71. data/lib/action_view/helpers/tags.rb +5 -2
  72. data/lib/action_view/helpers/text_helper.rb +80 -51
  73. data/lib/action_view/helpers/translation_helper.rb +120 -69
  74. data/lib/action_view/helpers/url_helper.rb +398 -171
  75. data/lib/action_view/helpers.rb +29 -27
  76. data/lib/action_view/layouts.rb +68 -63
  77. data/lib/action_view/log_subscriber.rb +77 -10
  78. data/lib/action_view/lookup_context.rb +137 -113
  79. data/lib/action_view/model_naming.rb +4 -2
  80. data/lib/action_view/path_set.rb +28 -32
  81. data/lib/action_view/railtie.rb +74 -13
  82. data/lib/action_view/record_identifier.rb +53 -26
  83. data/lib/action_view/render_parser.rb +188 -0
  84. data/lib/action_view/renderer/abstract_renderer.rb +152 -15
  85. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  86. data/lib/action_view/renderer/object_renderer.rb +34 -0
  87. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  88. data/lib/action_view/renderer/partial_renderer.rb +51 -333
  89. data/lib/action_view/renderer/renderer.rb +68 -11
  90. data/lib/action_view/renderer/streaming_template_renderer.rb +60 -56
  91. data/lib/action_view/renderer/template_renderer.rb +87 -74
  92. data/lib/action_view/rendering.rb +73 -47
  93. data/lib/action_view/ripper_ast_parser.rb +198 -0
  94. data/lib/action_view/routing_url_for.rb +35 -24
  95. data/lib/action_view/tasks/cache_digests.rake +25 -0
  96. data/lib/action_view/template/error.rb +151 -41
  97. data/lib/action_view/template/handlers/builder.rb +12 -13
  98. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  99. data/lib/action_view/template/handlers/erb.rb +29 -89
  100. data/lib/action_view/template/handlers/html.rb +11 -0
  101. data/lib/action_view/template/handlers/raw.rb +4 -4
  102. data/lib/action_view/template/handlers.rb +14 -10
  103. data/lib/action_view/template/html.rb +12 -13
  104. data/lib/action_view/template/inline.rb +22 -0
  105. data/lib/action_view/template/raw_file.rb +25 -0
  106. data/lib/action_view/template/renderable.rb +24 -0
  107. data/lib/action_view/template/resolver.rb +139 -300
  108. data/lib/action_view/template/sources/file.rb +17 -0
  109. data/lib/action_view/template/sources.rb +13 -0
  110. data/lib/action_view/template/text.rb +10 -12
  111. data/lib/action_view/template/types.rb +28 -26
  112. data/lib/action_view/template.rb +123 -91
  113. data/lib/action_view/template_details.rb +66 -0
  114. data/lib/action_view/template_path.rb +64 -0
  115. data/lib/action_view/test_case.rb +70 -53
  116. data/lib/action_view/testing/resolvers.rb +25 -35
  117. data/lib/action_view/unbound_template.rb +57 -0
  118. data/lib/action_view/version.rb +3 -1
  119. data/lib/action_view/view_paths.rb +73 -58
  120. data/lib/action_view.rb +16 -11
  121. data/lib/assets/compiled/rails-ujs.js +746 -0
  122. metadata +52 -32
  123. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  124. 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'
9
-
10
- module ActionView #:nodoc:
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"
11
+
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,37 @@ 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| content_tag :div, html_tag, class: "field_with_errors" }
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
- # Specify whether an error should be raised for missing translations
153
- cattr_accessor :raise_on_missing_translations
154
- @@raise_on_missing_translations = false
159
+ # Specify whether submit_tag should automatically disable on click
160
+ cattr_accessor :automatically_disable_submit_tag, default: true
161
+
162
+ # Annotate rendered view with file names
163
+ cattr_accessor :annotate_rendered_view_with_filenames, default: false
155
164
 
156
165
  class_attribute :_routes
157
166
  class_attribute :logger
158
167
 
159
168
  class << self
160
- delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
169
+ delegate :erb_trim_mode=, to: "ActionView::Template::Handlers::ERB"
161
170
 
162
171
  def cache_template_loading
163
172
  ActionView::Resolver.caching?
@@ -167,39 +176,103 @@ module ActionView #:nodoc:
167
176
  ActionView::Resolver.caching = value
168
177
  end
169
178
 
170
- def xss_safe? #:nodoc:
179
+ def xss_safe? # :nodoc:
171
180
  true
172
181
  end
182
+
183
+ def with_empty_template_cache # :nodoc:
184
+ subclass = Class.new(self) {
185
+ # We can't implement these as self.class because subclasses will
186
+ # share the same template cache as superclasses, so "changed?" won't work
187
+ # correctly.
188
+ define_method(:compiled_method_container) { subclass }
189
+ define_singleton_method(:compiled_method_container) { subclass }
190
+
191
+ def inspect
192
+ "#<ActionView::Base:#{'%#016x' % (object_id << 1)}>"
193
+ end
194
+ }
195
+ end
196
+
197
+ def changed?(other) # :nodoc:
198
+ compiled_method_container != other.compiled_method_container
199
+ end
173
200
  end
174
201
 
175
- attr_accessor :view_renderer
202
+ attr_reader :view_renderer, :lookup_context
176
203
  attr_internal :config, :assigns
177
204
 
178
- delegate :lookup_context, :to => :view_renderer
179
- delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
205
+ delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, to: :lookup_context
180
206
 
181
207
  def assign(new_assigns) # :nodoc:
182
208
  @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
183
209
  end
184
210
 
185
- def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
211
+ # :stopdoc:
212
+
213
+ def self.empty
214
+ with_view_paths([])
215
+ end
216
+
217
+ def self.with_view_paths(view_paths, assigns = {}, controller = nil)
218
+ with_context ActionView::LookupContext.new(view_paths), assigns, controller
219
+ end
220
+
221
+ def self.with_context(context, assigns = {}, controller = nil)
222
+ new context, assigns, controller
223
+ end
224
+
225
+ # :startdoc:
226
+
227
+ def initialize(lookup_context, assigns, controller) # :nodoc:
186
228
  @_config = ActiveSupport::InheritableOptions.new
187
229
 
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
230
+ @lookup_context = lookup_context
231
+
232
+ @view_renderer = ActionView::Renderer.new @lookup_context
233
+ @current_template = nil
197
234
 
198
235
  assign(assigns)
199
236
  assign_controller(controller)
200
237
  _prepare_context
201
238
  end
202
239
 
240
+ def _run(method, template, locals, buffer, add_to_stack: true, &block)
241
+ _old_output_buffer, _old_virtual_path, _old_template = @output_buffer, @virtual_path, @current_template
242
+ @current_template = template if add_to_stack
243
+ @output_buffer = buffer
244
+ public_send(method, locals, buffer, &block)
245
+ ensure
246
+ @output_buffer, @virtual_path, @current_template = _old_output_buffer, _old_virtual_path, _old_template
247
+ end
248
+
249
+ def compiled_method_container
250
+ raise NotImplementedError, <<~msg.squish
251
+ Subclasses of ActionView::Base must implement `compiled_method_container`
252
+ or use the class method `with_empty_template_cache` for constructing
253
+ an ActionView::Base subclass that has an empty cache.
254
+ msg
255
+ end
256
+
257
+ def in_rendering_context(options)
258
+ old_view_renderer = @view_renderer
259
+ old_lookup_context = @lookup_context
260
+
261
+ if !lookup_context.html_fallback_for_js && options[:formats]
262
+ formats = Array(options[:formats])
263
+ if formats == [:js]
264
+ formats << :html
265
+ end
266
+ @lookup_context = lookup_context.with_prepended_formats(formats)
267
+ @view_renderer = ActionView::Renderer.new @lookup_context
268
+ end
269
+
270
+ yield @view_renderer
271
+ ensure
272
+ @view_renderer = old_view_renderer
273
+ @lookup_context = old_lookup_context
274
+ end
275
+
203
276
  ActiveSupport.run_load_hooks(:action_view, self)
204
277
  end
205
278
  end
@@ -1,7 +1,24 @@
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
4
- class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
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:
5
22
  def initialize(*)
6
23
  super
7
24
  encode!
@@ -21,7 +38,7 @@ module ActionView
21
38
  alias :safe_append= :safe_concat
22
39
  end
23
40
 
24
- class StreamingBuffer #:nodoc:
41
+ class StreamingBuffer # :nodoc:
25
42
  def initialize(block)
26
43
  @block = block
27
44
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class CacheExpiry
5
+ class Executor
6
+ def initialize(watcher:)
7
+ @execution_lock = Concurrent::ReentrantReadWriteLock.new
8
+ @cache_expiry = ViewModificationWatcher.new(watcher: watcher) do
9
+ clear_cache
10
+ end
11
+ end
12
+
13
+ def run
14
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
15
+ @cache_expiry.execute_if_updated
16
+ @execution_lock.acquire_read_lock
17
+ end
18
+ end
19
+
20
+ def complete(_)
21
+ @execution_lock.release_read_lock
22
+ end
23
+
24
+ private
25
+ def clear_cache
26
+ @execution_lock.with_write_lock do
27
+ ActionView::LookupContext::DetailsKey.clear
28
+ end
29
+ end
30
+ end
31
+
32
+ class ViewModificationWatcher
33
+ def initialize(watcher:, &block)
34
+ @watched_dirs = nil
35
+ @watcher_class = watcher
36
+ @watcher = nil
37
+ @mutex = Mutex.new
38
+ @block = block
39
+ end
40
+
41
+ def execute_if_updated
42
+ @mutex.synchronize do
43
+ watched_dirs = dirs_to_watch
44
+ return if watched_dirs.empty?
45
+
46
+ if watched_dirs != @watched_dirs
47
+ @watched_dirs = watched_dirs
48
+ @watcher = @watcher_class.new([], watched_dirs, &@block)
49
+ @watcher.execute
50
+ else
51
+ @watcher.execute_if_updated
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+ def dirs_to_watch
58
+ all_view_paths.grep(FileSystemResolver).map!(&:path).tap(&:uniq!).sort!
59
+ end
60
+
61
+ def all_view_paths
62
+ ActionView::ViewPaths.all_view_paths.flat_map(&:paths)
63
+ end
64
+ end
65
+ end
66
+ 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
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class DependencyTracker # :nodoc:
5
+ class ERBTracker # :nodoc:
6
+ EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
7
+
8
+ # A valid ruby identifier - suitable for class, method and specially variable names
9
+ IDENTIFIER = /
10
+ [[:alpha:]_] # at least one uppercase letter, lowercase letter or underscore
11
+ [[:word:]]* # followed by optional letters, numbers or underscores
12
+ /x
13
+
14
+ # Any kind of variable name. e.g. @instance, @@class, $global or local.
15
+ # Possibly following a method call chain
16
+ VARIABLE_OR_METHOD_CHAIN = /
17
+ (?:\$|@{1,2})? # optional global, instance or class variable indicator
18
+ (?:#{IDENTIFIER}\.)* # followed by an optional chain of zero-argument method calls
19
+ (?<dynamic>#{IDENTIFIER}) # and a final valid identifier, captured as DYNAMIC
20
+ /x
21
+
22
+ # A simple string literal. e.g. "School's out!"
23
+ STRING = /
24
+ (?<quote>['"]) # an opening quote
25
+ (?<static>.*?) # with anything inside, captured as STATIC
26
+ \k<quote> # and a matching closing quote
27
+ /x
28
+
29
+ # Part of any hash containing the :partial key
30
+ PARTIAL_HASH_KEY = /
31
+ (?:\bpartial:|:partial\s*=>) # partial key in either old or new style hash syntax
32
+ \s* # followed by optional spaces
33
+ /x
34
+
35
+ # Part of any hash containing the :layout key
36
+ LAYOUT_HASH_KEY = /
37
+ (?:\blayout:|:layout\s*=>) # layout key in either old or new style hash syntax
38
+ \s* # followed by optional spaces
39
+ /x
40
+
41
+ # Matches:
42
+ # partial: "comments/comment", collection: @all_comments => "comments/comment"
43
+ # (object: @single_comment, partial: "comments/comment") => "comments/comment"
44
+ #
45
+ # "comments/comments"
46
+ # 'comments/comments'
47
+ # ('comments/comments')
48
+ #
49
+ # (@topic) => "topics/topic"
50
+ # topics => "topics/topic"
51
+ # (message.topics) => "topics/topic"
52
+ RENDER_ARGUMENTS = /\A
53
+ (?:\s*\(?\s*) # optional opening paren surrounded by spaces
54
+ (?:.*?#{PARTIAL_HASH_KEY}|#{LAYOUT_HASH_KEY})? # optional hash, up to the partial or layout key declaration
55
+ (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
56
+ /xm
57
+
58
+ LAYOUT_DEPENDENCY = /\A
59
+ (?:\s*\(?\s*) # optional opening paren surrounded by spaces
60
+ (?:.*?#{LAYOUT_HASH_KEY}) # check if the line has layout key declaration
61
+ (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
62
+ /xm
63
+
64
+ def self.supports_view_paths? # :nodoc:
65
+ true
66
+ end
67
+
68
+ def self.call(name, template, view_paths = nil)
69
+ new(name, template, view_paths).dependencies
70
+ end
71
+
72
+ def initialize(name, template, view_paths = nil)
73
+ @name, @template, @view_paths = name, template, view_paths
74
+ end
75
+
76
+ def dependencies
77
+ render_dependencies + explicit_dependencies
78
+ end
79
+
80
+ attr_reader :name, :template
81
+ private :name, :template
82
+
83
+ private
84
+ def source
85
+ template.source
86
+ end
87
+
88
+ def directory
89
+ name.split("/")[0..-2].join("/")
90
+ end
91
+
92
+ def render_dependencies
93
+ render_dependencies = []
94
+ render_calls = source.split(/\brender\b/).drop(1)
95
+
96
+ render_calls.each do |arguments|
97
+ add_dependencies(render_dependencies, arguments, LAYOUT_DEPENDENCY)
98
+ add_dependencies(render_dependencies, arguments, RENDER_ARGUMENTS)
99
+ end
100
+
101
+ render_dependencies.uniq
102
+ end
103
+
104
+ def add_dependencies(render_dependencies, arguments, pattern)
105
+ arguments.scan(pattern) do
106
+ match = Regexp.last_match
107
+ add_dynamic_dependency(render_dependencies, match[:dynamic])
108
+ add_static_dependency(render_dependencies, match[:static], match[:quote])
109
+ end
110
+ end
111
+
112
+ def add_dynamic_dependency(dependencies, dependency)
113
+ if dependency
114
+ dependencies << "#{dependency.pluralize}/#{dependency.singularize}"
115
+ end
116
+ end
117
+
118
+ def add_static_dependency(dependencies, dependency, quote_type)
119
+ if quote_type == '"'
120
+ # Ignore if there is interpolation
121
+ return if dependency.include?('#{')
122
+ end
123
+
124
+ if dependency
125
+ if dependency.include?("/")
126
+ dependencies << dependency
127
+ else
128
+ dependencies << "#{directory}/#{dependency}"
129
+ end
130
+ end
131
+ end
132
+
133
+ def resolve_directories(wildcard_dependencies)
134
+ return [] unless @view_paths
135
+ return [] if wildcard_dependencies.empty?
136
+
137
+ # Remove trailing "/*"
138
+ prefixes = wildcard_dependencies.map { |query| query[0..-3] }
139
+
140
+ @view_paths.flat_map(&:all_template_paths).uniq.filter_map { |path|
141
+ path.to_s if prefixes.include?(path.prefix)
142
+ }.sort
143
+ end
144
+
145
+ def explicit_dependencies
146
+ dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
147
+
148
+ wildcards, explicits = dependencies.partition { |dependency| dependency.end_with?("/*") }
149
+
150
+ (explicits + resolve_directories(wildcards)).uniq
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class DependencyTracker # :nodoc:
5
+ class RipperTracker # :nodoc:
6
+ EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
7
+
8
+ def self.call(name, template, view_paths = nil)
9
+ new(name, template, view_paths).dependencies
10
+ end
11
+
12
+ def dependencies
13
+ render_dependencies + explicit_dependencies
14
+ end
15
+
16
+ def self.supports_view_paths? # :nodoc:
17
+ true
18
+ end
19
+
20
+ def initialize(name, template, view_paths = nil)
21
+ @name, @template, @view_paths = name, template, view_paths
22
+ end
23
+
24
+ private
25
+ attr_reader :template, :name, :view_paths
26
+
27
+ def render_dependencies
28
+ return [] unless template.source.include?("render")
29
+
30
+ compiled_source = template.handler.call(template, template.source)
31
+
32
+ RenderParser.new(@name, compiled_source).render_calls.filter_map do |render_call|
33
+ next if render_call.end_with?("/_")
34
+ render_call.gsub(%r|/_|, "/")
35
+ end
36
+ end
37
+
38
+ def explicit_dependencies
39
+ dependencies = template.source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
40
+
41
+ wildcards, explicits = dependencies.partition { |dependency| dependency.end_with?("/*") }
42
+
43
+ (explicits + resolve_directories(wildcards)).uniq
44
+ end
45
+
46
+ def resolve_directories(wildcard_dependencies)
47
+ return [] unless view_paths
48
+ return [] if wildcard_dependencies.empty?
49
+
50
+ # Remove trailing "/*"
51
+ prefixes = wildcard_dependencies.map { |query| query[0..-3] }
52
+
53
+ view_paths.flat_map(&:all_template_paths).uniq.filter_map { |path|
54
+ path.to_s if prefixes.include?(path.prefix)
55
+ }.sort
56
+ end
57
+ end
58
+ end
59
+ end