actionview 4.2.11.3 → 5.2.7.1

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +115 -245
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -6
  5. data/lib/action_view/base.rb +38 -28
  6. data/lib/action_view/buffers.rb +3 -1
  7. data/lib/action_view/context.rb +3 -3
  8. data/lib/action_view/dependency_tracker.rb +54 -20
  9. data/lib/action_view/digestor.rb +94 -83
  10. data/lib/action_view/flows.rb +11 -11
  11. data/lib/action_view/gem_version.rb +5 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +17 -11
  13. data/lib/action_view/helpers/asset_tag_helper.rb +244 -62
  14. data/lib/action_view/helpers/asset_url_helper.rb +170 -67
  15. data/lib/action_view/helpers/atom_feed_helper.rb +19 -17
  16. data/lib/action_view/helpers/cache_helper.rb +105 -42
  17. data/lib/action_view/helpers/capture_helper.rb +16 -13
  18. data/lib/action_view/helpers/controller_helper.rb +15 -4
  19. data/lib/action_view/helpers/csp_helper.rb +24 -0
  20. data/lib/action_view/helpers/csrf_helper.rb +7 -5
  21. data/lib/action_view/helpers/date_helper.rb +170 -112
  22. data/lib/action_view/helpers/debug_helper.rb +7 -6
  23. data/lib/action_view/helpers/form_helper.rb +521 -127
  24. data/lib/action_view/helpers/form_options_helper.rb +109 -63
  25. data/lib/action_view/helpers/form_tag_helper.rb +110 -67
  26. data/lib/action_view/helpers/javascript_helper.rb +27 -12
  27. data/lib/action_view/helpers/number_helper.rb +77 -58
  28. data/lib/action_view/helpers/output_safety_helper.rb +36 -4
  29. data/lib/action_view/helpers/record_tag_helper.rb +14 -99
  30. data/lib/action_view/helpers/rendering_helper.rb +6 -5
  31. data/lib/action_view/helpers/sanitize_helper.rb +20 -15
  32. data/lib/action_view/helpers/tag_helper.rb +229 -73
  33. data/lib/action_view/helpers/tags/base.rb +134 -97
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -18
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -33
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +70 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -11
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +3 -1
  41. data/lib/action_view/helpers/tags/date_field.rb +2 -0
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -36
  43. data/lib/action_view/helpers/tags/datetime_field.rb +4 -2
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +2 -0
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
  50. data/lib/action_view/helpers/tags/label.rb +3 -1
  51. data/lib/action_view/helpers/tags/month_field.rb +2 -0
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -5
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  58. data/lib/action_view/helpers/tags/select.rb +11 -9
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  61. data/lib/action_view/helpers/tags/text_field.rb +8 -7
  62. data/lib/action_view/helpers/tags/time_field.rb +2 -0
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +17 -13
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +2 -0
  68. data/lib/action_view/helpers/tags.rb +3 -1
  69. data/lib/action_view/helpers/text_helper.rb +55 -36
  70. data/lib/action_view/helpers/translation_helper.rb +74 -32
  71. data/lib/action_view/helpers/url_helper.rb +159 -104
  72. data/lib/action_view/helpers.rb +5 -1
  73. data/lib/action_view/layouts.rb +65 -58
  74. data/lib/action_view/log_subscriber.rb +60 -8
  75. data/lib/action_view/lookup_context.rb +80 -65
  76. data/lib/action_view/model_naming.rb +3 -1
  77. data/lib/action_view/path_set.rb +30 -19
  78. data/lib/action_view/railtie.rb +39 -6
  79. data/lib/action_view/record_identifier.rb +53 -25
  80. data/lib/action_view/renderer/abstract_renderer.rb +21 -15
  81. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +57 -0
  82. data/lib/action_view/renderer/partial_renderer.rb +218 -214
  83. data/lib/action_view/renderer/renderer.rb +8 -6
  84. data/lib/action_view/renderer/streaming_template_renderer.rb +50 -48
  85. data/lib/action_view/renderer/template_renderer.rb +67 -66
  86. data/lib/action_view/rendering.rb +19 -14
  87. data/lib/action_view/routing_url_for.rb +27 -17
  88. data/lib/action_view/tasks/cache_digests.rake +25 -0
  89. data/lib/action_view/template/error.rb +16 -16
  90. data/lib/action_view/template/handlers/builder.rb +10 -11
  91. data/lib/action_view/template/handlers/erb/erubi.rb +83 -0
  92. data/lib/action_view/template/handlers/erb.rb +9 -80
  93. data/lib/action_view/template/handlers/html.rb +11 -0
  94. data/lib/action_view/template/handlers/raw.rb +3 -3
  95. data/lib/action_view/template/handlers.rb +11 -7
  96. data/lib/action_view/template/html.rb +5 -5
  97. data/lib/action_view/template/resolver.rb +140 -115
  98. data/lib/action_view/template/text.rb +8 -9
  99. data/lib/action_view/template/types.rb +18 -18
  100. data/lib/action_view/template.rb +54 -33
  101. data/lib/action_view/test_case.rb +50 -29
  102. data/lib/action_view/testing/resolvers.rb +31 -31
  103. data/lib/action_view/version.rb +3 -1
  104. data/lib/action_view/view_paths.rb +28 -34
  105. data/lib/action_view.rb +8 -7
  106. data/lib/assets/compiled/rails-ujs.js +720 -0
  107. metadata +28 -27
  108. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -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'
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"
9
11
 
