actionview 7.2.3 → 8.0.0.beta1

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -186
  3. data/README.rdoc +1 -1
  4. data/lib/action_view/base.rb +9 -6
  5. data/lib/action_view/dependency_tracker/erb_tracker.rb +35 -27
  6. data/lib/action_view/dependency_tracker/ruby_tracker.rb +2 -19
  7. data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
  8. data/lib/action_view/dependency_tracker.rb +1 -0
  9. data/lib/action_view/digestor.rb +2 -6
  10. data/lib/action_view/gem_version.rb +4 -4
  11. data/lib/action_view/helpers/asset_tag_helper.rb +4 -4
  12. data/lib/action_view/helpers/atom_feed_helper.rb +1 -1
  13. data/lib/action_view/helpers/cache_helper.rb +10 -2
  14. data/lib/action_view/helpers/date_helper.rb +1 -8
  15. data/lib/action_view/helpers/form_helper.rb +90 -91
  16. data/lib/action_view/helpers/form_options_helper.rb +19 -20
  17. data/lib/action_view/helpers/form_tag_helper.rb +18 -16
  18. data/lib/action_view/helpers/output_safety_helper.rb +2 -1
  19. data/lib/action_view/helpers/rendering_helper.rb +160 -50
  20. data/lib/action_view/helpers/tag_helper.rb +41 -38
  21. data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -3
  22. data/lib/action_view/helpers/tags/collection_helpers.rb +1 -2
  23. data/lib/action_view/helpers/text_helper.rb +4 -11
  24. data/lib/action_view/helpers/url_helper.rb +2 -4
  25. data/lib/action_view/layouts.rb +7 -7
  26. data/lib/action_view/render_parser/prism_render_parser.rb +13 -1
  27. data/lib/action_view/render_parser/ripper_render_parser.rb +10 -1
  28. data/lib/action_view/renderer/partial_renderer.rb +2 -2
  29. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -1
  30. data/lib/action_view/renderer/template_renderer.rb +3 -3
  31. data/lib/action_view/rendering.rb +2 -3
  32. data/lib/action_view/template/error.rb +0 -11
  33. data/lib/action_view/template/handlers/erb.rb +37 -45
  34. data/lib/action_view/template/resolver.rb +0 -1
  35. data/lib/action_view/template.rb +3 -14
  36. data/lib/action_view/test_case.rb +1 -0
  37. data/lib/action_view.rb +0 -1
  38. metadata +17 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 773338461dd6a54e8b6efa075c2be80d8f8c975ee46bd2167bc7e2fcd8e78f35
4
- data.tar.gz: 22244120a030dfc49034d8d790fa86013b1de42b5ee7acbe75243580c9eec7c1
3
+ metadata.gz: dec233fd596879151b606bffdbf29f2f8c46fe41675eeb91744d352265254b9d
4
+ data.tar.gz: 3623f09bcc8011ac7e69c90d6839f3b8580f0c7c374fd3c2b1261ea4de7bd9d3
5
5
  SHA512:
6
- metadata.gz: 1c26e2052e3f599c7f28c19892948c6b3f8cdeef005a4dc54762b4e74309ac32ef794115b0e46d2364624b26debb85a6aaebc938813d08e69e9c670c3bf79ae6
7
- data.tar.gz: 15bdc1f27280a327a1270ddf794b484d68f88af959c2d49361ef0e33c37e547b81166c8b92ef400f7e9a62c8192820bd58042733335ff4a9c3f1a77b660f775f
6
+ metadata.gz: daaae5e335bf7ba563dcf6db84459ee1cdf02053b2f6ae1a9b6422174d338947c47b33cb595db8e641534064d47f1129f07ee4eb72945b933851ed0562a5fc81
7
+ data.tar.gz: 67fccc917c86e90ad9201ed67d98e45709c7433a84a80adcf126ecd9b1044ccd72aafa23587e4bada689f9ff2e41f3a21bc51ea8b7e9a11f1af7c6b57a5fee16
data/CHANGELOG.md CHANGED
@@ -1,200 +1,27 @@
1
- ## Rails 7.2.3 (October 28, 2025) ##
1
+ ## Rails 8.0.0.beta1 (September 26, 2024) ##
2
2
 
3
- * Fix `javascript_include_tag` `type` option to accept either strings and symbols.
3
+ * Enable DependencyTracker to evaluate renders with trailing interpolation.
4
4
 
