actionview 6.1.7.2 → 7.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -277
  3. data/MIT-LICENSE +2 -1
  4. data/README.rdoc +3 -3
  5. data/app/assets/javascripts/rails-ujs.esm.js +686 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +37 -19
  8. data/lib/action_view/buffers.rb +107 -9
  9. data/lib/action_view/cache_expiry.rb +48 -37
  10. data/lib/action_view/context.rb +1 -1
  11. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  12. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  13. data/lib/action_view/dependency_tracker.rb +6 -147
  14. data/lib/action_view/deprecator.rb +7 -0
  15. data/lib/action_view/digestor.rb +8 -5
  16. data/lib/action_view/flows.rb +4 -4
  17. data/lib/action_view/gem_version.rb +4 -4
  18. data/lib/action_view/helpers/active_model_helper.rb +3 -3
  19. data/lib/action_view/helpers/asset_tag_helper.rb +200 -60
  20. data/lib/action_view/helpers/asset_url_helper.rb +22 -21
  21. data/lib/action_view/helpers/atom_feed_helper.rb +8 -9
  22. data/lib/action_view/helpers/cache_helper.rb +55 -12
  23. data/lib/action_view/helpers/capture_helper.rb +34 -14
  24. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  25. data/lib/action_view/helpers/controller_helper.rb +8 -2
  26. data/lib/action_view/helpers/csp_helper.rb +3 -3
  27. data/lib/action_view/helpers/csrf_helper.rb +4 -4
  28. data/lib/action_view/helpers/date_helper.rb +123 -57
  29. data/lib/action_view/helpers/debug_helper.rb +6 -4
  30. data/lib/action_view/helpers/form_helper.rb +253 -97
  31. data/lib/action_view/helpers/form_options_helper.rb +72 -34
  32. data/lib/action_view/helpers/form_tag_helper.rb +189 -58
  33. data/lib/action_view/helpers/javascript_helper.rb +4 -5
  34. data/lib/action_view/helpers/number_helper.rb +43 -335
  35. data/lib/action_view/helpers/output_safety_helper.rb +6 -6
  36. data/lib/action_view/helpers/rendering_helper.rb +6 -7
  37. data/lib/action_view/helpers/sanitize_helper.rb +54 -24
  38. data/lib/action_view/helpers/tag_helper.rb +42 -35
  39. data/lib/action_view/helpers/tags/base.rb +16 -77
  40. data/lib/action_view/helpers/tags/check_box.rb +1 -1
  41. data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
  42. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
  43. data/lib/action_view/helpers/tags/collection_select.rb +4 -1
  44. data/lib/action_view/helpers/tags/date_field.rb +1 -1
  45. data/lib/action_view/helpers/tags/date_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
  47. data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
  48. data/lib/action_view/helpers/tags/file_field.rb +16 -0
  49. data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
  50. data/lib/action_view/helpers/tags/month_field.rb +1 -1
  51. data/lib/action_view/helpers/tags/select.rb +4 -1
  52. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  53. data/lib/action_view/helpers/tags/time_field.rb +11 -2
  54. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
  55. data/lib/action_view/helpers/tags/week_field.rb +1 -1
  56. data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
  57. data/lib/action_view/helpers/tags.rb +5 -2
  58. data/lib/action_view/helpers/text_helper.rb +180 -97
  59. data/lib/action_view/helpers/translation_helper.rb +14 -45
  60. data/lib/action_view/helpers/url_helper.rb +230 -132
  61. data/lib/action_view/helpers.rb +27 -25
  62. data/lib/action_view/layouts.rb +15 -10
  63. data/lib/action_view/log_subscriber.rb +49 -32
  64. data/lib/action_view/lookup_context.rb +58 -61
  65. data/lib/action_view/model_naming.rb +2 -2
  66. data/lib/action_view/path_registry.rb +57 -0
  67. data/lib/action_view/path_set.rb +28 -35
  68. data/lib/action_view/railtie.rb +44 -9
  69. data/lib/action_view/record_identifier.rb +16 -9
  70. data/lib/action_view/render_parser.rb +188 -0
  71. data/lib/action_view/renderer/abstract_renderer.rb +3 -3
  72. data/lib/action_view/renderer/collection_renderer.rb +10 -2
  73. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
  74. data/lib/action_view/renderer/partial_renderer.rb +3 -36
  75. data/lib/action_view/renderer/renderer.rb +6 -4
  76. data/lib/action_view/renderer/streaming_template_renderer.rb +6 -5
  77. data/lib/action_view/renderer/template_renderer.rb +9 -4
  78. data/lib/action_view/rendering.rb +25 -7
  79. data/lib/action_view/ripper_ast_parser.rb +198 -0
  80. data/lib/action_view/routing_url_for.rb +8 -5
  81. data/lib/action_view/template/error.rb +122 -14
  82. data/lib/action_view/template/handlers/builder.rb +4 -4
  83. data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
  84. data/lib/action_view/template/handlers/erb.rb +79 -1
  85. data/lib/action_view/template/handlers.rb +4 -4
  86. data/lib/action_view/template/html.rb +4 -4
  87. data/lib/action_view/template/inline.rb +3 -3
  88. data/lib/action_view/template/raw_file.rb +4 -4
  89. data/lib/action_view/template/renderable.rb +1 -1
  90. data/lib/action_view/template/resolver.rb +96 -313
  91. data/lib/action_view/template/text.rb +4 -4
  92. data/lib/action_view/template/types.rb +25 -32
  93. data/lib/action_view/template.rb +245 -41
  94. data/lib/action_view/template_details.rb +66 -0
  95. data/lib/action_view/template_path.rb +66 -0
  96. data/lib/action_view/test_case.rb +182 -23
  97. data/lib/action_view/testing/resolvers.rb +11 -12
  98. data/lib/action_view/unbound_template.rb +43 -7
  99. data/lib/action_view/version.rb +1 -1
  100. data/lib/action_view/view_paths.rb +19 -28
  101. data/lib/action_view.rb +6 -4
  102. data/lib/assets/compiled/rails-ujs.js +36 -5
  103. metadata +32 -25
