actionview 6.1.7.2 → 7.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +265 -261
  3. data/MIT-LICENSE +1 -0
  4. data/lib/action_view/base.rb +4 -7
  5. data/lib/action_view/buffers.rb +2 -2
  6. data/lib/action_view/cache_expiry.rb +46 -32
  7. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  8. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  9. data/lib/action_view/dependency_tracker.rb +6 -147
  10. data/lib/action_view/digestor.rb +7 -4
  11. data/lib/action_view/flows.rb +4 -4
  12. data/lib/action_view/gem_version.rb +5 -5
  13. data/lib/action_view/helpers/active_model_helper.rb +2 -2
  14. data/lib/action_view/helpers/asset_tag_helper.rb +95 -39
  15. data/lib/action_view/helpers/asset_url_helper.rb +16 -16
  16. data/lib/action_view/helpers/atom_feed_helper.rb +3 -4
  17. data/lib/action_view/helpers/cache_helper.rb +52 -3
  18. data/lib/action_view/helpers/capture_helper.rb +4 -4
  19. data/lib/action_view/helpers/controller_helper.rb +2 -2
  20. data/lib/action_view/helpers/csp_helper.rb +1 -1
  21. data/lib/action_view/helpers/csrf_helper.rb +2 -2
  22. data/lib/action_view/helpers/date_helper.rb +111 -43
  23. data/lib/action_view/helpers/debug_helper.rb +3 -1
  24. data/lib/action_view/helpers/form_helper.rb +211 -85
  25. data/lib/action_view/helpers/form_options_helper.rb +70 -33
  26. data/lib/action_view/helpers/form_tag_helper.rb +150 -53
  27. data/lib/action_view/helpers/javascript_helper.rb +3 -5
  28. data/lib/action_view/helpers/number_helper.rb +17 -16
  29. data/lib/action_view/helpers/output_safety_helper.rb +4 -4
  30. data/lib/action_view/helpers/rendering_helper.rb +5 -6
  31. data/lib/action_view/helpers/sanitize_helper.rb +3 -3
  32. data/lib/action_view/helpers/tag_helper.rb +37 -8
  33. data/lib/action_view/helpers/tags/base.rb +5 -25
  34. data/lib/action_view/helpers/tags/check_box.rb +1 -1
  35. data/lib/action_view/helpers/tags/collection_select.rb +1 -1
  36. data/lib/action_view/helpers/tags/file_field.rb +16 -0
  37. data/lib/action_view/helpers/tags/select.rb +1 -1
  38. data/lib/action_view/helpers/tags/time_field.rb +10 -1
  39. data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
  40. data/lib/action_view/helpers/tags.rb +3 -2
  41. data/lib/action_view/helpers/text_helper.rb +25 -14
  42. data/lib/action_view/helpers/translation_helper.rb +12 -43
  43. data/lib/action_view/helpers/url_helper.rb +194 -123
  44. data/lib/action_view/helpers.rb +25 -25
  45. data/lib/action_view/layouts.rb +7 -4
  46. data/lib/action_view/lookup_context.rb +33 -52
  47. data/lib/action_view/model_naming.rb +2 -2
  48. data/lib/action_view/path_set.rb +16 -22
  49. data/lib/action_view/railtie.rb +19 -7
  50. data/lib/action_view/record_identifier.rb +1 -1
  51. data/lib/action_view/render_parser.rb +188 -0
  52. data/lib/action_view/renderer/abstract_renderer.rb +2 -2
  53. data/lib/action_view/renderer/partial_renderer.rb +1 -35
  54. data/lib/action_view/renderer/renderer.rb +4 -4
  55. data/lib/action_view/renderer/streaming_template_renderer.rb +3 -3
  56. data/lib/action_view/renderer/template_renderer.rb +6 -2
  57. data/lib/action_view/rendering.rb +3 -3
  58. data/lib/action_view/ripper_ast_parser.rb +198 -0
  59. data/lib/action_view/routing_url_for.rb +8 -5
  60. data/lib/action_view/template/error.rb +108 -13
  61. data/lib/action_view/template/handlers/erb.rb +6 -0
  62. data/lib/action_view/template/handlers.rb +3 -3
  63. data/lib/action_view/template/html.rb +3 -3
  64. data/lib/action_view/template/inline.rb +3 -3
  65. data/lib/action_view/template/raw_file.rb +3 -3
  66. data/lib/action_view/template/resolver.rb +89 -314
  67. data/lib/action_view/template/text.rb +3 -3
  68. data/lib/action_view/template/types.rb +14 -12
  69. data/lib/action_view/template.rb +18 -2
  70. data/lib/action_view/template_details.rb +66 -0
  71. data/lib/action_view/template_path.rb +64 -0
  72. data/lib/action_view/test_case.rb +7 -3
  73. data/lib/action_view/testing/resolvers.rb +11 -12
  74. data/lib/action_view/unbound_template.rb +33 -7
  75. data/lib/action_view/version.rb +1 -1
  76. data/lib/action_view/view_paths.rb +4 -4
  77. data/lib/action_view.rb +2 -3
  78. data/lib/assets/compiled/rails-ujs.js +36 -5
  79. metadata +23 -16