5
- ```ruby
6
- javascript_include_tag "application", type: :module
7
- javascript_include_tag "application", type: "module"
5
+ ```erb
6
+ <%= render "maintenance_tasks/runs/info/#{run.status}" %>
8
7
  ```
9
8
 
10
- Previously, only the string value was recoginized.
11
-
12
- *Jean Boussier*
13
-
14
- * Fix `excerpt` helper with non-whitespace separator.
15
-
16
- *Jonathan Hefner*
17
-
18
- * Respect `html_options[:form]` when `collection_checkboxes` generates the
19
- hidden `<input>`.
20
-
21
- *Riccardo Odone*
22
-
23
- * Layouts have access to local variables passed to `render`.
24
-
25
- This fixes #31680 which was a regression in Rails 5.1.
26
-
27
- *Mike Dalessio*
28
-
29
- * Argument errors related to strict locals in templates now raise an
30
- `ActionView::StrictLocalsError`, and all other argument errors are reraised as-is.
31
-
32
- Previously, any `ArgumentError` raised during template rendering was swallowed during strict
33
- local error handling, so that an `ArgumentError` unrelated to strict locals (e.g., a helper
34
- method invoked with incorrect arguments) would be replaced by a similar `ArgumentError` with an
35
- unrelated backtrace, making it difficult to debug templates.
36
-
37
- Now, any `ArgumentError` unrelated to strict locals is reraised, preserving the original
38
- backtrace for developers.
39
-
40
- Also note that `ActionView::StrictLocalsError` is a subclass of `ArgumentError`, so any existing
41
- code that rescues `ArgumentError` will continue to work.
42
-
43
- Fixes #52227.
44
-
45
- *Mike Dalessio*
46
-
47
- * Fix stack overflow error in dependency tracker when dealing with circular dependencies
48
-
49
- *Jean Boussier*
50
-
51
- * Fix a crash in ERB template error highlighting when the error occurs on a
52
- line in the compiled template that is past the end of the source template.
53
-
54
- *Martin Emde*
55
-
56
- * Improve reliability of ERB template error highlighting.
57
- Fix infinite loops and crashes in highlighting and
58
- improve tolerance for alternate ERB handlers.
59
-
60
- *Martin Emde*
61
-
62
-
63
- ## Rails 7.2.2.2 (August 13, 2025) ##
64
-
65
- * No changes.
66
-
67
-
68
- ## Rails 7.2.2.1 (December 10, 2024) ##
69
-
70
- * No changes.
71
-
72
-
73
- ## Rails 7.2.2 (October 30, 2024) ##
74
-
75
- * No changes.
76
-
77
-
78
- ## Rails 7.2.1.2 (October 23, 2024) ##
79
-
80
- * No changes.
81
-
82
-
83
- ## Rails 7.2.1.1 (October 15, 2024) ##
84
-
85
- * No changes.
86
-
87
-
88
- ## Rails 7.2.1 (August 22, 2024) ##
89
-
90
- * No changes.
91
-
92
-
93
- ## Rails 7.2.0 (August 09, 2024) ##
94
-
95
- * Fix templates with strict locals to also include `local_assigns`.
96
-
97
- Previously templates defining strict locals wouldn't receive the `local_assigns`
98
- hash.
99
-
100
- *Jean Boussier*
101
-
102
- * Add queries count to template rendering instrumentation.
103
-
104
- ```
105
- # Before
106
- Completed 200 OK in 3804ms (Views: 41.0ms | ActiveRecord: 33.5ms | Allocations: 112788)
107
-
108
- # After
109
- Completed 200 OK in 3804ms (Views: 41.0ms | ActiveRecord: 33.5ms (2 queries, 1 cached) | Allocations: 112788)
110
- ```
111
-
112
- *fatkodima*
113
-
114
- * Raise `ArgumentError` if `:renderable` object does not respond to `#render_in`.
115
-
116
- *Sean Doyle*
117
-
118
- * Add the `nonce: true` option for `stylesheet_link_tag` helper to support automatic nonce generation for Content Security Policy.
119
-
120
- Works the same way as `javascript_include_tag nonce: true` does.
121
-
122
- *Akhil G Krishnan*, *AJ Esler*
123
-
124
- * Parse `ActionView::TestCase#rendered` HTML content as `Nokogiri::XML::DocumentFragment` instead of `Nokogiri::XML::Document`.
125
-
126
- *Sean Doyle*
127
-
128
- * Rename `ActionView::TestCase::Behavior::Content` to `ActionView::TestCase::Behavior::RenderedViewContent`.
129
-
130
- Make `RenderedViewContent` inherit from `String`. Make private API with `:nodoc:`
131
-
132
- *Sean Doyle*
133
-
134
- * Deprecate passing `nil` as value for the `model:` argument to the `form_with` method.
135
-
136
- *Collin Jilbert*
137
-
138
- * Alias `field_set_tag` helper to `fieldset_tag` to match `<fieldset>` element.
139
-
140
- *Sean Doyle*
141
-
142
- * Deprecate passing content to void elements when using `tag.br` type tag builders.
9
+ Previously, the DependencyTracker would ignore this render, but now it will
10
+ mark all partials in the "maintenance_tasks/runs/info" folder as
11
+ dependencies.
143
12
 