10
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 setup 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| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
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
+ cattr_accessor :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
159
  # Specify whether an error should be raised for missing translations
153
- cattr_accessor :raise_on_missing_translations
154
- @@raise_on_missing_translations = false
160
+ cattr_accessor :raise_on_missing_translations, default: false
161
+
162
+ # Specify whether submit_tag should automatically disable on click
163
+ cattr_accessor :automatically_disable_submit_tag, default: true
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?
@@ -175,8 +184,8 @@ module ActionView #:nodoc:
175
184
  attr_accessor :view_renderer
176
185
  attr_internal :config, :assigns
177
186
 
178
- delegate :lookup_context, :to => :view_renderer
179
- delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
187
+ delegate :lookup_context, to: :view_renderer
188
+ delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, to: :lookup_context
180
189
 
181
190
  def assign(new_assigns) # :nodoc:
182
191
  @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
@@ -195,6 +204,7 @@ module ActionView #:nodoc:
195
204
  @view_renderer = ActionView::Renderer.new(lookup_context)
196
205
  end
197
206
 
207
+ @cache_hit = {}
198
208
  assign(assigns)
199
209
  assign_controller(controller)
200
210
  _prepare_context
@@ -1,4 +1,6 @@
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
6
  class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  module CompiledTemplates #:nodoc:
3
5
  # holds compiled template code
@@ -17,7 +19,6 @@ module ActionView
17
19
  attr_accessor :output_buffer, :view_flow
18
20
 
19
21
  # Prepares the context by setting the appropriate instance variables.
20
- # :api: plugin
21
22
  def _prepare_context
22
23
  @view_flow = OutputFlow.new
23
24
  @output_buffer = nil
@@ -27,8 +28,7 @@ module ActionView
27
28
  # Encapsulates the interaction with the view flow so it
28
29
  # returns the correct buffer on +yield+. This is usually
29
30
  # overwritten by helpers to add more behavior.
30
- # :api: plugin
31
- def _layout_for(name=nil)
31
+ def _layout_for(name = nil)
32
32
  name ||= :layout
33
33
  view_flow.get(name).html_safe
34
34
  end
@@ -1,22 +1,28 @@
1
- require 'thread_safe'
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+ require "action_view/path_set"
2
5
 
3
6
  module ActionView
4
7
  class DependencyTracker # :nodoc:
5
- @trackers = ThreadSafe::Cache.new
8
+ @trackers = Concurrent::Map.new
6
9
 
7
- def self.find_dependencies(name, template)
10
+ def self.find_dependencies(name, template, view_paths = nil)
8
11
  tracker = @trackers[template.handler]