@@ -12,12 +12,11 @@ module ActionView
12
12
  # <tt>LookupContext</tt> is also responsible for generating a key, given to
13
13
  # view paths, used in the resolver cache lookup. Since this key is generated
14
14
  # only once during the request, it speeds up all cache accesses.
15
- class LookupContext #:nodoc:
15
+ class LookupContext # :nodoc:
16
16
  attr_accessor :prefixes, :rendered_format
17
17
 
18
- mattr_accessor :fallbacks, default: FallbackFileSystemResolver.instances
19
-
20
- mattr_accessor :registered_details, default: []
18
+ singleton_class.attr_accessor :registered_details
19
+ self.registered_details = []
21
20
 
22
21
  def self.register_detail(name, &block)
23
22
  registered_details << name
@@ -37,7 +36,7 @@ module ActionView
37
36
  end
38
37
 
39
38
  # Holds accessors for the registered details.
40
- module Accessors #:nodoc:
39
+ module Accessors # :nodoc:
41
40
  DEFAULT_PROCS = {}
42
41
  end
43
42
 
@@ -52,7 +51,7 @@ module ActionView
52
51
  register_detail(:variants) { [] }
53
52
  register_detail(:handlers) { Template::Handlers.extensions }
54
53
 
55
- class DetailsKey #:nodoc:
54
+ class DetailsKey # :nodoc:
56
55
  alias :eql? :equal?
57
56
 
58
57
  @details_keys = Concurrent::Map.new
@@ -68,14 +67,13 @@ module ActionView
68
67
  details = details.dup
69
68
  details[:formats] &= Template::Types.symbols
70
69
  end
71
- @details_keys[details] ||= Object.new
70
+ @details_keys[details] ||= TemplateDetails::Requested.new(**details)
72
71
  end
73
72
 
74
73
  def self.clear
75
74
  ActionView::ViewPaths.all_view_paths.each do |path_set|
76
75
  path_set.each(&:clear_cache)
77
76
  end
78
- ActionView::LookupContext.fallbacks.each(&:clear_cache)
79
77
  @view_context_class = nil
80
78
  @details_keys.clear
81
79
  @digest_cache.clear
@@ -98,7 +96,7 @@ module ActionView
98
96
 
99
97
  # Calculate the details key. Remove the handlers from calculation to improve performance
100
98
  # since the user cannot modify it explicitly.
101
- def details_key #:nodoc:
99
+ def details_key # :nodoc:
102
100
  @details_key ||= DetailsKey.details_cache_key(@details) if @cache
103
101
  end
104
102
 
@@ -124,39 +122,32 @@ module ActionView
124
122
  attr_reader :view_paths, :html_fallback_for_js
125
123
 
126
124
  def find(name, prefixes = [], partial = false, keys = [], options = {})