@@ -9,6 +9,9 @@ require "rails-dom-testing"
9
9
 
10
10
  module ActionView
11
11
  # = Action View Test Case
12
+ #
13
+ # Read more about <tt>ActionView::TestCase</tt> in {Testing Rails Applications}[https://guides.rubyonrails.org/testing.html#testing-view-partials]
14
+ # in the guides.
12
15
  class TestCase < ActiveSupport::TestCase
13
16
  class TestController < ActionController::Base
14
17
  include ActionDispatch::TestProcess
@@ -24,6 +27,10 @@ module ActionView
24
27
  self.class.controller_path = path
25
28
  end
26
29
 
30
+ def self.controller_name
31
+ "test"
32
+ end
33
+
27
34
  def initialize
28
35
  super
29
36
  self.class.controller_path = ""
@@ -53,9 +60,98 @@ module ActionView
53
60
  include ActiveSupport::Testing::ConstantLookup
54
61
 
55
62
  delegate :lookup_context, to: :controller
56
- attr_accessor :controller, :output_buffer, :rendered
63
+ attr_accessor :controller, :request, :output_buffer
57
64
 
58
65
  module ClassMethods
66
+ def inherited(descendant) # :nodoc:
67
+ super
68
+
69
+ descendant_content_class = content_class.dup
70
+
71
+ if descendant_content_class.respond_to?(:set_temporary_name)
72
+ descendant_content_class.set_temporary_name("rendered_content")
73
+ end
74
+
75
+ descendant.content_class = descendant_content_class
76
+ end
77
+
78
+ # Register a callable to parse rendered content for a given template
79
+ # format.
80
+ #
81
+ # Each registered parser will also define a +#rendered.[FORMAT]+ helper
82
+ # method, where +[FORMAT]+ corresponds to the value of the
83
+ # +format+ argument.
84
+ #
85
+ # By default, ActionView::TestCase defines parsers for:
86
+ #
87
+ # * +:html+ - returns an instance of +Nokogiri::XML::Node+
88
+ # * +:json+ - returns an instance of ActiveSupport::HashWithIndifferentAccess
89
+ #
90
+ # These pre-registered parsers also define corresponding helpers:
91
+ #
92
+ # * +:html+ - defines +rendered.html+
93
+ # * +:json+ - defines +rendered.json+
94
+ #
95
+ # ==== Parameters
96
+ #
97
+ # [+format+]
98
+ # The name (as a +Symbol+) of the format used to render the content.
99
+ #
100
+ # [+callable+]
101
+ # The parser. A callable object that accepts the rendered string as
102
+ # its sole argument. Alternatively, the parser can be specified as a
103
+ # block.
104
+ #
105
+ # ==== Examples
106
+ #
107
+ # test "renders HTML" do
108
+ # article = Article.create!(title: "Hello, world")
109
+ #
110
+ # render partial: "articles/article", locals: { article: article }
111
+ #
112
+ # assert_pattern { rendered.html.at("main h1") => { content: "Hello, world" } }
113
+ # end
114
+ #
115
+ # test "renders JSON" do
116
+ # article = Article.create!(title: "Hello, world")
117
+ #
118
+ # render formats: :json, partial: "articles/article", locals: { article: article }
119
+ #
120
+ # assert_pattern { rendered.json => { title: "Hello, world" } }
121
+ # end
122
+ #
123
+ # To parse the rendered content into RSS, register a call to +RSS::Parser.parse+:
124
+ #
125
+ # register_parser :rss, -> rendered { RSS::Parser.parse(rendered) }
126
+ #
127
+ # test "renders RSS" do
128
+ # article = Article.create!(title: "Hello, world")
129
+ #
130
+ # render formats: :rss, partial: article
131
+ #
132
+ # assert_equal "Hello, world", rendered.rss.items.last.title
133
+ # end
134
+ #
135
+ # To parse the rendered content into a +Capybara::Simple::Node+,
136
+ # re-register an +:html+ parser with a call to +Capybara.string+:
137
+ #
138
+ # register_parser :html, -> rendered { Capybara.string(rendered) }
139
+ #
140
+ # test "renders HTML" do
141
+ # article = Article.create!(title: "Hello, world")
142
+ #
143
+ # render partial: article
144
+ #
145
+ # rendered.html.assert_css "h1", text: "Hello, world"
146
+ # end
147
+ #
148
+ def register_parser(format, callable = nil, &block)
149
+ parser = callable || block || :itself.to_proc
150
+ content_class.redefine_method(format) do
151
+ parser.call(to_s)
152
+ end
153
+ end
154
+
59
155
  def tests(helper_class)