12
+ return [] unless tracker
9
13
 
10
- if tracker.present?
11
- tracker.call(name, template)
12
- else
13
- []
14
- end
14
+ tracker.call(name, template, view_paths)
15
15
  end
16
16
 
17
17
  def self.register_tracker(extension, tracker)
18
18
  handler = Template.handler_for_extension(extension)
19
- @trackers[handler] = tracker
19
+ if tracker.respond_to?(:supports_view_paths?)
20
+ @trackers[handler] = tracker
21
+ else
22
+ @trackers[handler] = lambda { |name, template, _|
23
+ tracker.call(name, template)
24
+ }
25
+ end
20
26
  end
21
27
 
22
28
  def self.remove_tracker(handler)
@@ -76,12 +82,22 @@ module ActionView
76
82
  (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
77
83
  /xm
78
84
 
79
- def self.call(name, template)
80
- new(name, template).dependencies
85
+ LAYOUT_DEPENDENCY = /\A
86
+ (?:\s*\(?\s*) # optional opening paren surrounded by spaces
87
+ (?:.*?#{LAYOUT_HASH_KEY}) # check if the line has layout key declaration
88
+ (?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
89
+ /xm
90
+
91
+ def self.supports_view_paths? # :nodoc:
92
+ true
93
+ end
94
+
95
+ def self.call(name, template, view_paths = nil)
96
+ new(name, template, view_paths).dependencies
81
97
  end
82
98
 
83
- def initialize(name, template)
84
- @name, @template = name, template
99
+ def initialize(name, template, view_paths = nil)
100
+ @name, @template, @view_paths = name, template, view_paths
85
101
  end
86
102
 
87
103
  def dependencies
@@ -91,7 +107,6 @@ module ActionView
91
107
  attr_reader :name, :template
92
108
  private :name, :template
93
109
 
94
-
95
110
  private
96
111
  def source
97
112
  template.source
@@ -106,15 +121,20 @@ module ActionView
106
121
  render_calls = source.split(/\brender\b/).drop(1)
107
122
 
108
123
  render_calls.each do |arguments|
109
- arguments.scan(RENDER_ARGUMENTS) do
110
- add_dynamic_dependency(render_dependencies, Regexp.last_match[:dynamic])
111
- add_static_dependency(render_dependencies, Regexp.last_match[:static])
112
- end
124
+ add_dependencies(render_dependencies, arguments, LAYOUT_DEPENDENCY)
125
+ add_dependencies(render_dependencies, arguments, RENDER_ARGUMENTS)
113
126
  end
114
127
 
115
128
  render_dependencies.uniq
116
129
  end
117
130
 
131
+ def add_dependencies(render_dependencies, arguments, pattern)
132
+ arguments.scan(pattern) do
133
+ add_dynamic_dependency(render_dependencies, Regexp.last_match[:dynamic])
134
+ add_static_dependency(render_dependencies, Regexp.last_match[:static])
135
+ end
136
+ end
137
+
118
138
  def add_dynamic_dependency(dependencies, dependency)
119
139
  if dependency
120
140
  dependencies << "#{dependency.pluralize}/#{dependency.singularize}"
@@ -123,7 +143,7 @@ module ActionView
123
143
 
124
144
  def add_static_dependency(dependencies, dependency)
125
145
  if dependency
126
- if dependency.include?('/')
146
+ if dependency.include?("/")
127
147
  dependencies << dependency
128
148
  else
129
149
  dependencies << "#{directory}/#{dependency}"
@@ -131,8 +151,22 @@ module ActionView
131
151
  end
132
152
  end
133
153
 
154
+ def resolve_directories(wildcard_dependencies)
155
+ return [] unless @view_paths
156
+
157
+ wildcard_dependencies.flat_map { |query, templates|
158
+ @view_paths.find_all_with_query(query).map do |template|
159
+ "#{File.dirname(query)}/#{File.basename(template).split('.').first}"
160
+ end
161
+ }.sort
162
+ end
163
+
134
164
  def explicit_dependencies
135
- source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
165
+ dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
166
+
167
+ wildcards, explicits = dependencies.partition { |dependency| dependency[-1] == "*" }
168
+
169
+ (explicits + resolve_directories(wildcards)).uniq
136
170
  end
137
171
  end
138
172
 
@@ -1,123 +1,134 @@
1
- require 'thread_safe'
2
- require 'action_view/dependency_tracker'
3
- require 'monitor'
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent/map"
4
+ require "action_view/dependency_tracker"
5
+ require "monitor"
4
6
 
5
7
  module ActionView
6
8
  class Digestor
7
- cattr_reader(:cache)
8
- @@cache = ThreadSafe::Cache.new
9
- @@digest_monitor = Monitor.new
9
+ @@digest_mutex = Mutex.new
10
+
11
+ module PerExecutionDigestCacheExpiry
12
+ def self.before(target)
13
+ ActionView::LookupContext::DetailsKey.clear
14
+ end
15
+ end
10
16
 
11
17
  class << self
12
18
  # Supported options:
13
19
  #
14
20
  # * <tt>name</tt> - Template name
15
- # * <tt>finder</tt> - An instance of ActionView::LookupContext
21
+ # * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
16
22
  # * <tt>dependencies</tt> - An array of dependent views
17
- # * <tt>partial</tt> - Specifies whether the template is a partial
18
- def digest(options)
19
- options.assert_valid_keys(:name, :finder, :dependencies, :partial)
20
-
21
- cache_key = ([ options[:name], options[:finder].details_key.hash ].compact + Array.wrap(options[:dependencies])).join('.')
23
+ def digest(name:, finder:, dependencies: [])
24
+ dependencies ||= []
25
+ cache_key = [ name, finder.rendered_format, dependencies ].flatten.compact.join(".")
22
26
 
23
27
  # this is a correctly done double-checked locking idiom
24
- # (ThreadSafe::Cache's lookups have volatile semantics)
25
- @@cache[cache_key] || @@digest_monitor.synchronize do
26
- @@cache.fetch(cache_key) do # re-check under lock
27
- compute_and_store_digest(cache_key, options)
28
+ # (Concurrent::Map's lookups have volatile semantics)
29
+ finder.digest_cache[cache_key] || @@digest_mutex.synchronize do
30
+ finder.digest_cache.fetch(cache_key) do # re-check under lock
31
+ partial = name.include?("/_")
32
+ root = tree(name, finder, partial)
33
+ dependencies.each do |injected_dep|
34
+ root.children << Injected.new(injected_dep, nil, nil)
35
+ end
36
+ finder.digest_cache[cache_key] = root.digest(finder)
28
37
  end
29
38
  end
30
39
  end
31
40
 
32
- private
33
- def compute_and_store_digest(cache_key, options) # called under @@digest_monitor lock
34
- klass = if options[:partial] || options[:name].include?("/_")
35
- # Prevent re-entry or else recursive templates will blow the stack.
36
- # There is no need to worry about other threads seeing the +false+ value,
37
- # as they will then have to wait for this thread to let go of the @@digest_monitor lock.
38
- pre_stored = @@cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
39
- PartialDigestor
40
- else
41
- Digestor
42
- end
41
+ def logger
42
+ ActionView::Base.logger || NullLogger
43
+ end
43
44
 
44
- digest = klass.new(options).digest
45
- # Store the actual digest if config.cache_template_loading is true
46
- @@cache[cache_key] = stored_digest = digest if ActionView::Resolver.caching?
47
- digest
48
- ensure
49
- # something went wrong or ActionView::Resolver.caching? is false, make sure not to corrupt the @@cache
50
- @@cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
51
- end
52
- end
45
+ # Create a dependency tree for template named +name+.
46
+ def tree(name, finder, partial = false, seen = {})
47
+ logical_name = name.gsub(%r|/_|, "/")
53
48
 
54
- attr_reader :name, :finder, :options
49
+ if template = find_template(finder, logical_name, [], partial, [])
50
+ finder.rendered_format ||= template.formats.first
55
51
 
56
- def initialize(options)
57
- @name, @finder = options.values_at(:name, :finder)
58
- @options = options.except(:name, :finder)
59
- end
52
+ if node = seen[template.identifier] # handle cycles in the tree
53
+ node
54
+ else
55
+ node = seen[template.identifier] = Node.create(name, logical_name, template, partial)
56
+
57
+ deps = DependencyTracker.find_dependencies(name, template, finder.view_paths)
58
+ deps.uniq { |n| n.gsub(%r|/_|, "/") }.each do |dep_file|
59
+ node.children << tree(dep_file, finder, true, seen)
60
+ end
61
+ node
62
+ end
63
+ else
64
+ unless name.include?("#") # Dynamic template partial names can never be tracked
65
+ logger.error " Couldn't find template for digesting: #{name}"
66
+ end
60
67
 
61
- def digest
62
- Digest::MD5.hexdigest("#{source}-#{dependency_digest}").tap do |digest|
63
- logger.try :debug, " Cache digest for #{template.inspect}: #{digest}"
68
+ seen[name] ||= Missing.new(name, logical_name, nil)
69
+ end
64
70
  end
65
- rescue ActionView::MissingTemplate
66
- logger.try :error, " Couldn't find template for digesting: #{name}"
67
- ''
68
- end
69
71
 
70
- def dependencies
71
- DependencyTracker.find_dependencies(name, template)
72
- rescue ActionView::MissingTemplate
73
- logger.try :error, " '#{name}' file doesn't exist, so no dependencies"
74
- []
72
+ private
73
+ def find_template(finder, name, prefixes, partial, keys)
74
+ finder.disable_cache do
75
+ format = finder.rendered_format
76
+ result = finder.find_all(name, prefixes, partial, keys, formats: [format]).first if format
77
+ result || finder.find_all(name, prefixes, partial, keys).first
78
+ end
79
+ end
75
80
  end
76
81
 
77
- def nested_dependencies
78
- dependencies.collect do |dependency|
79
- dependencies = PartialDigestor.new(name: dependency, finder: finder).nested_dependencies
80
- dependencies.any? ? { dependency => dependencies } : dependency
81
- end
82
- end
82
+ class Node
83
+ attr_reader :name, :logical_name, :template, :children
83
84
 
84
- private
85
- def logger
86
- ActionView::Base.logger
85
+ def self.create(name, logical_name, template, partial)
86
+ klass = partial ? Partial : Node
87
+ klass.new(name, logical_name, template, [])
87
88
  end
88
89
 
89
- def logical_name
90
- name.gsub(%r|/_|, "/")
90
+ def initialize(name, logical_name, template, children = [])
91
+ @name = name
92
+ @logical_name = logical_name
93
+ @template = template
94
+ @children = children
91
95
  end
92
96
 
93
- def partial?
94
- false
97
+ def digest(finder, stack = [])
98
+ ActiveSupport::Digest.hexdigest("#{template.source}-#{dependency_digest(finder, stack)}")
95
99
  end
96
100
 
97
- def template
98
- @template ||= finder.disable_cache { finder.find(logical_name, [], partial?) }
101
+ def dependency_digest(finder, stack)
102
+ children.map do |node|
103
+ if stack.include?(node)
104
+ false
105
+ else
106
+ finder.digest_cache[node.name] ||= begin
107
+ stack.push node
108
+ node.digest(finder, stack).tap { stack.pop }
109
+ end
110
+ end
111
+ end.join("-")
99
112
  end
100
113
 
101
- def source
102
- template.source
114
+ def to_dep_map
115
+ children.any? ? { name => children.map(&:to_dep_map) } : name
103
116
  end
117
+ end
104
118
 
105
- def dependency_digest
106
- template_digests = dependencies.collect do |template_name|
107
- Digestor.digest(name: template_name, finder: finder, partial: true)
108
- end
119
+ class Partial < Node; end
109
120
 
110
- (template_digests + injected_dependencies).join("-")
111
- end
121
+ class Missing < Node
122
+ def digest(finder, _ = []) "" end
123
+ end
112
124
 
113
- def injected_dependencies
114
- Array.wrap(options[:dependencies])
115
- end
116
- end
125
+ class Injected < Node
126
+ def digest(finder, _ = []) name end
127
+ end
117
128
 
118
- class PartialDigestor < Digestor # :nodoc:
119
- def partial?
120
- true
129
+ class NullLogger
130
+ def self.debug(_); end
131
+ def self.error(_); end
121
132
  end
122
133
  end
123
134
  end
@@ -1,11 +1,13 @@
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
6
  class OutputFlow #:nodoc:
5
7
  attr_reader :content
6
8
 
7
9
  def initialize
8
- @content = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
10
+ @content = Hash.new { |h, k| h[k] = ActiveSupport::SafeBuffer.new }
9
11
  end
10
12
 
11
13
  # Called by _layout_for to read stored values.
@@ -23,7 +25,6 @@ module ActionView
23
25
  @content[key] << value
24
26
  end
25
27
  alias_method :append!, :append
26
-
27
28
  end
28
29
 
29
30
  class StreamingFlow < OutputFlow #:nodoc:
@@ -37,9 +38,8 @@ module ActionView
37
38
  end
38
39
 
39
40
  # Try to get stored content. If the content
40
- # is not available and we are inside the layout
41
- # fiber, we set that we are waiting for the given
42
- # key and yield.
41
+ # is not available and we're inside the layout fiber,
42
+ # then it will begin waiting for the given key and yield.
43
43
  def get(key)
44
44
  return super if @content.key?(key)
45
45
 
@@ -60,8 +60,8 @@ module ActionView
60
60
  end
61
61
 
62
62
  # Appends the contents for the given key. This is called
63
- # by provides and resumes back to the fiber if it is
64
- # the key it is waiting for.
63
+ # by providing and resuming back to the fiber,
64
+ # if that's the key it's waiting for.
65
65
  def append!(key, value)
66
66
  super
67
67
  @fiber.resume if @waiting_for == key
@@ -69,8 +69,8 @@ module ActionView
69
69
 
70
70
  private
71
71
 
72
- def inside_fiber?
73
- Fiber.current.object_id != @root
74
- end
72
+ def inside_fiber?
73
+ Fiber.current.object_id != @root
74
+ end
75
75
  end
76
76
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  # Returns the version of the currently loaded Action View as a <tt>Gem::Version</tt>
3
5
  def self.gem_version
@@ -5,10 +7,10 @@ module ActionView
5
7
  end
6
8
 
7
9
  module VERSION
8
- MAJOR = 4
10
+ MAJOR = 5
9
11
  MINOR = 2
10
- TINY = 11
11
- PRE = "3"
12
+ TINY = 7
13
+ PRE = "1"
12
14
 
13
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
16
  end
@@ -1,9 +1,11 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
- require 'active_support/core_ext/enumerable'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors"
4
+ require "active_support/core_ext/enumerable"
3
5
 
4
6
  module ActionView
5
7
  # = Active Model Helpers
6
- module Helpers
8
+ module Helpers #:nodoc:
7
9
  module ActiveModelHelper
8
10
  end
9
11
 
@@ -15,8 +17,8 @@ module ActionView
15
17
  end
16
18
  end
17
19
 
18
- def content_tag(*)
19
- error_wrapping(super)
20
+ def content_tag(type, options, *)
21
+ select_markup_helper?(type) ? super : error_wrapping(super)
20
22
  end
21
23
 
22
24
  def tag(type, options, *)
@@ -37,13 +39,17 @@ module ActionView
37
39
 
38
40
  private
39
41
 
40
- def object_has_errors?
41
- object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
42
- end
42
+ def object_has_errors?
43
+ object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
44
+ end
43
45
 
44
- def tag_generate_errors?(options)
45
- options['type'] != 'hidden'
46
- end
46
+ def select_markup_helper?(type)
47
+ ["optgroup", "option"].include?(type)
48
+ end
49
+
50
+ def tag_generate_errors?(options)
51
+ options["type"] != "hidden"
52
+ end
47
53
  end
48
54
  end
49
55
  end