127
- @view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options))
125
+ name, prefixes = normalize_name(name, prefixes)
126
+ details, details_key = detail_args_for(options)
127
+ @view_paths.find(name, prefixes, partial, details, details_key, keys)
128
128
  end
129
129
  alias :find_template :find
130
130
 
131
131
  def find_all(name, prefixes = [], partial = false, keys = [], options = {})
132
- @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
132
+ name, prefixes = normalize_name(name, prefixes)
133
+ details, details_key = detail_args_for(options)
134
+ @view_paths.find_all(name, prefixes, partial, details, details_key, keys)
133
135
  end
134
136
 
135
137
  def exists?(name, prefixes = [], partial = false, keys = [], **options)
136
- @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
138
+ name, prefixes = normalize_name(name, prefixes)
139
+ details, details_key = detail_args_for(options)
140
+ @view_paths.exists?(name, prefixes, partial, details, details_key, keys)
137
141
  end
138
142
  alias :template_exists? :exists?
139
143
 
140
144
  def any?(name, prefixes = [], partial = false)
141
- @view_paths.exists?(*args_for_any(name, prefixes, partial))
145
+ name, prefixes = normalize_name(name, prefixes)
146
+ details, details_key = detail_args_for_any
147
+ @view_paths.exists?(name, prefixes, partial, details, details_key, [])
142
148
  end
143
149
  alias :any_templates? :any?
144
150
 
145
- # Adds fallbacks to the view paths. Useful in cases when you are rendering
146
- # a :file.
147
- def with_fallbacks
148
- view_paths = build_view_paths((@view_paths.paths + self.class.fallbacks).uniq)
149
-
150
- if block_given?
151
- raise ArgumentError, <<~eowarn.squish
152
- Calling `with_fallbacks` with a block is not supported. Call methods on
153
- the lookup context returned by `with_fallbacks` instead.
154
- eowarn
155
- else
156
- ActionView::LookupContext.new(view_paths, @details, @prefixes)
157
- end
158
- end
159
-
160
151
  private
161
152
  # Whenever setting view paths, makes a copy so that we can manipulate them in
162
153
  # instance objects as we wish.
@@ -164,12 +155,6 @@ module ActionView
164
155
  ActionView::PathSet.new(Array(paths))
165
156
  end
166
157
 
167
- def args_for_lookup(name, prefixes, partial, keys, details_options)
168
- name, prefixes = normalize_name(name, prefixes)
169
- details, details_key = detail_args_for(details_options)
170
- [name, prefixes, partial || false, details, details_key, keys]
171
- end
172
-
173
158
  # Compute details hash and key according to user options (e.g. passed from #render).
174
159
  def detail_args_for(options) # :doc:
175
160
  return @details, details_key if options.empty? # most common path.
@@ -184,17 +169,11 @@ module ActionView
184
169
  [user_details, details_key]
185
170
  end
186
171
 
187
- def args_for_any(name, prefixes, partial)
188
- name, prefixes = normalize_name(name, prefixes)
189
- details, details_key = detail_args_for_any
190
- [name, prefixes, partial || false, details, details_key]
191
- end
192
-
193
172
  def detail_args_for_any
194
173
  @detail_args_for_any ||= begin
195
174
  details = {}
196
175
 
197
- registered_details.each do |k|
176
+ LookupContext.registered_details.each do |k|
198
177
  if k == :variants
199
178
  details[k] = :any
200
179
  else
@@ -210,19 +189,21 @@ module ActionView
210
189
  end
211
190
  end
212
191
 
213
- # Support legacy foo.erb names even though we now ignore .erb
214
- # as well as incorrectly putting part of the path in the template
215
- # name instead of the prefix.
192
+ # Fix when prefix is specified as part of the template name
216
193
  def normalize_name(name, prefixes)
217
- prefixes = prefixes.presence
218
- parts = name.to_s.split("/")
219
- parts.shift if parts.first.empty?
220
- name = parts.pop
194
+ name = name.to_s
195
+ idx = name.rindex("/")
196
+ return name, prefixes.presence || [""] unless idx
221
197
 
