actionview 7.2.2.1 → 8.1.2

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -71
  3. data/README.rdoc +1 -1
  4. data/lib/action_view/base.rb +11 -11
  5. data/lib/action_view/buffers.rb +1 -1
  6. data/lib/action_view/dependency_tracker/erb_tracker.rb +37 -28
  7. data/lib/action_view/dependency_tracker/ruby_tracker.rb +2 -19
  8. data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
  9. data/lib/action_view/dependency_tracker.rb +7 -1
  10. data/lib/action_view/digestor.rb +6 -2
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers/asset_tag_helper.rb +25 -6
  13. data/lib/action_view/helpers/atom_feed_helper.rb +1 -3
  14. data/lib/action_view/helpers/cache_helper.rb +10 -2
  15. data/lib/action_view/helpers/capture_helper.rb +2 -2
  16. data/lib/action_view/helpers/controller_helper.rb +6 -2
  17. data/lib/action_view/helpers/date_helper.rb +28 -4
  18. data/lib/action_view/helpers/form_helper.rb +103 -103
  19. data/lib/action_view/helpers/form_options_helper.rb +39 -35
  20. data/lib/action_view/helpers/form_tag_helper.rb +35 -25
  21. data/lib/action_view/helpers/javascript_helper.rb +5 -1
  22. data/lib/action_view/helpers/number_helper.rb +14 -0
  23. data/lib/action_view/helpers/output_safety_helper.rb +1 -2
  24. data/lib/action_view/helpers/rendering_helper.rb +160 -50
  25. data/lib/action_view/helpers/sanitize_helper.rb +6 -0
  26. data/lib/action_view/helpers/tag_helper.rb +57 -73
  27. data/lib/action_view/helpers/tags/base.rb +11 -9
  28. data/lib/action_view/helpers/tags/check_box.rb +9 -3
  29. data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -3
  30. data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
  31. data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
  32. data/lib/action_view/helpers/tags/file_field.rb +7 -2
  33. data/lib/action_view/helpers/tags/hidden_field.rb +1 -1
  34. data/lib/action_view/helpers/tags/label.rb +3 -10
  35. data/lib/action_view/helpers/tags/radio_button.rb +1 -1
  36. data/lib/action_view/helpers/tags/select.rb +6 -1
  37. data/lib/action_view/helpers/tags/select_renderer.rb +6 -4
  38. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  39. data/lib/action_view/helpers/tags/text_field.rb +1 -1
  40. data/lib/action_view/helpers/text_helper.rb +10 -3
  41. data/lib/action_view/helpers/translation_helper.rb +6 -1
  42. data/lib/action_view/helpers/url_helper.rb +39 -13
  43. data/lib/action_view/layouts.rb +7 -7
  44. data/lib/action_view/locale/en.yml +3 -0
  45. data/lib/action_view/log_subscriber.rb +1 -4
  46. data/lib/action_view/railtie.rb +12 -1
  47. data/lib/action_view/record_identifier.rb +22 -1
  48. data/lib/action_view/render_parser/prism_render_parser.rb +13 -1
  49. data/lib/action_view/render_parser/ripper_render_parser.rb +10 -1
  50. data/lib/action_view/renderer/partial_renderer.rb +18 -2
  51. data/lib/action_view/renderer/streaming_template_renderer.rb +8 -2
  52. data/lib/action_view/renderer/template_renderer.rb +3 -3
  53. data/lib/action_view/rendering.rb +2 -3
  54. data/lib/action_view/structured_event_subscriber.rb +97 -0
  55. data/lib/action_view/template/error.rb +18 -3
  56. data/lib/action_view/template/handlers/erb/erubi.rb +1 -1
  57. data/lib/action_view/template/handlers/erb.rb +77 -44
  58. data/lib/action_view/template/raw_file.rb +4 -0
  59. data/lib/action_view/template/resolver.rb +0 -1
  60. data/lib/action_view/template.rb +3 -4
  61. data/lib/action_view/test_case.rb +50 -53
  62. data/lib/action_view.rb +4 -0
  63. metadata +15 -16
@@ -40,18 +40,22 @@ module ActionView
40
40
 
41
41
  # Translate an error location returned by ErrorHighlight to the correct
42
42
  # source location inside the template.
