actionview 7.1.5.1 → 7.2.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -416
  3. data/README.rdoc +1 -1
  4. data/lib/action_view/base.rb +24 -9
  5. data/lib/action_view/cache_expiry.rb +9 -3
  6. data/lib/action_view/dependency_tracker/{ripper_tracker.rb → ruby_tracker.rb} +4 -3
  7. data/lib/action_view/dependency_tracker.rb +1 -1
  8. data/lib/action_view/digestor.rb +6 -2
  9. data/lib/action_view/gem_version.rb +3 -3
  10. data/lib/action_view/helpers/asset_tag_helper.rb +19 -7
  11. data/lib/action_view/helpers/atom_feed_helper.rb +1 -1
  12. data/lib/action_view/helpers/cache_helper.rb +2 -2
  13. data/lib/action_view/helpers/csrf_helper.rb +1 -1
  14. data/lib/action_view/helpers/date_helper.rb +8 -1
  15. data/lib/action_view/helpers/form_helper.rb +222 -217
  16. data/lib/action_view/helpers/form_options_helper.rb +6 -3
  17. data/lib/action_view/helpers/form_tag_helper.rb +80 -47
  18. data/lib/action_view/helpers/output_safety_helper.rb +5 -6
  19. data/lib/action_view/helpers/tag_helper.rb +208 -18
  20. data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
  21. data/lib/action_view/helpers/text_helper.rb +11 -4
  22. data/lib/action_view/helpers/url_helper.rb +3 -77
  23. data/lib/action_view/layouts.rb +8 -10
  24. data/lib/action_view/log_subscriber.rb +8 -4
  25. data/lib/action_view/railtie.rb +0 -1
  26. data/lib/action_view/render_parser/prism_render_parser.rb +127 -0
  27. data/lib/action_view/{ripper_ast_parser.rb → render_parser/ripper_render_parser.rb} +152 -9
  28. data/lib/action_view/render_parser.rb +21 -169
  29. data/lib/action_view/renderer/abstract_renderer.rb +1 -1
  30. data/lib/action_view/renderer/partial_renderer.rb +2 -2
  31. data/lib/action_view/renderer/renderer.rb +32 -38
  32. data/lib/action_view/renderer/template_renderer.rb +3 -3
  33. data/lib/action_view/rendering.rb +4 -4
  34. data/lib/action_view/template/error.rb +11 -0
  35. data/lib/action_view/template/handlers/erb.rb +45 -37
  36. data/lib/action_view/template/renderable.rb +7 -1
  37. data/lib/action_view/template/resolver.rb +0 -2
  38. data/lib/action_view/template.rb +36 -8
  39. data/lib/action_view/test_case.rb +7 -10
  40. data/lib/action_view.rb +1 -0
  41. metadata +30 -18
@@ -80,6 +80,23 @@ module ActionView # :nodoc:
80
80
  # This is useful in cases where you aren't sure if the local variable has been assigned. Alternatively, you could also use
81
81
  # <tt>defined? headline</tt> to first check if the variable has been assigned before using it.
82
82
  #
83
+ # By default, templates will accept any <tt>locals</tt> as keyword arguments. To restrict what <tt>locals</tt> a template accepts, add a <tt>locals:</tt> magic comment:
84
+ #
85
+ # <%# locals: (headline:) %>
86
+ #
87
+ # Headline: <%= headline %>
88
+ #
89
+ # In cases where the local variables are optional, declare the keyword argument with a default value:
90
+ #
91
+ # <%# locals: (headline: nil) %>
92
+ #
93
+ # <% unless headline.nil? %>
94
+ # Headline: <%= headline %>
95
+ # <% end %>
96
+ #
97
+ # Read more about strict locals in {Action View Overview}[https://guides.rubyonrails.org/action_view_overview.html#strict-locals]
98
+ # in the guides.
99
+ #
83
100
  # === Template caching
84
101
  #
85
102
  # By default, \Rails will compile each template to a method in order to render it. When you alter a template,
@@ -248,16 +265,14 @@ module ActionView # :nodoc:
248
265
 
249
266
  if has_strict_locals
250
267
  begin
251
- public_send(method, buffer, **locals, &block)
268
+ public_send(method, locals, buffer, **locals, &block)
252
269
  rescue ArgumentError => argument_error
253
- raise(
254
- ArgumentError,
255
- argument_error.
256
- message.
257
- gsub("unknown keyword:", "unknown local:").
258
- gsub("missing keyword:", "missing local:").
259
- gsub("no keywords accepted", "no locals accepted")
260
- )
270
+ public_send_line = __LINE__ - 2
271
+ frame = argument_error.backtrace_locations[1]
272
+ if frame.path == __FILE__ && frame.lineno == public_send_line
273
+ raise StrictLocalsError.new(argument_error, @current_template)
274
+ end
275
+ raise
261
276
  end
262
277
  else
263
278
  public_send(method, locals, buffer, &block)
@@ -10,16 +10,17 @@ module ActionView
10
10
  @watcher = nil