222
- return name, prefixes || [""] if parts.empty?
198
+ path_prefix = name[0, idx]
199
+ path_prefix = path_prefix.from(1) if path_prefix.start_with?("/")
200
+ name = name.from(idx + 1)
223
201
 
224
- parts = parts.join("/")
225
- prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
202
+ if !prefixes || prefixes.empty?
203
+ prefixes = [path_prefix]
204
+ else
205
+ prefixes = prefixes.map { |p| "#{p}/#{path_prefix}" }
206
+ end
226
207
 
227
208
  return name, prefixes
228
209
  end
@@ -254,7 +235,7 @@ module ActionView
254
235
  end
255
236
 
256
237
  def initialize_details(target, details)
257
- registered_details.each do |k|
238
+ LookupContext.registered_details.each do |k|
258
239
  target[k] = details[k] || Accessors::DEFAULT_PROCS[k].call
259
240
  end
260
241
  target
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionView
4
- module ModelNaming #:nodoc:
5
- # Converts the given object to an ActiveModel compliant one.
4
+ module ModelNaming # :nodoc:
5
+ # Converts the given object to an Active Model compliant one.
6
6
  def convert_to_model(object)
7
7
  object.respond_to?(:to_model) ? object.to_model : object
8
8
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActionView #:nodoc:
3
+ module ActionView # :nodoc:
4
4
  # = Action View PathSet
5
5
  #
6
6
  # This class is used to store and access paths in Action View. A number of
@@ -8,7 +8,7 @@ module ActionView #:nodoc:
8
8
  # set and also perform operations on other +PathSet+ objects.
9
9
  #
10
10
  # A +LookupContext+ will use a +PathSet+ to store the paths in its context.
11
- class PathSet #:nodoc:
11
+ class PathSet # :nodoc:
12
12
  include Enumerable
13
13
 
14
14
  attr_reader :paths
@@ -44,44 +44,38 @@ module ActionView #:nodoc:
44
44
  METHOD
45
45
  end
46
46
 
47
- def find(*args)
48
- find_all(*args).first || raise(MissingTemplate.new(self, *args))
47
+ def find(path, prefixes, partial, details, details_key, locals)
48
+ find_all(path, prefixes, partial, details, details_key, locals).first ||
49
+ raise(MissingTemplate.new(self, path, prefixes, partial, details, details_key, locals))
49
50
  end
50
51
 
51
- def find_all(path, prefixes = [], *args)
52
- _find_all path, prefixes, args
53
- end
54
-
55
- def exists?(path, prefixes, *args)
56
- find_all(path, prefixes, *args).any?
57
- end
58
-
59
- def find_all_with_query(query) # :nodoc:
60
- paths.each do |resolver|
61
- templates = resolver.find_all_with_query(query)
52
+ def find_all(path, prefixes, partial, details, details_key, locals)
53
+ search_combinations(prefixes) do |resolver, prefix|
54
+ templates = resolver.find_all(path, prefix, partial, details, details_key, locals)
62
55
  return templates unless templates.empty?
63
56
  end
64
-
65
57
  []
66
58
  end
67
59
 
60
+ def exists?(path, prefixes, partial, details, details_key, locals)
61
+ find_all(path, prefixes, partial, details, details_key, locals).any?
62
+ end
63
+
68
64
  private
69
- def _find_all(path, prefixes, args)
70
- prefixes = [prefixes] if String === prefixes
65
+ def search_combinations(prefixes)
66
+ prefixes = Array(prefixes)
71
67
  prefixes.each do |prefix|
72
68
  paths.each do |resolver|
73
- templates = resolver.find_all(path, prefix, *args)
74
- return templates unless templates.empty?
69
+ yield resolver, prefix
75
70
  end
76
71
  end
77
- []
78
72
  end
79
73
 
80
74
  def typecast(paths)
81
75
  paths.map do |path|
82
76
  case path
83
77
  when Pathname, String
84
- OptimizedFileSystemResolver.new path.to_s
78
+ FileSystemResolver.new path.to_s
85
79
  else
86
80
  path
87
81
  end