43
- def translate_location(spot, backtrace_location, source)
44
- # Tokenize the source line
45
- tokens = ::ERB::Util.tokenize(source.lines[backtrace_location.lineno - 1])
46
- new_first_column = find_offset(spot[:snippet], tokens, spot[:first_column])
47
- lineno_delta = spot[:first_lineno] - backtrace_location.lineno
43
+ def translate_location(spot, _backtrace_location, source)
44
+ compiled = spot[:script_lines]
45
+ highlight = compiled[spot[:first_lineno] - 1]&.byteslice((spot[:first_column] - 1)...spot[:last_column])
46
+ return nil if highlight.blank?
47
+
48
+ source_lines = source.lines
49
+ lineno_delta = find_lineno_offset(compiled, source_lines, highlight, spot[:first_lineno])
50
+
51
+ tokens = ::ERB::Util.tokenize(source_lines[spot[:first_lineno] - lineno_delta - 1])
52
+ column_delta = find_offset(spot[:snippet], tokens, spot[:first_column])
53
+
48
54
  spot[:first_lineno] -= lineno_delta
49
55
  spot[:last_lineno] -= lineno_delta
50
-
51
- column_delta = spot[:first_column] - new_first_column
52
56
  spot[:first_column] -= column_delta
53
57
  spot[:last_column] -= column_delta
54
- spot[:script_lines] = source.lines
58
+ spot[:script_lines] = source_lines
55
59
 
56
60
  spot
57
61
  rescue NotImplementedError, LocationParsingError
@@ -82,7 +86,7 @@ module ActionView
82
86
  }
83
87
 
84
88
  if ActionView::Base.annotate_rendered_view_with_filenames && template.format == :html
85
- options[:preamble] = "@output_buffer.safe_append='<!-- BEGIN #{template.short_identifier} -->';"
89
+ options[:preamble] = "@output_buffer.safe_append='<!-- BEGIN #{template.short_identifier}\n-->';"
86
90
  options[:postamble] = "@output_buffer.safe_append='<!-- END #{template.short_identifier} -->';@output_buffer"
87
91
  end
88
92
 
@@ -105,51 +109,80 @@ module ActionView
105
109
  raise WrongEncodingError.new(string, string.encoding)
106
110
  end
107
111
 
112
+ # Return the offset between the error lineno and the source lineno.
113
+ # Searches in reverse from the backtrace lineno so we have a better
114
+ # chance of finding the correct line
115
+ #
116
+ # The compiled template is likely to be longer than the source.
117
+ # Use the difference between the compiled and source sizes to
118
+ # determine the earliest line that could contain the highlight.
119
+ def find_lineno_offset(compiled, source_lines, highlight, error_lineno)
120
+ first_index = error_lineno - 1 - compiled.size + source_lines.size
121
+ first_index = 0 if first_index < 0
122
+
123
+ last_index = error_lineno - 1
124
+ last_index = source_lines.size - 1 if last_index >= source_lines.size
125
+
126
+ last_index.downto(first_index) do |line_index|
127
+ next unless source_lines[line_index].include?(highlight)
128
+ return error_lineno - 1 - line_index
129
+ end
130
+
131
+ raise LocationParsingError, "Couldn't find code snippet"
132
+ end
133
+
134
+ # Find which token in the source template spans the byte range that
135
+ # contains the error_column, then return the offset compared to the
136
+ # original source template.
137
+ #
138
+ # Iterate consecutive pairs of CODE or TEXT tokens, requiring
139
+ # a match of the first token before matching either token.
140
+ #
141
+ # For example, if we want to find tokens A, B, C, we do the following:
142
+ # 1. Find a match for A: test error_column or advance scanner.
143
+ # 2. Find a match for B or A:
144
+ # a. If B: start over with next token set (B, C).
145
+ # b. If A: test error_column or advance scanner.
146
+ # c. Otherwise: Advance 1 byte
147
+ #
148
+ # Prioritize matching the next token over the current token once
149
+ # a match for the current token has been found. This is to prevent
150
+ # the current token from looping past the next token if they both
151
+ # match (i.e. if the current token is a single space character).
108
152
  def find_offset(compiled, source_tokens, error_column)
109
153
  compiled = StringScanner.new(compiled)
154
+ offset_source_tokens(source_tokens).each_cons(2) do |(name, str, offset), (_, next_str, _)|
155
+ matched_str = false
110
156
 
111
- passed_tokens = []
157
+ until compiled.eos?
158
+ if matched_str && next_str && compiled.match?(next_str)
159
+ break
160
+ elsif compiled.match?(str)
161
+ matched_str = true
112
162
 
