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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +265 -261
- data/MIT-LICENSE +1 -0
- data/lib/action_view/base.rb +4 -7
- data/lib/action_view/buffers.rb +2 -2
- data/lib/action_view/cache_expiry.rb +46 -32
- data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
- data/lib/action_view/dependency_tracker.rb +6 -147
- data/lib/action_view/digestor.rb +7 -4
- data/lib/action_view/flows.rb +4 -4
- data/lib/action_view/gem_version.rb +5 -5
- data/lib/action_view/helpers/active_model_helper.rb +2 -2
- data/lib/action_view/helpers/asset_tag_helper.rb +95 -39
- data/lib/action_view/helpers/asset_url_helper.rb +16 -16
- data/lib/action_view/helpers/atom_feed_helper.rb +3 -4
- data/lib/action_view/helpers/cache_helper.rb +52 -3
- data/lib/action_view/helpers/capture_helper.rb +4 -4
- data/lib/action_view/helpers/controller_helper.rb +2 -2
- data/lib/action_view/helpers/csp_helper.rb +1 -1
- data/lib/action_view/helpers/csrf_helper.rb +2 -2
- data/lib/action_view/helpers/date_helper.rb +111 -43
- data/lib/action_view/helpers/debug_helper.rb +3 -1
- data/lib/action_view/helpers/form_helper.rb +211 -85
- data/lib/action_view/helpers/form_options_helper.rb +70 -33
- data/lib/action_view/helpers/form_tag_helper.rb +150 -53
- data/lib/action_view/helpers/javascript_helper.rb +3 -5
- data/lib/action_view/helpers/number_helper.rb +17 -16
- data/lib/action_view/helpers/output_safety_helper.rb +4 -4
- data/lib/action_view/helpers/rendering_helper.rb +5 -6
- data/lib/action_view/helpers/sanitize_helper.rb +3 -3
- data/lib/action_view/helpers/tag_helper.rb +37 -8
- data/lib/action_view/helpers/tags/base.rb +5 -25
- data/lib/action_view/helpers/tags/check_box.rb +1 -1
- data/lib/action_view/helpers/tags/collection_select.rb +1 -1
- data/lib/action_view/helpers/tags/file_field.rb +16 -0
- data/lib/action_view/helpers/tags/select.rb +1 -1
- data/lib/action_view/helpers/tags/time_field.rb +10 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
- data/lib/action_view/helpers/tags.rb +3 -2
- data/lib/action_view/helpers/text_helper.rb +25 -14
- data/lib/action_view/helpers/translation_helper.rb +12 -43
- data/lib/action_view/helpers/url_helper.rb +194 -123
- data/lib/action_view/helpers.rb +25 -25
- data/lib/action_view/layouts.rb +7 -4
- data/lib/action_view/lookup_context.rb +33 -52
- data/lib/action_view/model_naming.rb +2 -2
- data/lib/action_view/path_set.rb +16 -22
- data/lib/action_view/railtie.rb +19 -7
- data/lib/action_view/record_identifier.rb +1 -1
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +2 -2
- data/lib/action_view/renderer/partial_renderer.rb +1 -35
- data/lib/action_view/renderer/renderer.rb +4 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -3
- data/lib/action_view/renderer/template_renderer.rb +6 -2
- data/lib/action_view/rendering.rb +3 -3
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +8 -5
- data/lib/action_view/template/error.rb +108 -13
- data/lib/action_view/template/handlers/erb.rb +6 -0
- data/lib/action_view/template/handlers.rb +3 -3
- data/lib/action_view/template/html.rb +3 -3
- data/lib/action_view/template/inline.rb +3 -3
- data/lib/action_view/template/raw_file.rb +3 -3
- data/lib/action_view/template/resolver.rb +89 -314
- data/lib/action_view/template/text.rb +3 -3
- data/lib/action_view/template/types.rb +14 -12
- data/lib/action_view/template.rb +18 -2
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +64 -0
- data/lib/action_view/test_case.rb +7 -3
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +33 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +4 -4
- data/lib/action_view.rb +2 -3
- data/lib/assets/compiled/rails-ujs.js +36 -5
- 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
|
15
|
+
class LookupContext # :nodoc:
|
16
16
|
attr_accessor :prefixes, :rendered_format
|
17
17
|
|
18
|
-
|
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
|
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
|
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] ||=
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
218
|
-
|
219
|
-
|
220
|
-
name = parts.pop
|
194
|
+
name = name.to_s
|
195
|
+
idx = name.rindex("/")
|
196
|
+
return name, prefixes.presence || [""] unless idx
|
221
197
|
|
222
|
-
|
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
|
-
|
225
|
-
|
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
|
5
|
-
# Converts the given object to an
|
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
|
data/lib/action_view/path_set.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module ActionView
|
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
|
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(
|
48
|
-
find_all(
|
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
|
52
|
-
|
53
|
-
|
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
|
70
|
-
prefixes =
|
65
|
+
def search_combinations(prefixes)
|
66
|
+
prefixes = Array(prefixes)
|
71
67
|
prefixes.each do |prefix|
|
72
68
|
paths.each do |resolver|
|
73
|
-
|
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
|
-
|
78
|
+
FileSystemResolver.new path.to_s
|
85
79
|
else
|
86
80
|
path
|
87
81
|
end
|
data/lib/action_view/railtie.rb
CHANGED
@@ -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.
|
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
|
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
|
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
|
-
|
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
|
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)
|
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)
|
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)
|
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)
|
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
|
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
|
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 = {})
|
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
|
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
|
-
|
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
|
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
|
-
#
|
37
|
-
def process(
|
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
|