144
13
  *Hartley McGuire*
145
14
 
146
- * Fix the `number_to_human_size` view helper to correctly work with negative numbers.
147
-
148
- *Earlopain*
149
-
150
- * Automatically discard the implicit locals injected by collection rendering for template that can't accept them.
15
+ * Rename `text_area` methods into `textarea`
151
16
 
152
- When rendering a collection, two implicit variables are injected, which breaks templates with strict locals.
17
+ Old names are still available as aliases.
153
18
 
154
- Now they are only passed if the template will actually accept them.
155
-
156
- *Yasha Krasnou*, *Jean Boussier*
157
-
158
- * Fix `@rails/ujs` calling `start()` an extra time when using bundlers.
159
-
160
- *Hartley McGuire*, *Ryunosuke Sato*
19
+ *Sean Doyle*
161
20
 
162
- * Fix the `capture` view helper compatibility with HAML and Slim.
21
+ * Rename `check_box*` methods into `checkbox*`.
163
22
 
164
- When a blank string was captured in HAML or Slim (and possibly other template engines)
165
- it would instead return the entire buffer.
23
+ Old names are still available as aliases.
166
24
 
167
25
  *Jean Boussier*
168
26
 
169
- * Updated `@rails/ujs` files to ignore certain data-* attributes when element is contenteditable.
170
-
171
- This fix was already landed in >= 7.0.4.3, < 7.1.0.
172
- [[CVE-2023-23913](https://github.com/advisories/GHSA-xp5h-f8jf-rc8q)]
173
-
174
- *Ryunosuke Sato*
175
-
176
- * Added validation for HTML tag names in the `tag` and `content_tag` helper method.
177
-
178
- The `tag` and `content_tag` method now checks that the provided tag name adheres to the HTML
179
- specification. If an invalid HTML tag name is provided, the method raises an `ArgumentError`
180
- with an appropriate error message.
181
-
182
- Examples:
183
-
184
- ```ruby
185
- # Raises ArgumentError: Invalid HTML5 tag name: 12p
186
- content_tag("12p") # Starting with a number
187
-
188
- # Raises ArgumentError: Invalid HTML5 tag name: ""
189
- content_tag("") # Empty tag name
190
-
191
- # Raises ArgumentError: Invalid HTML5 tag name: div/
192
- tag("div/") # Contains a solidus
193
-
194
- # Raises ArgumentError: Invalid HTML5 tag name: "image file"
195
- tag("image file") # Contains a space
196
- ```
197
-
198
- *Akhil G Krishnan*
199
-
200
- Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/actionview/CHANGELOG.md) for previous changes.
27
+ Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/actionview/CHANGELOG.md) for previous changes.
data/README.rdoc CHANGED
@@ -35,6 +35,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
35
35
 
36
36
  * https://github.com/rails/rails/issues
37
37
 
38
- Feature requests should be discussed on the rubyonrails-core forum here:
38
+ Feature requests should be discussed on the rails-core mailing list here:
39
39
 
40
40
  * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -267,12 +267,15 @@ module ActionView # :nodoc:
267
267
  begin
268
268
  public_send(method, locals, buffer, **locals, &block)
269
269
  rescue ArgumentError => argument_error
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
270
+ raise(
271
+ ArgumentError,
272
+ argument_error.
273
+ message.
274
+ gsub("unknown keyword:", "unknown local:").
275
+ gsub("missing keyword:", "missing local:").
276
+ gsub("no keywords accepted", "no locals accepted").
277
+ concat(" for #{@current_template.short_identifier}")
278
+ )
276
279
  end
277
280
  else
278
281
  public_send(method, locals, buffer, &block)
@@ -74,7 +74,7 @@ module ActionView
74
74
  end
75
75
 
76
76
  def dependencies
77
- render_dependencies + explicit_dependencies
77
+ WildcardResolver.new(@view_paths, render_dependencies + explicit_dependencies).resolve
78
78
  end
79
79
 
80
80
  attr_reader :name, :template
@@ -90,15 +90,15 @@ module ActionView
90
90
  end