113
- while tok = source_tokens.shift
114
- tok_name, str = *tok
115
- case tok_name
116
- when :TEXT
117
- loop do
118
- break if compiled.match?(str)
119
- compiled.getch
120
- end
121
- raise LocationParsingError unless compiled.scan(str)
122
- when :CODE
123
- if compiled.pos > error_column
124
- raise LocationParsingError, "We went too far"
125
- end
163
+ if name == :CODE && compiled.pos <= error_column && compiled.pos + str.bytesize >= error_column
164
+ return compiled.pos - offset
165
+ end
126
166
 
127
- if compiled.pos + str.bytesize >= error_column
128
- offset = error_column - compiled.pos
129
- return passed_tokens.map(&:last).join.bytesize + offset
167
+ compiled.pos += str.bytesize
130
168
  else
131
- unless compiled.scan(str)
132
- raise LocationParsingError, "Couldn't find code snippet"
133
- end
134
- end
135
- when :OPEN
136
- next_tok = source_tokens.first.last
137
- loop do
138
- break if compiled.match?(next_tok)
139
- compiled.getch
169
+ compiled.pos += 1
140
170
  end
141
- when :CLOSE
142
- next_tok = source_tokens.first.last
143
- loop do
144
- break if compiled.match?(next_tok)
145
- compiled.getch
146
- end
147
- else
148
- raise LocationParsingError, "Not implemented: #{tok.first}"
149
171
  end
172
+ end
173
+
174
+ raise LocationParsingError, "Couldn't find code snippet"
175
+ end
150
176
 
151
- passed_tokens << tok
177
+ def offset_source_tokens(source_tokens)
178
+ source_offset = 0
179
+ with_offset = source_tokens.filter_map do |name, str|
180
+ result = [:CODE, str, source_offset] if name == :CODE || name == :PLAIN
181
+ result = [:TEXT, str, source_offset] if name == :TEXT
182
+ source_offset += str.bytesize
183
+ result
152
184
  end
185
+ with_offset << [:EOS, nil, source_offset]
153
186
  end
154
187
  end
155
188
  end
@@ -20,6 +20,10 @@ module ActionView # :nodoc:
20
20
  def render(*args)
21
21
  ::File.read(@filename)
22
22
  end
23
+
24
+ def supports_streaming?
25
+ false
26
+ end
23
27
  end
24
28
  end
25
29
  end
@@ -4,7 +4,6 @@ require "pathname"
4
4
  require "active_support/core_ext/class"
5
5
  require "active_support/core_ext/module/attribute_accessors"
6
6
  require "action_view/template"
7
- require "thread"
8
7
  require "concurrent/map"
9
8
 
10
9
  module ActionView
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "thread"
4
3
  require "delegate"
5
4
 
6
5
  module ActionView
@@ -8,7 +7,7 @@ module ActionView
8
7
  class Template
9
8
  extend ActiveSupport::Autoload
10
9
 
11
- STRICT_LOCALS_REGEX = /\#\s+locals:\s+\((.*)\)/
10
+ STRICT_LOCALS_REGEX = /\#\s+locals:\s+\((.*?)\)(?=\s*-?%>|\s*$)/m
12
11
 
13
12
  # === Encodings in ActionView::Template
14
13
  #
@@ -357,7 +356,7 @@ module ActionView
357
356
 
358
357
  # This method is responsible for marking a template as having strict locals
359
358
  # which means the template can only accept the locals defined in a magic
360
- # comment. For example, if your template acceps the locals +title+ and
359
+ # comment. For example, if your template accepts the locals +title+ and
361
360
  # +comment_count+, add the following to your template file:
362
361
  #
363
362
  # <%# locals: (title: "Default title", comment_count: 0) %>
@@ -367,7 +366,7 @@ module ActionView
367
366
  def strict_locals!
368
367
  if @strict_locals == NONE
369
368
  self.source.sub!(STRICT_LOCALS_REGEX, "")
370
- @strict_locals = $1
369
+ @strict_locals = $1&.rstrip
371
370
 
372
371
  return if @strict_locals.nil? # Magic comment not found
373
372
 
@@ -60,7 +60,56 @@ module ActionView
60
60
  include ActiveSupport::Testing::ConstantLookup
61
61
 
62
62
  delegate :lookup_context, to: :controller