11
11
  @previous_change = false
12
12
 
13
- rebuild_watcher
14
-
15
13
  ActionView::PathRegistry.file_system_resolver_hooks << method(:rebuild_watcher)
16
14
  end
17
15
 
18
16
  def updated?
17
+ build_watcher unless @watcher
19
18
  @previous_change || @watcher.updated?
20
19
  end
21
20
 
22
21
  def execute
22
+ return unless @watcher
23
+
23
24
  watcher = nil
24
25
  @mutex.synchronize do
25
26
  @previous_change = false
@@ -33,7 +34,7 @@ module ActionView
33
34
  ActionView::LookupContext::DetailsKey.clear
34
35
  end
35
36
 
36
- def rebuild_watcher
37
+ def build_watcher
37
38
  @mutex.synchronize do
38
39
  old_watcher = @watcher
39
40
 
@@ -51,6 +52,11 @@ module ActionView
51
52
  end
52
53
  end
53
54
 
55
+ def rebuild_watcher
56
+ return unless @watcher
57
+ build_watcher
58
+ end
59
+
54
60
  def dirs_to_watch
55
61
  all_view_paths.uniq.sort
56
62
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActionView
4
4
  class DependencyTracker # :nodoc:
5
- class RipperTracker # :nodoc:
5
+ class RubyTracker # :nodoc:
6
6
  EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
7
7
 
8
8
  def self.call(name, template, view_paths = nil)
@@ -17,8 +17,9 @@ module ActionView
17
17
  true
18
18
  end
19
19
 
20
- def initialize(name, template, view_paths = nil)
20
+ def initialize(name, template, view_paths = nil, parser_class: RenderParser::Default)
21
21
  @name, @template, @view_paths = name, template, view_paths
22
+ @parser_class = parser_class
22
23
  end
23
24
 
24
25
  private
@@ -29,7 +30,7 @@ module ActionView
29
30
 
30
31
  compiled_source = template.handler.call(template, template.source)
31
32
 
32
- RenderParser.new(@name, compiled_source).render_calls.filter_map do |render_call|
33
+ @parser_class.new(@name, compiled_source).render_calls.filter_map do |render_call|
33
34
  next if render_call.end_with?("/_")
34
35
  render_call.gsub(%r|/_|, "/")
35
36
  end
@@ -9,7 +9,7 @@ module ActionView
9
9
  extend ActiveSupport::Autoload
10
10
 
11
11
  autoload :ERBTracker
12
- autoload :RipperTracker
12
+ autoload :RubyTracker
13
13
 
14
14
  @trackers = Concurrent::Map.new
15
15
 
@@ -107,8 +107,12 @@ module ActionView
107
107
  end.join("-")
108
108
  end
109
109
 
110
- def to_dep_map
111
- children.any? ? { name => children.map(&:to_dep_map) } : name
110
+ def to_dep_map(seen = Set.new.compare_by_identity)
111
+ if seen.add?(self)
112
+ children.any? ? { name => children.map { |c| c.to_dep_map(seen) } } : name
113
+ else # the tree has a cycle
114
+ name
115
+ end
112
116
  end
113
117
  end
114
118
 
@@ -8,9 +8,9 @@ module ActionView
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 7
11
- MINOR = 1
12
- TINY = 5
13
- PRE = "1"
11
+ MINOR = 2
12
+ TINY = 3
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -68,6 +68,8 @@ module ActionView
68
68
  # attribute, which indicates to the browser that the script is meant to
69
69
  # be executed after the document has been parsed. Additionally, prevents
70
70
  # sending the Preload Links header.
71
+ # * <tt>:nopush</tt> - Specify if the use of server push is not desired
72
+ # for the script. Defaults to +true+.
71
73
  #
72
74
  # Any other specified options will be treated as HTML attributes for the
73
75
  # +script+ tag.
@@ -113,11 +115,11 @@ module ActionView
113
115
  path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
114
116
  preload_links = []
115
117
  use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
116
- nopush = options["nopush"].nil? ? true : options.delete("nopush")
118
+ nopush = options["nopush"].nil? || options.delete("nopush")
117
119
  crossorigin = options.delete("crossorigin")
118
120
  crossorigin = "anonymous" if crossorigin == true
119
121
  integrity = options["integrity"]
120
- rel = options["type"] == "module" ? "modulepreload" : "preload"
122
+ rel = options["type"] == "module" || options["type"] == :module ? "modulepreload" : "preload"
121
123
 