@@ -10,6 +10,9 @@ module ActionView
10
10
  config.action_view.embed_authenticity_token_in_remote_forms = nil
11
11
  config.action_view.debug_missing_translation = true
12
12
  config.action_view.default_enforce_utf8 = nil
13
+ config.action_view.image_loading = nil
14
+ config.action_view.image_decoding = nil
15
+ config.action_view.apply_stylesheet_media_default = true
13
16
 
14
17
  config.eager_load_namespaces << ActionView
15
18
 
@@ -38,18 +41,27 @@ module ActionView
38
41
  end
39
42
 
40
43
  config.after_initialize do |app|
44
+ button_to_generates_button_tag = app.config.action_view.delete(:button_to_generates_button_tag)
45
+ unless button_to_generates_button_tag.nil?
46
+ ActionView::Helpers::UrlHelper.button_to_generates_button_tag = button_to_generates_button_tag
47
+ end
48
+ end
49
+
50
+ config.after_initialize do |app|
51
+ frozen_string_literal = app.config.action_view.delete(:frozen_string_literal)
52
+ ActionView::Template.frozen_string_literal = frozen_string_literal
53
+ end
54
+
55
+ config.after_initialize do |app|
56
+ ActionView::Helpers::AssetTagHelper.image_loading = app.config.action_view.delete(:image_loading)
57
+ ActionView::Helpers::AssetTagHelper.image_decoding = app.config.action_view.delete(:image_decoding)
41
58
  ActionView::Helpers::AssetTagHelper.preload_links_header = app.config.action_view.delete(:preload_links_header)
59
+ ActionView::Helpers::AssetTagHelper.apply_stylesheet_media_default = app.config.action_view.delete(:apply_stylesheet_media_default)
42
60
  end
43
61
 
44
62
  config.after_initialize do |app|
45
63
  ActiveSupport.on_load(:action_view) do
46
64
  app.config.action_view.each do |k, v|
47
- if k == :raise_on_missing_translations
48
- ActiveSupport::Deprecation.warn \
49
- "action_view.raise_on_missing_translations is deprecated and will be removed in Rails 7.0. " \
50
- "Set i18n.raise_on_missing_translations instead. " \
51
- "Note that this new setting also affects how missing translations are handled in controllers."
52
- end
53
65
  send "#{k}=", v
54
66
  end
55
67
  end
@@ -85,7 +97,7 @@ module ActionView
85
97
  end
86
98
 
87
99
  unless enable_caching
88
- app.executor.to_run ActionView::CacheExpiry::Executor.new(watcher: app.config.file_watcher)
100
+ app.executor.register_hook ActionView::CacheExpiry::Executor.new(watcher: app.config.file_watcher)
89
101
  end
90
102
  end
91
103
 
@@ -102,7 +102,7 @@ module ActionView
102
102
  # on the default implementation (which just joins all key attributes with '_') or on your own
103
103
  # overwritten version of the method. By default, this implementation passes the key string through a
104
104
  # method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
105
- # make sure yourself that your dom ids are valid, in case you overwrite this method.
105
+ # make sure yourself that your dom ids are valid, in case you override this method.
106
106
  def record_key_for_dom_id(record) # :doc:
107
107
  key = convert_to_model(record).to_key