63
- attr_accessor :controller, :request, :output_buffer, :rendered
63
+ attr_accessor :controller, :request, :output_buffer
64
+
65
+ # Returns the content rendered by the last +render+ call.
66
+ #
67
+ # The returned object behaves like a string but also exposes a number of methods
68
+ # that allows you to parse the content string in formats registered using
69
+ # <tt>.register_parser</tt>.
70
+ #
71
+ # By default includes the following parsers:
72
+ #
73
+ # +.html+
74
+ #
75
+ # Parse the <tt>rendered</tt> content String into HTML. By default, this means
76
+ # a <tt>Nokogiri::XML::Node</tt>.
77
+ #
78
+ # test "renders HTML" do
79
+ # article = Article.create!(title: "Hello, world")
80
+ #
81
+ # render partial: "articles/article", locals: { article: article }
82
+ #
83
+ # assert_pattern { rendered.html.at("main h1") => { content: "Hello, world" } }
84
+ # end
85
+ #
86
+ # To parse the rendered content into a <tt>Capybara::Simple::Node</tt>,
87
+ # re-register an <tt>:html</tt> parser with a call to
88
+ # <tt>Capybara.string</tt>:
89
+ #
90
+ # register_parser :html, -> rendered { Capybara.string(rendered) }
91
+ #
92
+ # test "renders HTML" do
93
+ # article = Article.create!(title: "Hello, world")
94
+ #
95
+ # render partial: article
96
+ #
97
+ # rendered.html.assert_css "h1", text: "Hello, world"
98
+ # end
99
+ #
100
+ # +.json+
101
+ #
102
+ # Parse the <tt>rendered</tt> content String into JSON. By default, this means
103
+ # a <tt>ActiveSupport::HashWithIndifferentAccess</tt>.
104
+ #
105
+ # test "renders JSON" do
106
+ # article = Article.create!(title: "Hello, world")
107
+ #
108
+ # render formats: :json, partial: "articles/article", locals: { article: article }
109
+ #
110
+ # assert_pattern { rendered.json => { title: "Hello, world" } }
111
+ # end
112
+ attr_accessor :rendered
64
113
 
65
114
  module ClassMethods
66
115
  def inherited(descendant) # :nodoc:
@@ -243,57 +292,6 @@ module ActionView
243
292
  @_rendered_views ||= RenderedViewsCollection.new
244
293
  end
245
294
 
246
- ##
247
- # :method: rendered
248
- #
249
- # Returns the content rendered by the last +render+ call.
250
- #
251
- # The returned object behaves like a string but also exposes a number of methods
252
- # that allows you to parse the content string in formats registered using
253
- # <tt>.register_parser</tt>.
254
- #
255
- # By default includes the following parsers:
256
- #
257
- # +.html+
258
- #
259
- # Parse the <tt>rendered</tt> content String into HTML. By default, this means
260
- # a <tt>Nokogiri::XML::Node</tt>.
261
- #
262
- # test "renders HTML" do
263
- # article = Article.create!(title: "Hello, world")
264
- #
265
- # render partial: "articles/article", locals: { article: article }
266
- #
267
- # assert_pattern { rendered.html.at("main h1") => { content: "Hello, world" } }
268
- # end
269
- #
270
- # To parse the rendered content into a <tt>Capybara::Simple::Node</tt>,
271
- # re-register an <tt>:html</tt> parser with a call to
272
- # <tt>Capybara.string</tt>:
273
- #
274
- # register_parser :html, -> rendered { Capybara.string(rendered) }
275
- #
276
- # test "renders HTML" do
277
- # article = Article.create!(title: "Hello, world")
278
- #
279
- # render partial: article
280
- #
281
- # rendered.html.assert_css "h1", text: "Hello, world"
282
- # end
283
- #
284
- # +.json+
285
- #
286
- # Parse the <tt>rendered</tt> content String into JSON. By default, this means
287
- # a <tt>ActiveSupport::HashWithIndifferentAccess</tt>.
288
- #
289
- # test "renders JSON" do
290
- # article = Article.create!(title: "Hello, world")
291
- #
292
- # render formats: :json, partial: "articles/article", locals: { article: article }
293
- #
294
- # assert_pattern { rendered.json => { title: "Hello, world" } }
295
- # end
296
-
297
295
  def _routes
298
296
  @controller._routes if @controller.respond_to?(:_routes)
299
297
  end
@@ -301,7 +299,6 @@ module ActionView
301
299
  class RenderedViewContent < String # :nodoc:
302
300
  end
303
301
 
304
- # Need to experiment if this priority is the best one: rendered => output_buffer
305
302
  class RenderedViewsCollection
306
303
  def initialize
307
304
  @rendered_views ||= Hash.new { |hash, key| hash[key] = [] }
data/lib/action_view.rb CHANGED
@@ -81,6 +81,7 @@ module ActionView
81
81
  autoload :MissingTemplate
82
82
  autoload :ActionViewError
83
83
  autoload :EncodingError
84
+ autoload :StrictLocalsError
84
85
  autoload :TemplateError
85
86
  autoload :SyntaxErrorInTemplate