60
156
  case helper_class
61
157
  when String, Symbol
@@ -74,11 +170,11 @@ module ActionView
74
170
  def helper_method(*methods)
75
171
  # Almost a duplicate from ActionController::Helpers
76
172
  methods.flatten.each do |method|
77
- _helpers_for_modification.module_eval <<-end_eval, __FILE__, __LINE__ + 1
173
+ _helpers_for_modification.module_eval <<~end_eval, __FILE__, __LINE__ + 1
78
174
  def #{method}(*args, &block) # def current_user(*args, &block)
79
175
  _test_case.send(:'#{method}', *args, &block) # _test_case.send(:'current_user', *args, &block)
80
176
  end # end
81
- ruby2_keywords(:'#{method}') if respond_to?(:ruby2_keywords, true)
177
+ ruby2_keywords(:'#{method}')
82
178
  end_eval
83
179
  end
84
180
  end
@@ -101,14 +197,33 @@ module ActionView
101
197
  end
102
198
  end
103
199
 
200
+ included do
201
+ class_attribute :content_class, instance_accessor: false, default: RenderedViewContent
202
+
203
+ setup :setup_with_controller
204
+
205
+ register_parser :html, -> rendered { Rails::Dom::Testing.html_document.parse(rendered).root }
206
+ register_parser :json, -> rendered { JSON.parse(rendered, object_class: ActiveSupport::HashWithIndifferentAccess) }
207
+
208
+ ActiveSupport.run_load_hooks(:action_view_test_case, self)
209
+
210
+ helper do
211
+ def protect_against_forgery?
212
+ false
213
+ end
214
+
215
+ def _test_case
216
+ controller._test_case
217
+ end
218
+ end
219
+ end
220
+
104
221
  def setup_with_controller
105
222
  controller_class = Class.new(ActionView::TestCase::TestController)
106
223
  @controller = controller_class.new
107
224
  @request = @controller.request
108
225
  @view_flow = ActionView::OutputFlow.new
