actionview 6.0.0.beta1 → 6.1.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +273 -119
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -3
- data/lib/action_view/base.rb +81 -15
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +0 -5
- data/lib/action_view/dependency_tracker.rb +10 -4
- data/lib/action_view/digestor.rb +11 -19
- data/lib/action_view/flows.rb +0 -1
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/active_model_helper.rb +0 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +62 -22
- data/lib/action_view/helpers/asset_url_helper.rb +6 -4
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
- data/lib/action_view/helpers/cache_helper.rb +16 -23
- data/lib/action_view/helpers/csp_helper.rb +4 -2
- data/lib/action_view/helpers/date_helper.rb +5 -6
- data/lib/action_view/helpers/form_helper.rb +70 -34
- data/lib/action_view/helpers/form_options_helper.rb +10 -18
- data/lib/action_view/helpers/form_tag_helper.rb +12 -9
- data/lib/action_view/helpers/javascript_helper.rb +7 -5
- data/lib/action_view/helpers/number_helper.rb +9 -8
- data/lib/action_view/helpers/output_safety_helper.rb +1 -1
- data/lib/action_view/helpers/rendering_helper.rb +17 -7
- data/lib/action_view/helpers/sanitize_helper.rb +10 -16
- data/lib/action_view/helpers/tag_helper.rb +94 -19
- data/lib/action_view/helpers/tags/base.rb +10 -7
- data/lib/action_view/helpers/tags/check_box.rb +0 -1
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
- data/lib/action_view/helpers/tags/color_field.rb +0 -1
- data/lib/action_view/helpers/tags/date_field.rb +1 -2
- data/lib/action_view/helpers/tags/date_select.rb +2 -3
- data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
- data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -2
- data/lib/action_view/helpers/tags/label.rb +4 -1
- data/lib/action_view/helpers/tags/month_field.rb +1 -2
- data/lib/action_view/helpers/tags/radio_button.rb +0 -1
- data/lib/action_view/helpers/tags/select.rb +1 -2
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/time_field.rb +1 -2
- data/lib/action_view/helpers/tags/week_field.rb +1 -2
- data/lib/action_view/helpers/text_helper.rb +2 -3
- data/lib/action_view/helpers/translation_helper.rb +98 -51
- data/lib/action_view/helpers/url_helper.rb +124 -16
- data/lib/action_view/layouts.rb +8 -10
- data/lib/action_view/log_subscriber.rb +26 -11
- data/lib/action_view/lookup_context.rb +59 -31
- data/lib/action_view/path_set.rb +3 -12
- data/lib/action_view/railtie.rb +39 -41
- data/lib/action_view/record_identifier.rb +0 -1
- data/lib/action_view/renderer/abstract_renderer.rb +142 -11
- data/lib/action_view/renderer/collection_renderer.rb +196 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +35 -29
- data/lib/action_view/renderer/partial_renderer.rb +21 -273
- data/lib/action_view/renderer/renderer.rb +59 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +9 -7
- data/lib/action_view/renderer/template_renderer.rb +35 -27
- data/lib/action_view/rendering.rb +49 -29
- data/lib/action_view/routing_url_for.rb +1 -1
- data/lib/action_view/template/error.rb +30 -15
- data/lib/action_view/template/handlers/builder.rb +2 -2
- data/lib/action_view/template/handlers/erb/erubi.rb +15 -9
- data/lib/action_view/template/handlers/erb.rb +14 -19
- data/lib/action_view/template/handlers/html.rb +1 -1
- data/lib/action_view/template/handlers/raw.rb +2 -2
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/html.rb +5 -6
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +141 -140
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +2 -3
- data/lib/action_view/template.rb +49 -75
- data/lib/action_view/test_case.rb +20 -28
- data/lib/action_view/testing/resolvers.rb +18 -27
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/view_paths.rb +59 -38
- data/lib/action_view.rb +7 -2
- data/lib/assets/compiled/rails-ujs.js +25 -16
- metadata +30 -18
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "concurrent/map"
|
4
|
-
require "active_support/core_ext/module/remove_method"
|
5
4
|
require "active_support/core_ext/module/attribute_accessors"
|
6
5
|
require "action_view/template/resolver"
|
7
6
|
|
@@ -27,7 +26,7 @@ module ActionView
|
|
27
26
|
Accessors.define_method(:"default_#{name}", &block)
|
28
27
|
Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
|
29
28
|
def #{name}
|
30
|
-
@details
|
29
|
+
@details[:#{name}] || []
|
31
30
|
end
|
32
31
|
|
33
32
|
def #{name}=(value)
|
@@ -57,21 +56,39 @@ module ActionView
|
|
57
56
|
alias :eql? :equal?
|
58
57
|
|
59
58
|
@details_keys = Concurrent::Map.new
|
59
|
+
@digest_cache = Concurrent::Map.new
|
60
|
+
@view_context_mutex = Mutex.new
|
60
61
|
|
61
|
-
def self.
|
62
|
+
def self.digest_cache(details)
|
63
|
+
@digest_cache[details_cache_key(details)] ||= Concurrent::Map.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.details_cache_key(details)
|
62
67
|
if details[:formats]
|
63
68
|
details = details.dup
|
64
69
|
details[:formats] &= Template::Types.symbols
|
65
70
|
end
|
66
|
-
@details_keys[details] ||=
|
71
|
+
@details_keys[details] ||= Object.new
|
67
72
|
end
|
68
73
|
|
69
74
|
def self.clear
|
75
|
+
ActionView::ViewPaths.all_view_paths.each do |path_set|
|
76
|
+
path_set.each(&:clear_cache)
|
77
|
+
end
|
78
|
+
ActionView::LookupContext.fallbacks.each(&:clear_cache)
|
79
|
+
@view_context_class = nil
|
70
80
|
@details_keys.clear
|
81
|
+
@digest_cache.clear
|
71
82
|
end
|
72
83
|
|
73
84
|
def self.digest_caches
|
74
|
-
@
|
85
|
+
@digest_cache.values
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.view_context_class(klass)
|
89
|
+
@view_context_mutex.synchronize do
|
90
|
+
@view_context_class ||= klass.with_empty_template_cache
|
91
|
+
end
|
75
92
|
end
|
76
93
|
end
|
77
94
|
|
@@ -82,7 +99,7 @@ module ActionView
|
|
82
99
|
# Calculate the details key. Remove the handlers from calculation to improve performance
|
83
100
|
# since the user cannot modify it explicitly.
|
84
101
|
def details_key #:nodoc:
|
85
|
-
@details_key ||= DetailsKey.
|
102
|
+
@details_key ||= DetailsKey.details_cache_key(@details) if @cache
|
86
103
|
end
|
87
104
|
|
88
105
|
# Temporary skip passing the details_key forward.
|
@@ -94,9 +111,9 @@ module ActionView
|
|
94
111
|
end
|
95
112
|
|
96
113
|
private
|
97
|
-
|
98
114
|
def _set_detail(key, value) # :doc:
|
99
|
-
@details = @details.dup if @details_key
|
115
|
+
@details = @details.dup if @digest_cache || @details_key
|
116
|
+
@digest_cache = nil
|
100
117
|
@details_key = nil
|
101
118
|
@details[key] = value
|
102
119
|
end
|
@@ -106,21 +123,11 @@ module ActionView
|
|
106
123
|
module ViewPaths
|
107
124
|
attr_reader :view_paths, :html_fallback_for_js
|
108
125
|
|
109
|
-
# Whenever setting view paths, makes a copy so that we can manipulate them in
|
110
|
-
# instance objects as we wish.
|
111
|
-
def view_paths=(paths)
|
112
|
-
@view_paths = ActionView::PathSet.new(Array(paths))
|
113
|
-
end
|
114
|
-
|
115
126
|
def find(name, prefixes = [], partial = false, keys = [], options = {})
|
116
127
|
@view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options))
|
117
128
|
end
|
118
129
|
alias :find_template :find
|
119
130
|
|
120
|
-
def find_file(name, prefixes = [], partial = false, keys = [], options = {})
|
121
|
-
@view_paths.find_file(*args_for_lookup(name, prefixes, partial, keys, options))
|
122
|
-
end
|
123
|
-
|
124
131
|
def find_all(name, prefixes = [], partial = false, keys = [], options = {})
|
125
132
|
@view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
|
126
133
|
end
|
@@ -138,18 +145,24 @@ module ActionView
|
|
138
145
|
# Adds fallbacks to the view paths. Useful in cases when you are rendering
|
139
146
|
# a :file.
|
140
147
|
def with_fallbacks
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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)
|
146
157
|
end
|
147
|
-
yield
|
148
|
-
ensure
|
149
|
-
added_resolvers.times { view_paths.pop }
|
150
158
|
end
|
151
159
|
|
152
160
|
private
|
161
|
+
# Whenever setting view paths, makes a copy so that we can manipulate them in
|
162
|
+
# instance objects as we wish.
|
163
|
+
def build_view_paths(paths)
|
164
|
+
ActionView::PathSet.new(Array(paths))
|
165
|
+
end
|
153
166
|
|
154
167
|
def args_for_lookup(name, prefixes, partial, keys, details_options)
|
155
168
|
name, prefixes = normalize_name(name, prefixes)
|
@@ -163,7 +176,7 @@ module ActionView
|
|
163
176
|
user_details = @details.merge(options)
|
164
177
|
|
165
178
|
if @cache
|
166
|
-
details_key = DetailsKey.
|
179
|
+
details_key = DetailsKey.details_cache_key(user_details)
|
167
180
|
else
|
168
181
|
details_key = nil
|
169
182
|
end
|
@@ -190,7 +203,7 @@ module ActionView
|
|
190
203
|
end
|
191
204
|
|
192
205
|
if @cache
|
193
|
-
[details, DetailsKey.
|
206
|
+
[details, DetailsKey.details_cache_key(details)]
|
194
207
|
else
|
195
208
|
[details, nil]
|
196
209
|
end
|
@@ -221,16 +234,23 @@ module ActionView
|
|
221
234
|
|
222
235
|
def initialize(view_paths, details = {}, prefixes = [])
|
223
236
|
@details_key = nil
|
237
|
+
@digest_cache = nil
|
224
238
|
@cache = true
|
225
239
|
@prefixes = prefixes
|
226
|
-
@rendered_format = nil
|
227
240
|
|
228
241
|
@details = initialize_details({}, details)
|
229
|
-
|
242
|
+
@view_paths = build_view_paths(view_paths)
|
230
243
|
end
|
231
244
|
|
232
245
|
def digest_cache
|
233
|
-
|
246
|
+
@digest_cache ||= DetailsKey.digest_cache(@details)
|
247
|
+
end
|
248
|
+
|
249
|
+
def with_prepended_formats(formats)
|
250
|
+
details = @details.dup
|
251
|
+
details[:formats] = formats
|
252
|
+
|
253
|
+
self.class.new(@view_paths, details, @prefixes)
|
234
254
|
end
|
235
255
|
|
236
256
|
def initialize_details(target, details)
|
@@ -245,7 +265,15 @@ module ActionView
|
|
245
265
|
# add :html as fallback to :js.
|
246
266
|
def formats=(values)
|
247
267
|
if values
|
268
|
+
values = values.dup
|
248
269
|
values.concat(default_formats) if values.delete "*/*"
|
270
|
+
values.uniq!
|
271
|
+
|
272
|
+
invalid_values = (values - Template::Types.symbols)
|
273
|
+
unless invalid_values.empty?
|
274
|
+
raise ArgumentError, "Invalid formats: #{invalid_values.map(&:inspect).join(", ")}"
|
275
|
+
end
|
276
|
+
|
249
277
|
if values == [:js]
|
250
278
|
values << :html
|
251
279
|
@html_fallback_for_js = true
|
data/lib/action_view/path_set.rb
CHANGED
@@ -48,12 +48,8 @@ module ActionView #:nodoc:
|
|
48
48
|
find_all(*args).first || raise(MissingTemplate.new(self, *args))
|
49
49
|
end
|
50
50
|
|
51
|
-
def find_file(path, prefixes = [], *args)
|
52
|
-
_find_all(path, prefixes, args, true).first || raise(MissingTemplate.new(self, path, prefixes, *args))
|
53
|
-
end
|
54
|
-
|
55
51
|
def find_all(path, prefixes = [], *args)
|
56
|
-
_find_all path, prefixes, args
|
52
|
+
_find_all path, prefixes, args
|
57
53
|
end
|
58
54
|
|
59
55
|
def exists?(path, prefixes, *args)
|
@@ -70,16 +66,11 @@ module ActionView #:nodoc:
|
|
70
66
|
end
|
71
67
|
|
72
68
|
private
|
73
|
-
|
74
|
-
def _find_all(path, prefixes, args, outside_app)
|
69
|
+
def _find_all(path, prefixes, args)
|
75
70
|
prefixes = [prefixes] if String === prefixes
|
76
71
|
prefixes.each do |prefix|
|
77
72
|
paths.each do |resolver|
|
78
|
-
|
79
|
-
templates = resolver.find_all_anywhere(path, prefix, *args)
|
80
|
-
else
|
81
|
-
templates = resolver.find_all(path, prefix, *args)
|
82
|
-
end
|
73
|
+
templates = resolver.find_all(path, prefix, *args)
|
83
74
|
return templates unless templates.empty?
|
84
75
|
end
|
85
76
|
end
|
data/lib/action_view/railtie.rb
CHANGED
@@ -10,61 +10,55 @@ 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.finalize_compiled_template_methods = true
|
14
13
|
|
15
14
|
config.eager_load_namespaces << ActionView
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
app.config.action_view.delete(:embed_authenticity_token_in_remote_forms)
|
21
|
-
end
|
16
|
+
config.after_initialize do |app|
|
17
|
+
ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms =
|
18
|
+
app.config.action_view.delete(:embed_authenticity_token_in_remote_forms)
|
22
19
|
end
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
ActionView::Helpers::FormHelper.form_with_generates_remote_forms = form_with_generates_remote_forms
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
initializer "action_view.form_with_generates_ids" do |app|
|
32
|
-
ActiveSupport.on_load(:action_view) do
|
33
|
-
form_with_generates_ids = app.config.action_view.delete(:form_with_generates_ids)
|
34
|
-
unless form_with_generates_ids.nil?
|
35
|
-
ActionView::Helpers::FormHelper.form_with_generates_ids = form_with_generates_ids
|
36
|
-
end
|
37
|
-
end
|
21
|
+
config.after_initialize do |app|
|
22
|
+
form_with_generates_remote_forms = app.config.action_view.delete(:form_with_generates_remote_forms)
|
23
|
+
ActionView::Helpers::FormHelper.form_with_generates_remote_forms = form_with_generates_remote_forms
|
38
24
|
end
|
39
25
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
ActionView::Helpers::FormTagHelper.default_enforce_utf8 = default_enforce_utf8
|
45
|
-
end
|
26
|
+
config.after_initialize do |app|
|
27
|
+
form_with_generates_ids = app.config.action_view.delete(:form_with_generates_ids)
|
28
|
+
unless form_with_generates_ids.nil?
|
29
|
+
ActionView::Helpers::FormHelper.form_with_generates_ids = form_with_generates_ids
|
46
30
|
end
|
47
31
|
end
|
48
32
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
33
|
+
config.after_initialize do |app|
|
34
|
+
default_enforce_utf8 = app.config.action_view.delete(:default_enforce_utf8)
|
35
|
+
unless default_enforce_utf8.nil?
|
36
|
+
ActionView::Helpers::FormTagHelper.default_enforce_utf8 = default_enforce_utf8
|
53
37
|
end
|
54
38
|
end
|
55
39
|
|
56
|
-
|
57
|
-
|
40
|
+
config.after_initialize do |app|
|
41
|
+
ActionView::Helpers::AssetTagHelper.preload_links_header = app.config.action_view.delete(:preload_links_header)
|
58
42
|
end
|
59
43
|
|
60
|
-
|
44
|
+
config.after_initialize do |app|
|
61
45
|
ActiveSupport.on_load(:action_view) do
|
62
46
|
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 6.2. " \
|
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
|
63
53
|
send "#{k}=", v
|
64
54
|
end
|
65
55
|
end
|
66
56
|
end
|
67
57
|
|
58
|
+
initializer "action_view.logger" do
|
59
|
+
ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
|
60
|
+
end
|
61
|
+
|
68
62
|
initializer "action_view.caching" do |app|
|
69
63
|
ActiveSupport.on_load(:action_view) do
|
70
64
|
if app.config.action_view.cache_template_loading.nil?
|
@@ -73,14 +67,6 @@ module ActionView
|
|
73
67
|
end
|
74
68
|
end
|
75
69
|
|
76
|
-
initializer "action_view.per_request_digest_cache" do |app|
|
77
|
-
ActiveSupport.on_load(:action_view) do
|
78
|
-
unless ActionView::Resolver.caching?
|
79
|
-
app.executor.to_run ActionView::Digestor::PerExecutionDigestCacheExpiry
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
70
|
initializer "action_view.setup_action_pack" do |app|
|
85
71
|
ActiveSupport.on_load(:action_controller) do
|
86
72
|
ActionView::RoutingUrlFor.include(ActionDispatch::Routing::UrlFor)
|
@@ -91,6 +77,18 @@ module ActionView
|
|
91
77
|
PartialRenderer.collection_cache = app.config.action_controller.cache_store
|
92
78
|
end
|
93
79
|
|
80
|
+
config.after_initialize do |app|
|
81
|
+
enable_caching = if app.config.action_view.cache_template_loading.nil?
|
82
|
+
app.config.cache_classes
|
83
|
+
else
|
84
|
+
app.config.action_view.cache_template_loading
|
85
|
+
end
|
86
|
+
|
87
|
+
unless enable_caching
|
88
|
+
app.executor.to_run ActionView::CacheExpiry::Executor.new(watcher: app.config.file_watcher)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
94
92
|
rake_tasks do |app|
|
95
93
|
unless app.config.api_only
|
96
94
|
load "action_view/tasks/cache_digests.rake"
|
@@ -95,7 +95,6 @@ module ActionView
|
|
95
95
|
end
|
96
96
|
|
97
97
|
private
|
98
|
-
|
99
98
|
# Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
|
100
99
|
# This can be overwritten to customize the default generated string representation if desired.
|
101
100
|
# If you need to read back a key from a dom_id in order to query for the underlying database record,
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "concurrent/map"
|
4
|
+
|
3
5
|
module ActionView
|
4
6
|
# This class defines the interface for a renderer. Each class that
|
5
7
|
# subclasses +AbstractRenderer+ is used by the base +Renderer+ class to
|
@@ -14,10 +16,10 @@ module ActionView
|
|
14
16
|
#
|
15
17
|
# Whenever the +render+ method is called on the base +Renderer+ class, a new
|
16
18
|
# renderer object of the correct type is created, and the +render+ method on
|
17
|
-
# that new object is called in turn. This abstracts the
|
19
|
+
# that new object is called in turn. This abstracts the set up and rendering
|
18
20
|
# into a separate classes for partials and templates.
|
19
21
|
class AbstractRenderer #:nodoc:
|
20
|
-
delegate :
|
22
|
+
delegate :template_exists?, :any_templates?, :formats, to: :@lookup_context
|
21
23
|
|
22
24
|
def initialize(lookup_context)
|
23
25
|
@lookup_context = lookup_context
|
@@ -27,22 +29,143 @@ module ActionView
|
|
27
29
|
raise NotImplementedError
|
28
30
|
end
|
29
31
|
|
30
|
-
|
32
|
+
module ObjectRendering # :nodoc:
|
33
|
+
PREFIXED_PARTIAL_NAMES = Concurrent::Map.new do |h, k|
|
34
|
+
h[k] = Concurrent::Map.new
|
35
|
+
end
|
31
36
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
37
|
+
def initialize(lookup_context, options)
|
38
|
+
super
|
39
|
+
@context_prefix = lookup_context.prefixes.first
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def local_variable(path)
|
44
|
+
if as = @options[:as]
|
45
|
+
raise_invalid_option_as(as) unless /\A[a-z_]\w*\z/.match?(as.to_s)
|
46
|
+
as.to_sym
|
47
|
+
else
|
48
|
+
base = path.end_with?("/") ? "" : File.basename(path)
|
49
|
+
raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
|
50
|
+
$1.to_sym
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " \
|
55
|
+
"make sure your partial name starts with underscore."
|
56
|
+
|
57
|
+
OPTION_AS_ERROR_MESSAGE = "The value (%s) of the option `as` is not a valid Ruby identifier; " \
|
58
|
+
"make sure it starts with lowercase letter, " \
|
59
|
+
"and is followed by any combination of letters, numbers and underscores."
|
60
|
+
|
61
|
+
def raise_invalid_identifier(path)
|
62
|
+
raise ArgumentError, IDENTIFIER_ERROR_MESSAGE % path
|
63
|
+
end
|
64
|
+
|
65
|
+
def raise_invalid_option_as(as)
|
66
|
+
raise ArgumentError, OPTION_AS_ERROR_MESSAGE % as
|
67
|
+
end
|
68
|
+
|
69
|
+
# Obtains the path to where the object's partial is located. If the object
|
70
|
+
# responds to +to_partial_path+, then +to_partial_path+ will be called and
|
71
|
+
# will provide the path. If the object does not respond to +to_partial_path+,
|
72
|
+
# then an +ArgumentError+ is raised.
|
73
|
+
#
|
74
|
+
# If +prefix_partial_path_with_controller_namespace+ is true, then this
|
75
|
+
# method will prefix the partial paths with a namespace.
|
76
|
+
def partial_path(object, view)
|
77
|
+
object = object.to_model if object.respond_to?(:to_model)
|
78
|
+
|
79
|
+
path = if object.respond_to?(:to_partial_path)
|
80
|
+
object.to_partial_path
|
81
|
+
else
|
82
|
+
raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
|
83
|
+
end
|
84
|
+
|
85
|
+
if view.prefix_partial_path_with_controller_namespace
|
86
|
+
PREFIXED_PARTIAL_NAMES[@context_prefix][path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
|
87
|
+
else
|
88
|
+
path
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def merge_prefix_into_object_path(prefix, object_path)
|
93
|
+
if prefix.include?(?/) && object_path.include?(?/)
|
94
|
+
prefixes = []
|
95
|
+
prefix_array = File.dirname(prefix).split("/")
|
96
|
+
object_path_array = object_path.split("/")[0..-3] # skip model dir & partial
|
97
|
+
|
98
|
+
prefix_array.each_with_index do |dir, index|
|
99
|
+
break if dir == object_path_array[index]
|
100
|
+
prefixes << dir
|
101
|
+
end
|
102
|
+
|
103
|
+
(prefixes << object_path).join("/")
|
104
|
+
else
|
105
|
+
object_path
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class RenderedCollection # :nodoc:
|
111
|
+
def self.empty(format)
|
112
|
+
EmptyCollection.new format
|
113
|
+
end
|
35
114
|
|
36
|
-
|
115
|
+
attr_reader :rendered_templates
|
116
|
+
|
117
|
+
def initialize(rendered_templates, spacer)
|
118
|
+
@rendered_templates = rendered_templates
|
119
|
+
@spacer = spacer
|
120
|
+
end
|
121
|
+
|
122
|
+
def body
|
123
|
+
@rendered_templates.map(&:body).join(@spacer.body).html_safe
|
124
|
+
end
|
125
|
+
|
126
|
+
def format
|
127
|
+
rendered_templates.first.format
|
128
|
+
end
|
129
|
+
|
130
|
+
class EmptyCollection
|
131
|
+
attr_reader :format
|
132
|
+
|
133
|
+
def initialize(format)
|
134
|
+
@format = format
|
37
135
|
end
|
136
|
+
|
137
|
+
def body; nil; end
|
38
138
|
end
|
139
|
+
end
|
39
140
|
|
40
|
-
|
41
|
-
|
141
|
+
class RenderedTemplate # :nodoc:
|
142
|
+
attr_reader :body, :template
|
42
143
|
|
43
|
-
|
44
|
-
|
144
|
+
def initialize(body, template)
|
145
|
+
@body = body
|
146
|
+
@template = template
|
147
|
+
end
|
148
|
+
|
149
|
+
def format
|
150
|
+
template.format
|
151
|
+
end
|
152
|
+
|
153
|
+
EMPTY_SPACER = Struct.new(:body).new
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
NO_DETAILS = {}.freeze
|
158
|
+
|
159
|
+
def extract_details(options) # :doc:
|
160
|
+
details = nil
|
161
|
+
@lookup_context.registered_details.each do |key|
|
162
|
+
value = options[key]
|
163
|
+
|
164
|
+
if value
|
165
|
+
(details ||= {})[key] = Array(value)
|
166
|
+
end
|
45
167
|
end
|
168
|
+
details || NO_DETAILS
|
46
169
|
end
|
47
170
|
|
48
171
|
def prepend_formats(formats) # :doc:
|
@@ -51,5 +174,13 @@ module ActionView
|
|
51
174
|
|
52
175
|
@lookup_context.formats = formats | @lookup_context.formats
|
53
176
|
end
|
177
|
+
|
178
|
+
def build_rendered_template(content, template)
|
179
|
+
RenderedTemplate.new content, template
|
180
|
+
end
|
181
|
+
|
182
|
+
def build_rendered_collection(templates, spacer)
|
183
|
+
RenderedCollection.new templates, spacer
|
184
|
+
end
|
54
185
|
end
|
55
186
|
end
|