86
87
  autoload :WrongEncodingError
@@ -90,6 +91,9 @@ module ActionView
90
91
  autoload :CacheExpiry
91
92
  autoload :TestCase
92
93
 
94
+ singleton_class.attr_accessor :render_tracker
95
+ self.render_tracker = :regex
96
+
93
97
  def self.eager_load!
94
98
  super
95
99
  ActionView::Helpers.eager_load!
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionview
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.2.2.1
4
+ version: 8.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-10 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - '='
18
17
  - !ruby/object:Gem::Version
19
- version: 7.2.2.1
18
+ version: 8.1.2
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - '='
25
24
  - !ruby/object:Gem::Version
26
- version: 7.2.2.1
25
+ version: 8.1.2
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: builder
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -86,28 +85,28 @@ dependencies:
86
85
  requirements:
87
86
  - - '='
88
87
  - !ruby/object:Gem::Version
89
- version: 7.2.2.1
88
+ version: 8.1.2
90
89
  type: :development
91
90
  prerelease: false
92
91
  version_requirements: !ruby/object:Gem::Requirement
93
92
  requirements:
94
93
  - - '='
95
94
  - !ruby/object:Gem::Version
96
- version: 7.2.2.1
95
+ version: 8.1.2
97
96
  - !ruby/object:Gem::Dependency
98
97
  name: activemodel
99
98
  requirement: !ruby/object:Gem::Requirement
100
99
  requirements:
101
100
  - - '='
102
101
  - !ruby/object:Gem::Version
103
- version: 7.2.2.1
102
+ version: 8.1.2
104
103
  type: :development
105
104
  prerelease: false
106
105
  version_requirements: !ruby/object:Gem::Requirement
107
106
  requirements:
108
107
  - - '='
109
108
  - !ruby/object:Gem::Version
110
- version: 7.2.2.1
109
+ version: 8.1.2
111
110
  description: Simple, battle-tested conventions and helpers for building web pages.
112
111
  email: david@loudthinking.com
113
112
  executables: []
@@ -127,6 +126,7 @@ files:
127
126
  - lib/action_view/dependency_tracker.rb
128
127
  - lib/action_view/dependency_tracker/erb_tracker.rb
129
128
  - lib/action_view/dependency_tracker/ruby_tracker.rb
129
+ - lib/action_view/dependency_tracker/wildcard_resolver.rb
130
130
  - lib/action_view/deprecator.rb
131
131
  - lib/action_view/digestor.rb
132
132
  - lib/action_view/flows.rb
@@ -216,6 +216,7 @@ files:
216
216
  - lib/action_view/renderer/template_renderer.rb
217
217
  - lib/action_view/rendering.rb
218
218
  - lib/action_view/routing_url_for.rb
219
+ - lib/action_view/structured_event_subscriber.rb
219
220
  - lib/action_view/tasks/cache_digests.rake
220
221
  - lib/action_view/template.rb
221
222
  - lib/action_view/template/error.rb
@@ -246,12 +247,11 @@ licenses:
246
247
  - MIT
247
248
  metadata:
248
249
  bug_tracker_uri: https://github.com/rails/rails/issues
249
- changelog_uri: https://github.com/rails/rails/blob/v7.2.2.1/actionview/CHANGELOG.md
250
- documentation_uri: https://api.rubyonrails.org/v7.2.2.1/
250
+ changelog_uri: https://github.com/rails/rails/blob/v8.1.2/actionview/CHANGELOG.md
251
+ documentation_uri: https://api.rubyonrails.org/v8.1.2/
251
252
  mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
252
- source_code_uri: https://github.com/rails/rails/tree/v7.2.2.1/actionview
253
+ source_code_uri: https://github.com/rails/rails/tree/v8.1.2/actionview
253
254
  rubygems_mfa_required: 'true'
254
- post_install_message:
255
255
  rdoc_options: []
256
256
  require_paths:
257
257
  - lib
@@ -259,7 +259,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
259
259
  requirements:
260
260
  - - ">="
261
261
  - !ruby/object:Gem::Version
262
- version: 3.1.0
262
+ version: 3.2.0
263
263
  required_rubygems_version: !ruby/object:Gem::Requirement
264
264
  requirements:
265
265
  - - ">="
@@ -267,8 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
267
267
  version: '0'
268
268
  requirements:
269
269
  - none
270
- rubygems_version: 3.5.22
271
- signing_key:
270
+ rubygems_version: 4.0.3
272
271
  specification_version: 4
273
272
  summary: Rendering framework putting the V in MVC (part of Rails).
274
273
  test_files: []