109
- # empty string ensures buffer has UTF-8 encoding as
110
- # new without arguments returns ASCII-8BIT encoded buffer like String#new
111
- @output_buffer = ActiveSupport::SafeBuffer.new ""
226
+ @output_buffer = ActionView::OutputBuffer.new
112
227
  @rendered = +""
113
228
 
114
229
  test_case_instance = self
@@ -129,10 +244,64 @@ module ActionView
129
244
  @_rendered_views ||= RenderedViewsCollection.new
130
245
  end
131
246
 
247
+ # Returns the content rendered by the last +render+ call.
248
+ #
249
+ # The returned object behaves like a string but also exposes a number of methods
250
+ # that allows you to parse the content string in formats registered using
251
+ # <tt>.register_parser</tt>.
252
+ #
253
+ # By default includes the following parsers:
254
+ #
255
+ # +.html+
256
+ #
257
+ # Parse the <tt>rendered</tt> content String into HTML. By default, this means
258
+ # a <tt>Nokogiri::XML::Node</tt>.
259
+ #
260
+ # test "renders HTML" do
261
+ # article = Article.create!(title: "Hello, world")
262
+ #
263
+ # render partial: "articles/article", locals: { article: article }
264
+ #
265
+ # assert_pattern { rendered.html.at("main h1") => { content: "Hello, world" } }
266
+ # end
267
+ #
268
+ # To parse the rendered content into a <tt>Capybara::Simple::Node</tt>,
269
+ # re-register an <tt>:html</tt> parser with a call to
270
+ # <tt>Capybara.string</tt>:
271
+ #
272
+ # register_parser :html, -> rendered { Capybara.string(rendered) }
273
+ #
274
+ # test "renders HTML" do
275
+ # article = Article.create!(title: "Hello, world")
276
+ #
277
+ # render partial: article
278
+ #
279
+ # rendered.html.assert_css "h1", text: "Hello, world"
280
+ # end
281
+ #
282
+ # +.json+
283
+ #
284
+ # Parse the <tt>rendered</tt> content String into JSON. By default, this means
285
+ # a <tt>ActiveSupport::HashWithIndifferentAccess</tt>.
286
+ #
287
+ # test "renders JSON" do
288
+ # article = Article.create!(title: "Hello, world")
289
+ #
290
+ # render formats: :json, partial: "articles/article", locals: { article: article }
291
+ #
292
+ # assert_pattern { rendered.json => { title: "Hello, world" } }
293
+ # end
294
+ def rendered
295
+ @_rendered ||= self.class.content_class.new(@rendered)
296
+ end
297
+
132
298
  def _routes
133
299
  @controller._routes if @controller.respond_to?(:_routes)
134
300
  end
135
301
 
302
+ class RenderedViewContent < String # :nodoc:
303
+ end
304
+
136
305
  # Need to experiment if this priority is the best one: rendered => output_buffer
137
306
  class RenderedViewsCollection
138
307
  def initialize
@@ -159,25 +328,10 @@ module ActionView
159
328
  end
160
329
  end
161
330
 
162
- included do
163
- setup :setup_with_controller
164
- ActiveSupport.run_load_hooks(:action_view_test_case, self)
165
-
166
- helper do
167
- def protect_against_forgery?
168
- false
169
- end
170
-
171
- def _test_case
172
- controller._test_case
173
- end
174
- end
175
- end
176
-
177
331
  private
178
332
  # Need to experiment if this priority is the best one: rendered => output_buffer
179
333
  def document_root_element
180
- Nokogiri::HTML::Document.parse(@rendered.blank? ? @output_buffer : @rendered).root
334
+ Rails::Dom::Testing.html_document.parse(@rendered.blank? ? @output_buffer.to_str : @rendered).root
181
335
  end
182
336
 
183
337
  module Locals
@@ -223,6 +377,10 @@ module ActionView
223
377
  :@_result,
224
378
  :@_routes,
225
379
  :@controller,
380
+ :@_controller,
381
+ :@_request,
382
+ :@_config,
383
+ :@_default_form_builder,
226
384
  :@_layouts,
227
385
  :@_files,
228
386
  :@_rendered_views,
@@ -241,7 +399,7 @@ module ActionView
241
399
  :@view_context_class,