108
108
  key ? key.join(JOIN) : key
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_view/ripper_ast_parser"
4
+
5
+ module ActionView
6
+ class RenderParser # :nodoc:
7
+ def initialize(name, code)
8
+ @name = name
9
+ @code = code
10
+ @parser = RipperASTParser
11
+ end
12
+
13
+ def render_calls
14
+ render_nodes = @parser.parse_render_nodes(@code)
15
+
16
+ render_nodes.map do |method, nodes|
17
+ nodes.map { |n| send(:parse_render, n) }
18
+ end.flatten.compact
19
+ end
20
+
21
+ private
22
+ def directory
23
+ File.dirname(@name)
24
+ end
25
+
26
+ def resolve_path_directory(path)
27
+ if path.include?("/")
28
+ path
29
+ else
30
+ "#{directory}/#{path}"
31
+ end
32
+ end
33
+
34
+ # Convert
35
+ # render("foo", ...)
36
+ # into either
37
+ # render(template: "foo", ...)
38
+ # or
39
+ # render(partial: "foo", ...)
40
+ def normalize_args(string, options_hash)
41
+ if options_hash
42
+ { partial: string, locals: options_hash }
43
+ else
44
+ { partial: string }
45
+ end
46
+ end
47
+
48
+ def parse_render(node)
49
+ node = node.argument_nodes
50
+
51
+ if (node.length == 1 || node.length == 2) && !node[0].hash?
52
+ if node.length == 1
53
+ options = normalize_args(node[0], nil)
54
+ elsif node.length == 2
55
+ options = normalize_args(node[0], node[1])
56
+ end
57
+
58
+ return nil unless options
59
+
60
+ parse_render_from_options(options)
61
+ elsif node.length == 1 && node[0].hash?
62
+ options = parse_hash_to_symbols(node[0])
63
+
64
+ return nil unless options
65
+
66
+ parse_render_from_options(options)
67
+ else
68
+ nil
69
+ end
70
+ end
71
+
72
+ def parse_hash(node)
73
+ node.hash? && node.to_hash
74
+ end
75
+
76
+ def parse_hash_to_symbols(node)
77
+ hash = parse_hash(node)
78
+
79
+ return unless hash
80
+
81
+ hash.transform_keys do |key_node|
82
+ key = parse_sym(key_node)
83
+
84
+ return unless key
85
+
86
+ key
87
+ end
88
+ end
89
+
90
+ ALL_KNOWN_KEYS = [:partial, :template, :layout, :formats, :locals, :object, :collection, :as, :status, :content_type, :location, :spacer_template]
91
+
92
+ RENDER_TYPE_KEYS =
93
+ [:partial, :template, :layout]
94
+
95
+ def parse_render_from_options(options_hash)
96
+ renders = []
97
+ keys = options_hash.keys
98
+
99
+ if (keys & RENDER_TYPE_KEYS).size < 1
100
+ # Must have at least one of render keys
101
+ return nil
102
+ end
103
+
104
+ if (keys - ALL_KNOWN_KEYS).any?
105
+ # de-opt in case of unknown option
106
+ return nil
107
+ end
108
+
109
+ render_type = (keys & RENDER_TYPE_KEYS)[0]
110
+
111
+ node = options_hash[render_type]
112
+
113
+ if node.string?
114
+ template = resolve_path_directory(node.to_string)
115
+ else
116
+ if node.variable_reference?
117
+ dependency = node.variable_name.sub(/\A(?:\$|@{1,2})/, "")
118
+ elsif node.vcall?
119
+ dependency = node.variable_name
120
+ elsif node.call?
121
+ dependency = node.call_method_name
122
+ else
123
+ return
124
+ end
125
+
126
+ object_template = true
127
+ template = "#{dependency.pluralize}/#{dependency.singularize}"
128
+ end
129
+
130
+ return unless template
131
+
132
+ if spacer_template = render_template_with_spacer?(options_hash)
133
+ virtual_path = partial_to_virtual_path(:partial, spacer_template)
134
+ renders << virtual_path
135
+ end
136
+
137
+ if options_hash.key?(:object) || options_hash.key?(:collection) || object_template
138
+ return nil if options_hash.key?(:object) && options_hash.key?(:collection)
139
+ return nil unless options_hash.key?(:partial)
140
+ end
141
+
142
+ virtual_path = partial_to_virtual_path(render_type, template)
143
+ renders << virtual_path
144
+
145
+ # Support for rendering multiple templates (i.e. a partial with a layout)
146
+ if layout_template = render_template_with_layout?(render_type, options_hash)
147
+ virtual_path = partial_to_virtual_path(:layout, layout_template)
148
+
149
+ renders << virtual_path
150
+ end
151
+
152
+ renders
153
+ end
154
+
155
+ def parse_str(node)
156
+ node.string? && node.to_string
157
+ end
158
+
159
+ def parse_sym(node)
160
+ node.symbol? && node.to_symbol
161
+ end
162
+
163
+ private
164
+ def render_template_with_layout?(render_type, options_hash)
165
+ if render_type != :layout && options_hash.key?(:layout)
166
+ parse_str(options_hash[:layout])
167
+ end
168
+ end
169
+
170
+ def render_template_with_spacer?(options_hash)
171
+ if options_hash.key?(:spacer_template)
172
+ parse_str(options_hash[:spacer_template])
173
+ end
174
+ end
175
+
176
+ def partial_to_virtual_path(render_type, partial_path)
177
+ if render_type == :partial || render_type == :layout
178
+ partial_path.gsub(%r{(/|^)([^/]*)\z}, '\1_\2')
179
+ else
180
+ partial_path
181
+ end
182
+ end
183
+
184
+ def layout_to_virtual_path(layout_path)
185
+ "layouts/#{layout_path}"
186
+ end
187
+ end
188
+ end
@@ -18,7 +18,7 @@ module ActionView
18
18
  # renderer object of the correct type is created, and the +render+ method on