122
124
  sources_tags = sources.uniq.map { |source|
123
125
  href = path_to_javascript(source, path_options)
@@ -166,6 +168,10 @@ module ActionView
166
168
  # that path.
167
169
  # * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
168
170
  # when it is set to true.
171
+ # * <tt>:nonce</tt> - When set to true, adds an automatic nonce value if
172
+ # you have Content Security Policy enabled.
173
+ # * <tt>:nopush</tt> - Specify if the use of server push is not desired
174
+ # for the stylesheet. Defaults to +true+.
169
175
  #
170
176
  # ==== Examples
171
177
  #
@@ -190,6 +196,9 @@ module ActionView
190
196
  # stylesheet_link_tag "random.styles", "/css/stylish"
191
197
  # # => <link href="/assets/random.styles" rel="stylesheet" />
192
198
  # # <link href="/css/stylish.css" rel="stylesheet" />
199
+ #
200
+ # stylesheet_link_tag "style", nonce: true
201
+ # # => <link href="/assets/style.css" rel="stylesheet" nonce="..." />
193
202
  def stylesheet_link_tag(*sources)
194
203
  options = sources.extract_options!.stringify_keys
195
204
  path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
@@ -197,7 +206,7 @@ module ActionView
197
206
  preload_links = []
198
207
  crossorigin = options.delete("crossorigin")
199
208
  crossorigin = "anonymous" if crossorigin == true
200
- nopush = options["nopush"].nil? ? true : options.delete("nopush")
209
+ nopush = options["nopush"].nil? || options.delete("nopush")
201
210
  integrity = options["integrity"]
202
211
 
203
212
  sources_tags = sources.uniq.map { |source|
@@ -214,6 +223,9 @@ module ActionView
214
223
  "crossorigin" => crossorigin,
215
224
  "href" => href
216
225
  }.merge!(options)
226
+ if tag_options["nonce"] == true
227
+ tag_options["nonce"] = content_security_policy_nonce
228
+ end
217
229
 
218
230
  if apply_stylesheet_media_default && tag_options["media"].blank?
219
231
  tag_options["media"] = "screen"
@@ -349,15 +361,15 @@ module ActionView
349
361
  crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
350
362
  integrity = options[:integrity]
351
363
  nopush = options.delete(:nopush) || false
352
- rel = mime_type == "module" ? "modulepreload" : "preload"
364
+ rel = mime_type == "module" || mime_type == :module ? "modulepreload" : "preload"
353
365
 
354
- link_tag = tag.link(**{
366
+ link_tag = tag.link(
355
367
  rel: rel,
356
368
  href: href,
357
369
  as: as_type,
358
370
  type: mime_type,
359
- crossorigin: crossorigin
360
- }.merge!(options.symbolize_keys))
371
+ crossorigin: crossorigin,
372
+ **options.symbolize_keys)
361
373
 
362
374
  preload_link = "<#{href}>; rel=#{rel}; as=#{as_type}"
363
375
  preload_link += "; type=#{mime_type}" if mime_type
@@ -172,7 +172,7 @@ module ActionView
172
172
 
173
173
  # Creates an entry tag for a specific record and prefills the id using class and id.
174
174
  #
175
- # Options:
175
+ # ==== Options
176
176
  #
177
177
  # * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
178
178
  # * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
@@ -189,7 +189,7 @@ module ActionView
189
189
  CachingRegistry.caching?
190
190
  end
191
191
 
192
- # Raises +UncacheableFragmentError+ when called from within a +cache+ block.
192
+ # Raises UncacheableFragmentError when called from within a +cache+ block.
193
193
  #
194
194
  # Useful to denote helper methods that can't participate in fragment caching:
195
195
  #
@@ -198,7 +198,7 @@ module ActionView
198
198
  # "#{project.name} - #{Time.now}"
199
199
  # end
200
200
  #
201
- # # Which will then raise if used within a +cache+ block:
201
+ # # Which will then raise if used within a `cache` block:
202
202
  # <% cache project do %>
203
203
  # <%= project_name_with_time(project) %>
204
204
  # <% end %>
@@ -17,7 +17,7 @@ module ActionView
17
17
  # You don't need to use these tags for regular forms as they generate their own hidden fields.
18
18
  #
19
19
  # For Ajax requests other than GETs, extract the "csrf-token" from the meta-tag and send as the
20
- # +X-CSRF-Token+ HTTP header. If you are using rails-ujs, this happens automatically.
20
+ # +X-CSRF-Token+ HTTP header.
21
21
  #
22
22
  def csrf_meta_tags
23
23
  if defined?(protect_against_forgery?) && protect_against_forgery?
@@ -136,8 +136,15 @@ module ActionView
136
136
  from_year += 1 if from_time.month >= 3
137
137
  to_year = to_time.year
138
138
  to_year -= 1 if to_time.month < 3
139
- leap_years = (from_year > to_year) ? 0 : (from_year..to_year).count { |x| Date.leap?(x) }
139
+
140
+ leap_years = if from_year > to_year
141
+ 0
142
+ else
143
+ fyear = from_year - 1
144
+ (to_year / 4 - to_year / 100 + to_year / 400) - (fyear / 4 - fyear / 100 + fyear / 400)
145
+ end
140
146
  minute_offset_for_leap_year = leap_years * 1440
147
+
141
148
  # Discount the leap year days when calculating year distance.
142
149
  # e.g. if there are 20 leap year days between 2 dates having the same day
143
150
  # and month then based on 365 days calculation