242
400
  :@view_flow,
243
401
  :@_subscribers,
244
- :@html_document
402
+ :@html_document,
245
403
  ]
246
404
 
247
405
  def _user_defined_ivars
@@ -273,6 +431,7 @@ module ActionView
273
431
  super
274
432
  end
275
433
  end
434
+ ruby2_keywords(:method_missing)
276
435
 
277
436
  def respond_to_missing?(name, include_private = false)
278
437
  begin
@@ -2,12 +2,12 @@
2
2
 
3
3
  require "action_view/template/resolver"
4
4
 
5
- module ActionView #:nodoc:
5
+ module ActionView # :nodoc:
6
6
  # Use FixtureResolver in your tests to simulate the presence of files on the
7
7
  # file system. This is used internally by Rails' own test suite, and is
8
8
  # useful for testing extensions that have no way of knowing what the file
9
9
  # system will look like at runtime.
10
- class FixtureResolver < OptimizedFileSystemResolver
10
+ class FixtureResolver < FileSystemResolver
11
11
  def initialize(hash = {})
12
12
  super("")
13
13
  @hash = hash
@@ -23,23 +23,22 @@ module ActionView #:nodoc:
23
23
  end
24
24
 
25
25
  private
26
- def find_candidate_template_paths(path)
27
- @hash.keys.select do |fixture|
28
- fixture.start_with?(path.virtual)
29
- end.map do |fixture|
30
- "/#{fixture}"
26
+ def template_glob(glob)
27
+ @hash.keys.filter_map do |path|
28
+ "/#{path}" if File.fnmatch(glob, path)
31
29
  end
32
30
  end
33
31
 
34
32
  def source_for_template(template)
35
- @hash[template[1..template.size]]
33
+ @hash[template.from(1)]
36
34
  end
37
35
  end
38
36
 
39
- class NullResolver < PathResolver
40
- def query(path, exts, _, locals, cache:)
41
- handler, format, variant = extract_handler_and_format_and_variant(path)
42
- [ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, virtual_path: path.virtual, format: format, variant: variant, locals: locals)]
37
+ class NullResolver < Resolver
38
+ def find_templates(name, prefix, partial, details, locals = [])
39
+ path = TemplatePath.build(name, prefix, partial)
40
+ handler = ActionView::Template::Handlers::Raw
41
+ [ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, virtual_path: path.virtual, format: nil, variant: nil, locals: locals)]
43
42
  end
44
43
  end
45
44
  end
@@ -4,28 +4,64 @@ require "concurrent/map"
4
4
 
5
5
  module ActionView
6
6
  class UnboundTemplate
7
- def initialize(source, identifier, handler, options)
7
+ attr_reader :virtual_path, :details
8
+ delegate :locale, :format, :variant, :handler, to: :@details
9
+
10
+ def initialize(source, identifier, details:, virtual_path:)
8
11
  @source = source
9
12
  @identifier = identifier
10
- @handler = handler
11
- @options = options
13
+ @details = details
14
+ @virtual_path = virtual_path
12
15
 
13
16
  @templates = Concurrent::Map.new(initial_capacity: 2)
17
+ @write_lock = Mutex.new
14
18
  end
15
19
 
16
20
  def bind_locals(locals)
17
- @templates[locals] ||= build_template(locals)
21
+ unless template = @templates[locals]
22
+ @write_lock.synchronize do
23
+ normalized_locals = normalize_locals(locals)
24
+
25
+ # We need ||=, both to dedup on the normalized locals and to check
26
+ # while holding the lock.
27
+ template = (@templates[normalized_locals] ||= build_template(normalized_locals))
28
+
29
+ # This may have already been assigned, but we've already de-dup'd so
30
+ # reassignment is fine.
31
+ @templates[locals.dup] = template
32
+
33
+ if template.strict_locals?
34
+ # Under strict locals, we only need one template.
35
+ # This replaces the @templates Concurrent::Map with a hash which
36
+ # returns this template for every key.
37
+ @templates = Hash.new(template).freeze
38
+ end
39
+ end
40
+ end
41
+ template
42
+ end
43
+
44
+ def built_templates # :nodoc:
45
+ @templates.values
18
46
  end
19
47
 
20
48
  private
21
49
  def build_template(locals)