19
19
  # that new object is called in turn. This abstracts the set up and rendering
20
20
  # into a separate classes for partials and templates.
21
- class AbstractRenderer #:nodoc:
21
+ class AbstractRenderer # :nodoc:
22
22
  delegate :template_exists?, :any_templates?, :formats, to: :@lookup_context
23
23
 
24
24
  def initialize(lookup_context)
@@ -158,7 +158,7 @@ module ActionView
158
158
 
159
159
  def extract_details(options) # :doc:
160
160
  details = nil
161
- @lookup_context.registered_details.each do |key|
161
+ LookupContext.registered_details.each do |key|
162
162
  value = options[key]
163
163
 
164
164
  if value
@@ -27,7 +27,7 @@ module ActionView
27
27
  # This would first render <tt>advertiser/_account.html.erb</tt> with <tt>@buyer</tt> passed in as the local variable +account+, then
28
28
  # render <tt>advertiser/_ad.html.erb</tt> and pass the local variable +ad+ to the template for display.
29
29
  #
30
- # == The :as and :object options
30
+ # == The +:as+ and +:object+ options
31
31
  #
32
32
  # By default ActionView::PartialRenderer doesn't have any local variables.
33
33
  # The <tt>:object</tt> option can be used to pass an object to the partial. For instance:
@@ -217,40 +217,6 @@ module ActionView
217
217
  # </div>
218
218
  #
219
219
  # As you can see, the <tt>:locals</tt> hash is shared between both the partial and its layout.
220
- #
221
- # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
222
- # an array to layout and treat it as an enumerable.
223
- #
224
- # <%# app/views/users/_user.html.erb %>
225
- # <div class="user">
226
- # Budget: $<%= user.budget %>
227
- # <%= yield user %>
228
- # </div>
229
- #
230
- # <%# app/views/users/index.html.erb %>
231
- # <%= render layout: @users do |user| %>
232
- # Title: <%= user.title %>
233
- # <% end %>
234
- #
235
- # This will render the layout for each user and yield to the block, passing the user, each time.
236
- #
237
- # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
238
- #
239
- # <%# app/views/users/_user.html.erb %>
240
- # <div class="user">
241
- # <%= yield user, :header %>
242
- # Budget: $<%= user.budget %>
243
- # <%= yield user, :footer %>
244
- # </div>
245
- #
246
- # <%# app/views/users/index.html.erb %>
247
- # <%= render layout: @users do |user, section| %>
248
- # <%- case section when :header -%>
249
- # Title: <%= user.title %>
250
- # <%- when :footer -%>
251
- # Deadline: <%= user.deadline %>
252
- # <%- end -%>
253
- # <% end %>
254
220
  class PartialRenderer < AbstractRenderer
255
221
  include CollectionCaching
256
222
 
@@ -44,12 +44,12 @@ module ActionView
44
44
  end
45
45
 
46
46
  # Direct access to template rendering.