91
91
 
92
92
  def render_dependencies
93
- render_dependencies = []
93
+ dependencies = []
94
94
  render_calls = source.split(/\brender\b/).drop(1)
95
95
 
96
96
  render_calls.each do |arguments|
97
- add_dependencies(render_dependencies, arguments, LAYOUT_DEPENDENCY)
98
- add_dependencies(render_dependencies, arguments, RENDER_ARGUMENTS)
97
+ add_dependencies(dependencies, arguments, LAYOUT_DEPENDENCY)
98
+ add_dependencies(dependencies, arguments, RENDER_ARGUMENTS)
99
99
  end
100
100
 
101
- render_dependencies.uniq
101
+ dependencies
102
102
  end
103
103
 
104
104
  def add_dependencies(render_dependencies, arguments, pattern)
@@ -116,12 +116,36 @@ module ActionView
116
116
  end
117
117
 
118
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
119
+ if quote_type == '"' && dependency.include?('#{')
120
+ scanner = StringScanner.new(dependency)
121
+
122
+ wildcard_dependency = +""
123
+
124
+ while !scanner.eos?
125
+ if scanner.scan_until(/\#{/)
126
+ unmatched_brackets = 1
127
+ wildcard_dependency << scanner.pre_match
128
+
129
+ while unmatched_brackets > 0 && !scanner.eos?
130
+ scanner.scan_until(/[{}]/)
131
+
132
+ case scanner.matched
133
+ when "{"
134
+ unmatched_brackets += 1
135
+ when "}"
136
+ unmatched_brackets -= 1
137
+ end
138
+ end
139
+
140
+ wildcard_dependency << "*"
141
+ else
142
+ wildcard_dependency << scanner.rest
143
+ scanner.terminate
144
+ end
145
+ end
123
146
 
124
- if dependency
147
+ dependencies << wildcard_dependency
148
+ elsif dependency
125
149
  if dependency.include?("/")
126
150
  dependencies << dependency
127
151
  else
@@ -130,24 +154,8 @@ module ActionView
130
154
  end
131
155
  end
132
156
 
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
157
  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
158
+ source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
151
159
  end
152
160
  end
153
161
  end
@@ -10,7 +10,7 @@ module ActionView
10
10
  end
11
11
 
12
12
  def dependencies
13
- render_dependencies + explicit_dependencies
13
+ WildcardResolver.new(view_paths, render_dependencies + explicit_dependencies).resolve
14
14
  end
15
15
 
16
16
  def self.supports_view_paths? # :nodoc:
@@ -31,29 +31,12 @@ module ActionView
31
31
  compiled_source = template.handler.call(template, template.source)
32
32
 
33
33
  @parser_class.new(@name, compiled_source).render_calls.filter_map do |render_call|
34
- next if render_call.end_with?("/_")
35
34
  render_call.gsub(%r|/_|, "/")
36
35
  end
37
36
  end
38
37
 
39
38
  def explicit_dependencies
40
- dependencies = template.source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
41
-
42
- wildcards, explicits = dependencies.partition { |dependency| dependency.end_with?("/*") }
43
-
44
- (explicits + resolve_directories(wildcards)).uniq
45
- end
46
-
47
- def resolve_directories(wildcard_dependencies)
48
- return [] unless view_paths
49
- return [] if wildcard_dependencies.empty?
50
-
51
- # Remove trailing "/*"
52
- prefixes = wildcard_dependencies.map { |query| query[0..-3] }
53
-
54
- view_paths.flat_map(&:all_template_paths).uniq.filter_map { |path|
55
- path.to_s if prefixes.include?(path.prefix)
56
- }.sort
39
+ template.source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
57
40
  end
58
41
  end
59
42
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class DependencyTracker # :nodoc:
5
+ class WildcardResolver # :nodoc:
6
+ def initialize(view_paths, dependencies)
7
+ @view_paths = view_paths
8
+
9
+ @wildcard_dependencies, @explicit_dependencies =
10
+ dependencies.partition { |dependency| dependency.end_with?("/*") }
11
+ end
12
+
13
+ def resolve
14
+ return explicit_dependencies.uniq if !view_paths || wildcard_dependencies.empty?
15
+
16
+ (explicit_dependencies + resolved_wildcard_dependencies).uniq
17
+ end
18
+
19
+ private
20
+ attr_reader :explicit_dependencies, :wildcard_dependencies, :view_paths
21
+
22
+ def resolved_wildcard_dependencies
23
+ # Remove trailing "/*"
24
+ prefixes = wildcard_dependencies.map { |query| query[0..-3] }
25
+
26
+ view_paths.flat_map(&:all_template_paths).uniq.filter_map { |path|
27
+ path.to_s if prefixes.include?(path.prefix)
28
+ }.sort
29
+ end
30
+ end
31
+ end
32
+ end
@@ -10,6 +10,7 @@ module ActionView
10
10
 
11
11
  autoload :ERBTracker
12
12
  autoload :RubyTracker
13
+ autoload :WildcardResolver
13
14
 
14
15
  @trackers = Concurrent::Map.new
15
16
 
@@ -107,12 +107,8 @@ module ActionView
107
107
  end.join("-")
108
108
  end
109
109
 
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
110
+ def to_dep_map
111
+ children.any? ? { name => children.map(&:to_dep_map) } : name
116
112
  end
117
113
  end
118
114
 
@@ -7,10 +7,10 @@ module ActionView
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 7
11
- MINOR = 2
12
- TINY = 3
13
- PRE = nil
10
+ MAJOR = 8
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = "beta1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -115,11 +115,11 @@ module ActionView
115
115
  path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
116
116
  preload_links = []
117
117
  use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
118
- nopush = options["nopush"].nil? || options.delete("nopush")
118
+ nopush = options["nopush"].nil? ? true : options.delete("nopush")
119
119
  crossorigin = options.delete("crossorigin")
120
120
  crossorigin = "anonymous" if crossorigin == true
121
121
  integrity = options["integrity"]
122
- rel = options["type"] == "module" || options["type"] == :module ? "modulepreload" : "preload"
122
+ rel = options["type"] == "module" ? "modulepreload" : "preload"
123
123
 
124
124
  sources_tags = sources.uniq.map { |source|
125
125
  href = path_to_javascript(source, path_options)
@@ -206,7 +206,7 @@ module ActionView
206
206
  preload_links = []
207
207
  crossorigin = options.delete("crossorigin")
208
208
  crossorigin = "anonymous" if crossorigin == true
209
- nopush = options["nopush"].nil? || options.delete("nopush")
209
+ nopush = options["nopush"].nil? ? true : options.delete("nopush")
210
210
  integrity = options["integrity"]
211
211
 
212
212
  sources_tags = sources.uniq.map { |source|
@@ -361,7 +361,7 @@ module ActionView
361
361
  crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
362
362
  integrity = options[:integrity]
363
363
  nopush = options.delete(:nopush) || false
364
- rel = mime_type == "module" || mime_type == :module ? "modulepreload" : "preload"
364
+ rel = mime_type == "module" ? "modulepreload" : "preload"
365
365
 
366
366
  link_tag = tag.link(
367
367
  rel: rel,
@@ -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.
@@ -93,6 +93,14 @@ module ActionView
93
93
  # render partial: 'attachments/attachment', collection: group_of_attachments
94
94
  # render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
95
95
  #
96
+ # One last type of dependency can be determined implicitly:
97
+ #
98
+ # render "maintenance_tasks/runs/info/#{run.status}"
99
+ #
100
+ # Because the value passed to render ends in interpolation, Action View
101
+ # will mark all partials within the "maintenace_tasks/runs/info" folder as
102
+ # dependencies.
103
+ #
96
104
  # === Explicit dependencies
97
105
  #
98
106
  # Sometimes you'll have template dependencies that can't be derived at all. This is typically
@@ -189,7 +197,7 @@ module ActionView
189
197
  CachingRegistry.caching?
190
198
  end
191
199
 
192
- # Raises UncacheableFragmentError when called from within a +cache+ block.
200
+ # Raises +UncacheableFragmentError+ when called from within a +cache+ block.
193
201
  #
194
202
  # Useful to denote helper methods that can't participate in fragment caching:
195
203
  #
@@ -198,7 +206,7 @@ module ActionView
198
206
  # "#{project.name} - #{Time.now}"
199
207
  # end
200
208
  #
201
- # # Which will then raise if used within a `cache` block:
209
+ # # Which will then raise if used within a +cache+ block:
202
210
  # <% cache project do %>
203
211
  # <%= project_name_with_time(project) %>
204
212
  # <% end %>
@@ -136,15 +136,8 @@ 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
-
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
139
+ leap_years = (from_year > to_year) ? 0 : (from_year..to_year).count { |x| Date.leap?(x) }
146
140
  minute_offset_for_leap_year = leap_years * 1440
147
-
148
141
  # Discount the leap year days when calculating year distance.
149
142
  # e.g. if there are 20 leap year days between 2 dates having the same day
150
143
  # and month then based on 365 days calculation