22
- options = @options.merge(locals: locals)
23
50
  Template.new(
24
51
  @source,
25
52
  @identifier,
26
- @handler,
27
- **options
53
+ details.handler_class,
54
+
55
+ format: details.format_or_default,
56
+ variant: variant&.to_s,
57
+ virtual_path: @virtual_path,
58
+
59
+ locals: locals.map(&:to_s)
28
60
  )
29
61
  end
62
+
63
+ def normalize_locals(locals)
64
+ locals.map(&:to_sym).sort!.freeze
65
+ end
30
66
  end
31
67
  end
@@ -3,7 +3,7 @@
3
3
  require_relative "gem_version"
4
4
 
5
5
  module ActionView
6
- # Returns the version of the currently loaded ActionView as a <tt>Gem::Version</tt>
6
+ # Returns the currently loaded version of Action View as a +Gem::Version+.
7
7
  def self.version
8
8
  gem_version
9
9
  end
@@ -5,7 +5,7 @@ module ActionView
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- ViewPaths.set_view_paths(self, ActionView::PathSet.new.freeze)
8
+ ActionView::PathRegistry.set_view_paths(self, ActionView::PathSet.new.freeze)
9
9
  end
10
10
 
11
11
  delegate :template_exists?, :any_templates?, :view_paths, :formats, :formats=,
@@ -13,11 +13,11 @@ module ActionView
13
13
 
14
14
  module ClassMethods
15
15
  def _view_paths
16
- ViewPaths.get_view_paths(self)
16
+ ActionView::PathRegistry.get_view_paths(self)
17
17
  end
18
18
 
19
19
  def _view_paths=(paths)
20
- ViewPaths.set_view_paths(self, paths)
20
+ ActionView::PathRegistry.set_view_paths(self, paths)
21
21
  end
22
22
 
23
23
  def _prefixes # :nodoc:
@@ -28,6 +28,13 @@ module ActionView
28
28
  end
29
29
  end
30
30
 
31
+ def _build_view_paths(paths) # :nodoc:
32
+ return paths if ActionView::PathSet === paths
33
+
34
+ paths = ActionView::PathRegistry.cast_file_system_resolvers(paths)
35
+ ActionView::PathSet.new(paths)
36
+ end
37
+
31
38
  # Append a path to the list of view paths for this controller.
32
39
  #
33
40
  # ==== Parameters
@@ -35,7 +42,7 @@ module ActionView
35
42
  # the default view path. You may also provide a custom view path
36
43
  # (see ActionView::PathSet for more information)
37
44
  def append_view_path(path)
38
- self._view_paths = view_paths + Array(path)
45
+ self._view_paths = view_paths + _build_view_paths(path)
39
46
  end
40
47
 
41
48
  # Prepend a path to the list of view paths for this controller.
@@ -45,7 +52,7 @@ module ActionView
45
52
  # the default view path. You may also provide a custom view path
46
53
  # (see ActionView::PathSet for more information)
47
54
  def prepend_view_path(path)
48
- self._view_paths = ActionView::PathSet.new(Array(path) + view_paths)
55
+ self._view_paths = _build_view_paths(path) + view_paths
49
56
  end
50
57
 
51
58
  # A list of all of the default view paths for this controller.
@@ -59,7 +66,7 @@ module ActionView
59
66
  # * <tt>paths</tt> - If a PathSet is provided, use that;
60
67
  # otherwise, process the parameter into a PathSet.
61
68
  def view_paths=(paths)
62
- self._view_paths = ActionView::PathSet.new(Array(paths))
69
+ self._view_paths = _build_view_paths(paths)
63
70
  end
64
71
 
65
72
  private
@@ -70,30 +77,14 @@ module ActionView
70
77
  end
71
78
  end
72
79
 
73
- # :stopdoc:
74
- @all_view_paths = {}
75
-
76
- def self.get_view_paths(klass)
77
- @all_view_paths[klass] || get_view_paths(klass.superclass)
78
- end
79
-
80
- def self.set_view_paths(klass, paths)
81
- @all_view_paths[klass] = paths
82
- end
83
-
84
- def self.all_view_paths
85
- @all_view_paths.values.uniq
86
- end
87
- # :startdoc:
88
-
89
80
  # The prefixes used in render "foo" shortcuts.