47
- def render_template(context, options) #:nodoc:
47
+ def render_template(context, options) # :nodoc:
48
48
  render_template_to_object(context, options).body
49
49
  end
50
50
 
51
51
  # Direct access to partial rendering.
52
- def render_partial(context, options, &block) #:nodoc:
52
+ def render_partial(context, options, &block) # :nodoc:
53
53
  render_partial_to_object(context, options, &block).body
54
54
  end
55
55
 
@@ -57,11 +57,11 @@ module ActionView
57
57
  @cache_hits ||= {}
58
58
  end
59
59
 
60
- def render_template_to_object(context, options) #:nodoc:
60
+ def render_template_to_object(context, options) # :nodoc:
61
61
  TemplateRenderer.new(@lookup_context).render(context, options)
62
62
  end
63
63
 
64
- def render_partial_to_object(context, options, &block) #:nodoc:
64
+ def render_partial_to_object(context, options, &block) # :nodoc:
65
65
  partial = options[:partial]
66
66
  if String === partial
67
67
  collection = collection_from_options(options)
@@ -7,11 +7,11 @@ module ActionView
7
7
  #
8
8
  # * Support streaming from child templates, partials and so on.
9
9
  # * Rack::Cache needs to support streaming bodies
10
- class StreamingTemplateRenderer < TemplateRenderer #:nodoc:
10
+ class StreamingTemplateRenderer < TemplateRenderer # :nodoc:
11
11
  # A valid Rack::Body (i.e. it responds to each).
12
12
  # It is initialized with a block that, when called, starts
13
13
  # rendering the template.
14
- class Body #:nodoc:
14
+ class Body # :nodoc:
15
15
  def initialize(&start)
16
16
  @start = start
17
17
  end
@@ -42,7 +42,7 @@ module ActionView
42
42
  # For streaming, instead of rendering a given a template, we return a Body
43
43
  # object that responds to each. This object is initialized with a block
44
44
  # that knows how to render the template.
45
- def render_template(view, template, layout_name = nil, locals = {}) #:nodoc:
45
+ def render_template(view, template, layout_name = nil, locals = {}) # :nodoc:
46
46
  return [super.body] unless layout_name && template.supports_streaming?
47
47
 
48
48
  locals ||= {}
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionView
4
- class TemplateRenderer < AbstractRenderer #:nodoc:
4
+ class TemplateRenderer < AbstractRenderer # :nodoc:
5
5
  def render(context, options)
6
6
  @details = extract_details(options)
7
7
  template = determine_template(options)
@@ -26,7 +26,11 @@ module ActionView
26
26
  if File.exist?(options[:file])
27
27
  Template::RawFile.new(options[:file])
28
28
  else
29
- raise ArgumentError, "`render file:` should be given the absolute path to a file. '#{options[:file]}' was given instead"
29
+ if Pathname.new(options[:file]).absolute?
30
+ raise ArgumentError, "File #{options[:file]} does not exist"
31
+ else
32
+ raise ArgumentError, "`render file:` should be given the absolute path to a file. '#{options[:file]}' was given instead"
33
+ end
30
34
  end
31
35
  elsif options.key?(:inline)
32
36
  handler = Template.handler_for_extension(options[:type] || "erb")
@@ -5,7 +5,7 @@ require "action_view/view_paths"
5
5
  module ActionView
6
6
  # This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
7
7
  # it will trigger the lookup_context and consequently expire the cache.
8
- class I18nProxy < ::I18n::Config #:nodoc:
8
+ class I18nProxy < ::I18n::Config # :nodoc:
9
9
  attr_reader :original_config, :lookup_context
10
10
 
11
11
  def initialize(original_config, lookup_context)
@@ -33,8 +33,8 @@ module ActionView
33
33
  super
34
34
  end
35
35
 
36
- # Overwrite process to set up I18n proxy.
37
- def process(*) #:nodoc:
36
+ # Override process to set up I18n proxy.
37
+ def process(...) # :nodoc:
38
38
  old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
39
39
  super
40
40
  ensure