90
81
  def _prefixes # :nodoc:
91
82
  self.class._prefixes
92
83
  end
93
84
 
94
- # <tt>LookupContext</tt> is the object responsible for holding all
85
+ # LookupContext is the object responsible for holding all
95
86
  # information required for looking up templates, i.e. view paths and
96
- # details. Check <tt>ActionView::LookupContext</tt> for more information.
87
+ # details. Check ActionView::LookupContext for more information.
97
88
  def lookup_context
98
89
  @_lookup_context ||=
99
90
  ActionView::LookupContext.new(self.class._view_paths, details_for_lookup, _prefixes)
@@ -103,24 +94,24 @@ module ActionView
103
94
  {}
104
95
  end
105
96
 
106
- # Append a path to the list of view paths for the current <tt>LookupContext</tt>.
97
+ # Append a path to the list of view paths for the current LookupContext.
107
98
  #
108
99
  # ==== Parameters
109
100
  # * <tt>path</tt> - If a String is provided, it gets converted into
110
101
  # the default view path. You may also provide a custom view path
111
102
  # (see ActionView::PathSet for more information)
112
103
  def append_view_path(path)
113
- lookup_context.view_paths.push(*path)
104
+ lookup_context.append_view_paths(self.class._build_view_paths(path))
114
105
  end
115
106
 
116
- # Prepend a path to the list of view paths for the current <tt>LookupContext</tt>.
107
+ # Prepend a path to the list of view paths for the current LookupContext.
117
108
  #
118
109
  # ==== Parameters
119
110
  # * <tt>path</tt> - If a String is provided, it gets converted into
120
111
  # the default view path. You may also provide a custom view path
121
112
  # (see ActionView::PathSet for more information)
122
113
  def prepend_view_path(path)
123
- lookup_context.view_paths.unshift(*path)
114
+ lookup_context.prepend_view_paths(self.class._build_view_paths(path))
124
115
  end
125
116
  end
126
117
  end
data/lib/action_view.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright (c) 2004-2022 David Heinemeier Hansson
4
+ # Copyright (c) David Heinemeier Hansson
5
5
  #
6
6
  # Permission is hereby granted, free of charge, to any person obtaining
7
7
  # a copy of this software and associated documentation files (the
@@ -26,7 +26,9 @@
26
26
  require "active_support"
27
27
  require "active_support/rails"
28
28
  require "action_view/version"
29
+ require "action_view/deprecator"
29
30
 
31
+ # :include: actionview/README.rdoc
30
32
  module ActionView
31
33
  extend ActiveSupport::Autoload
32
34
 
@@ -39,11 +41,14 @@ module ActionView
39
41
  autoload :Helpers
40
42
  autoload :LookupContext
41
43
  autoload :Layouts
44
+ autoload :PathRegistry
42
45
  autoload :PathSet
43
46
  autoload :RecordIdentifier
44
47
  autoload :Rendering
45
48
  autoload :RoutingUrlFor
46
49
  autoload :Template
50
+ autoload :TemplateDetails
51
+ autoload :TemplatePath
47
52
  autoload :UnboundTemplate
48
53
  autoload :ViewPaths
49
54
 
@@ -59,10 +64,7 @@ module ActionView
59
64
 
60
65
  autoload_at "action_view/template/resolver" do
61
66
  autoload :Resolver
62
- autoload :PathResolver
63
67
  autoload :FileSystemResolver
64
- autoload :OptimizedFileSystemResolver
65
- autoload :FallbackFileSystemResolver
66
68
  end
67
69
 
68
70
  autoload_at "action_view/buffers" do
@@ -73,6 +73,22 @@ Released under the MIT license
73
73
  return element[expando][key] = value;
74
74
  };
75
75
 
76
+ Rails.isContentEditable = function(element) {
77
+ var isEditable;
78
+ isEditable = false;
79
+ while (true) {
80
+ if (element.isContentEditable) {
81
+ isEditable = true;
82
+ break;
83
+ }
84
+ element = element.parentElement;
85
+ if (!element) {
86
+ break;
87
+ }
88
+ }
89
+ return isEditable;
90
+ };
91
+
76
92
  Rails.$ = function(selector) {
77
93
  return Array.prototype.slice.call(document.querySelectorAll(selector));
78
94
  };
@@ -395,9 +411,9 @@ Released under the MIT license
395
411
 
396
412
  }).call(this);
397
413
  (function() {
398
- var disableFormElement, disableFormElements, disableLinkElement, enableFormElement, enableFormElements, enableLinkElement, formElements, getData, isXhrRedirect, matches, setData, stopEverything;
414
+ var disableFormElement, disableFormElements, disableLinkElement, enableFormElement, enableFormElements, enableLinkElement, formElements, getData, isContentEditable, isXhrRedirect, matches, setData, stopEverything;
399
415
 
400
- matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, stopEverything = Rails.stopEverything, formElements = Rails.formElements;
416
+ matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, stopEverything = Rails.stopEverything, formElements = Rails.formElements, isContentEditable = Rails.isContentEditable;
401
417
 
402
418
  Rails.handleDisabledElement = function(e) {
403
419
  var element;
@@ -417,6 +433,9 @@ Released under the MIT license
417
433
  } else {
418
434
  element = e;
419
435
  }
436
+ if (isContentEditable(element)) {
437
+ return;
438
+ }
420
439
  if (matches(element, Rails.linkDisableSelector)) {
421
440
  return enableLinkElement(element);
422
441
  } else if (matches(element, Rails.buttonDisableSelector) || matches(element, Rails.formEnableSelector)) {
@@ -429,6 +448,9 @@ Released under the MIT license
429
448
  Rails.disableElement = function(e) {
430
449
  var element;
431
450
  element = e instanceof Event ? e.target : e;
451
+ if (isContentEditable(element)) {
452
+ return;
453
+ }
432
454
  if (matches(element, Rails.linkDisableSelector)) {
433
455
  return disableLinkElement(element);
434
456
  } else if (matches(element, Rails.buttonDisableSelector) || matches(element, Rails.formDisableSelector)) {
@@ -513,10 +535,12 @@ Released under the MIT license
513
535
 
514
536
  }).call(this);
515
537
  (function() {
516
- var stopEverything;
538
+ var isContentEditable, stopEverything;
517
539
 
518
540
  stopEverything = Rails.stopEverything;
519
541
 
542
+ isContentEditable = Rails.isContentEditable;
543
+
520
544
  Rails.handleMethod = function(e) {
521
545
  var csrfParam, csrfToken, form, formContent, href, link, method;
522
546
  link = this;
@@ -524,6 +548,9 @@ Released under the MIT license
524
548
  if (!method) {
525
549
  return;
526
550
  }
551
+ if (isContentEditable(this)) {
552
+ return;
553
+ }
527
554
  href = Rails.href(link);
528
555
  csrfToken = Rails.csrfToken();
529
556
  csrfParam = Rails.csrfParam();
@@ -545,10 +572,10 @@ Released under the MIT license
545
572
 
546
573
  }).call(this);
547
574
  (function() {
548
- var ajax, fire, getData, isCrossDomain, isRemote, matches, serializeElement, setData, stopEverything,
575
+ var ajax, fire, getData, isContentEditable, isCrossDomain, isRemote, matches, serializeElement, setData, stopEverything,
549
576
  slice = [].slice;
550
577
 
551
- matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, fire = Rails.fire, stopEverything = Rails.stopEverything, ajax = Rails.ajax, isCrossDomain = Rails.isCrossDomain, serializeElement = Rails.serializeElement;
578
+ matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, fire = Rails.fire, stopEverything = Rails.stopEverything, ajax = Rails.ajax, isCrossDomain = Rails.isCrossDomain, serializeElement = Rails.serializeElement, isContentEditable = Rails.isContentEditable;
552
579
 
553
580
  isRemote = function(element) {
554
581
  var value;
@@ -566,6 +593,10 @@ Released under the MIT license
566
593
  fire(element, 'ajax:stopped');
567
594
  return false;
568
595
  }
596
+ if (isContentEditable(element)) {
597
+ fire(element, 'ajax:stopped');
598
+ return false;
599
+ }
569
600
  withCredentials = element.getAttribute('data-with-credentials');
570
601
  dataType = element.getAttribute('data-type') || 'script';
571
602
  if (matches(element, Rails.formSubmitSelector)) {