actionview 7.0.1 → 7.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +281 -202
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/app/assets/javascripts/rails-ujs.esm.js +693 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +33 -12
- data/lib/action_view/buffers.rb +106 -8
- data/lib/action_view/cache_expiry.rb +40 -43
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +1 -1
- data/lib/action_view/gem_version.rb +2 -2
- data/lib/action_view/helpers/active_model_helper.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +133 -48
- data/lib/action_view/helpers/asset_url_helper.rb +13 -12
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
- data/lib/action_view/helpers/cache_helper.rb +3 -9
- data/lib/action_view/helpers/capture_helper.rb +26 -12
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +6 -0
- data/lib/action_view/helpers/csp_helper.rb +2 -2
- data/lib/action_view/helpers/csrf_helper.rb +3 -3
- data/lib/action_view/helpers/date_helper.rb +76 -64
- data/lib/action_view/helpers/debug_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +62 -31
- data/lib/action_view/helpers/form_options_helper.rb +6 -3
- data/lib/action_view/helpers/form_tag_helper.rb +88 -44
- data/lib/action_view/helpers/javascript_helper.rb +1 -0
- data/lib/action_view/helpers/number_helper.rb +15 -13
- 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 +34 -15
- data/lib/action_view/helpers/tag_helper.rb +27 -16
- data/lib/action_view/helpers/tags/base.rb +11 -52
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
- data/lib/action_view/helpers/tags/collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/date_field.rb +1 -1
- data/lib/action_view/helpers/tags/date_select.rb +2 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
- data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/month_field.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +4 -1
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/time_field.rb +1 -1
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
- data/lib/action_view/helpers/tags/week_field.rb +1 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
- data/lib/action_view/helpers/tags.rb +2 -0
- data/lib/action_view/helpers/text_helper.rb +33 -17
- data/lib/action_view/helpers/translation_helper.rb +6 -6
- data/lib/action_view/helpers/url_helper.rb +90 -65
- data/lib/action_view/helpers.rb +2 -0
- data/lib/action_view/layouts.rb +13 -8
- data/lib/action_view/log_subscriber.rb +49 -32
- data/lib/action_view/lookup_context.rb +29 -13
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +13 -14
- data/lib/action_view/railtie.rb +26 -3
- data/lib/action_view/record_identifier.rb +16 -9
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- data/lib/action_view/renderer/collection_renderer.rb +9 -1
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
- data/lib/action_view/renderer/partial_renderer.rb +3 -2
- data/lib/action_view/renderer/renderer.rb +2 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
- data/lib/action_view/renderer/template_renderer.rb +3 -2
- data/lib/action_view/rendering.rb +24 -6
- data/lib/action_view/ripper_ast_parser.rb +6 -6
- data/lib/action_view/routing_url_for.rb +7 -4
- data/lib/action_view/template/error.rb +14 -1
- data/lib/action_view/template/handlers/builder.rb +4 -4
- data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
- data/lib/action_view/template/handlers/erb.rb +73 -1
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/html.rb +1 -1
- data/lib/action_view/template/raw_file.rb +1 -1
- data/lib/action_view/template/renderable.rb +1 -1
- data/lib/action_view/template/resolver.rb +15 -5
- data/lib/action_view/template/text.rb +1 -1
- data/lib/action_view/template/types.rb +25 -34
- data/lib/action_view/template.rb +227 -53
- data/lib/action_view/template_path.rb +2 -0
- data/lib/action_view/test_case.rb +174 -21
- data/lib/action_view/unbound_template.rb +15 -5
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +19 -28
- data/lib/action_view.rb +4 -1
- data/lib/assets/compiled/rails-ujs.js +36 -5
- metadata +27 -27
@@ -5,7 +5,7 @@ require "active_support/log_subscriber"
|
|
5
5
|
module ActionView
|
6
6
|
# = Action View Log Subscriber
|
7
7
|
#
|
8
|
-
# Provides functionality so that Rails can output logs from Action View.
|
8
|
+
# Provides functionality so that \Rails can output logs from Action View.
|
9
9
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
10
10
|
VIEWS_PATTERN = /^app\/views\//
|
11
11
|
|
@@ -21,6 +21,7 @@ module ActionView
|
|
21
21
|
message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
|
22
22
|
end
|
23
23
|
end
|
24
|
+
subscribe_log_level :render_template, :debug
|
24
25
|
|
25
26
|
def render_partial(event)
|
26
27
|
debug do
|
@@ -31,6 +32,7 @@ module ActionView
|
|
31
32
|
message
|
32
33
|
end
|
33
34
|
end
|
35
|
+
subscribe_log_level :render_partial, :debug
|
34
36
|
|
35
37
|
def render_layout(event)
|
36
38
|
info do
|
@@ -38,6 +40,7 @@ module ActionView
|
|
38
40
|
message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
|
39
41
|
end
|
40
42
|
end
|
43
|
+
subscribe_log_level :render_layout, :info
|
41
44
|
|
42
45
|
def render_collection(event)
|
43
46
|
identifier = event.payload[:identifier] || "templates"
|
@@ -49,29 +52,60 @@ module ActionView
|
|
49
52
|
message
|
50
53
|
end
|
51
54
|
end
|
55
|
+
subscribe_log_level :render_collection, :debug
|
52
56
|
|
53
|
-
|
54
|
-
|
57
|
+
module Utils # :nodoc:
|
58
|
+
def logger
|
59
|
+
ActionView::Base.logger
|
60
|
+
end
|
55
61
|
|
56
|
-
|
57
|
-
|
62
|
+
private
|
63
|
+
def from_rails_root(string)
|
64
|
+
string = string.sub(rails_root, "")
|
65
|
+
string.sub!(VIEWS_PATTERN, "")
|
66
|
+
string
|
67
|
+
end
|
58
68
|
|
59
|
-
|
60
|
-
|
69
|
+
def rails_root # :doc:
|
70
|
+
@root ||= "#{Rails.root}/"
|
71
|
+
end
|
61
72
|
end
|
62
73
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
74
|
+
include Utils
|
75
|
+
|
76
|
+
class Start # :nodoc:
|
77
|
+
include Utils
|
78
|
+
|
79
|
+
def start(name, id, payload)
|
80
|
+
return unless logger
|
81
|
+
logger.debug do
|
82
|
+
qualifier =
|
83
|
+
if name == "render_template.action_view"
|
84
|
+
""
|
85
|
+
elsif name == "render_layout.action_view"
|
86
|
+
"layout "
|
87
|
+
end
|
88
|
+
|
89
|
+
return unless qualifier
|
90
|
+
|
91
|
+
message = +" Rendering #{qualifier}#{from_rails_root(payload[:identifier])}"
|
92
|
+
message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
|
93
|
+
message
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def finish(name, id, payload)
|
98
|
+
end
|
69
99
|
end
|
70
100
|
|
71
|
-
def
|
72
|
-
|
101
|
+
def self.attach_to(*)
|
102
|
+
ActiveSupport::Notifications.subscribe("render_template.action_view", ActionView::LogSubscriber::Start.new)
|
103
|
+
ActiveSupport::Notifications.subscribe("render_layout.action_view", ActionView::LogSubscriber::Start.new)
|
104
|
+
|
105
|
+
super
|
73
106
|
end
|
74
107
|
|
108
|
+
private
|
75
109
|
def render_count(payload) # :doc:
|
76
110
|
if payload[:cache_hits]
|
77
111
|
"[#{payload[:cache_hits]} / #{payload[:count]} cache hits]"
|
@@ -88,23 +122,6 @@ module ActionView
|
|
88
122
|
"[cache miss]"
|
89
123
|
end
|
90
124
|
end
|
91
|
-
|
92
|
-
def log_rendering_start(payload, name)
|
93
|
-
debug do
|
94
|
-
qualifier =
|
95
|
-
if name == "render_template.action_view"
|
96
|
-
""
|
97
|
-
elsif name == "render_layout.action_view"
|
98
|
-
"layout "
|
99
|
-
end
|
100
|
-
|
101
|
-
return unless qualifier
|
102
|
-
|
103
|
-
message = +" Rendering #{qualifier}#{from_rails_root(payload[:identifier])}"
|
104
|
-
message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
|
105
|
-
message
|
106
|
-
end
|
107
|
-
end
|
108
125
|
end
|
109
126
|
end
|
110
127
|
|
@@ -13,7 +13,7 @@ module ActionView
|
|
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
15
|
class LookupContext # :nodoc:
|
16
|
-
attr_accessor :prefixes
|
16
|
+
attr_accessor :prefixes
|
17
17
|
|
18
18
|
singleton_class.attr_accessor :registered_details
|
19
19
|
self.registered_details = []
|
@@ -63,16 +63,20 @@ module ActionView
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def self.details_cache_key(details)
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
@details_keys.fetch(details) do
|
67
|
+
if formats = details[:formats]
|
68
|
+
unless Template::Types.valid_symbols?(formats)
|
69
|
+
details = details.dup
|
70
|
+
details[:formats] &= Template::Types.symbols
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@details_keys[details] ||= TemplateDetails::Requested.new(**details)
|
69
74
|
end
|
70
|
-
@details_keys[details] ||= TemplateDetails::Requested.new(**details)
|
71
75
|
end
|
72
76
|
|
73
77
|
def self.clear
|
74
|
-
ActionView::
|
75
|
-
|
78
|
+
ActionView::PathRegistry.all_resolvers.each do |resolver|
|
79
|
+
resolver.clear_cache
|
76
80
|
end
|
77
81
|
@view_context_class = nil
|
78
82
|
@details_keys.clear
|
@@ -83,9 +87,9 @@ module ActionView
|
|
83
87
|
@digest_cache.values
|
84
88
|
end
|
85
89
|
|
86
|
-
def self.view_context_class
|
90
|
+
def self.view_context_class
|
87
91
|
@view_context_mutex.synchronize do
|
88
|
-
@view_context_class ||=
|
92
|
+
@view_context_class ||= ActionView::Base.with_empty_template_cache
|
89
93
|
end
|
90
94
|
end
|
91
95
|
end
|
@@ -148,11 +152,23 @@ module ActionView
|
|
148
152
|
end
|
149
153
|
alias :any_templates? :any?
|
150
154
|
|
155
|
+
def append_view_paths(paths)
|
156
|
+
@view_paths = build_view_paths(@view_paths.to_a + paths)
|
157
|
+
end
|
158
|
+
|
159
|
+
def prepend_view_paths(paths)
|
160
|
+
@view_paths = build_view_paths(paths + @view_paths.to_a)
|
161
|
+
end
|
162
|
+
|
151
163
|
private
|
152
164
|
# Whenever setting view paths, makes a copy so that we can manipulate them in
|
153
165
|
# instance objects as we wish.
|
154
166
|
def build_view_paths(paths)
|
155
|
-
ActionView::PathSet
|
167
|
+
if ActionView::PathSet === paths
|
168
|
+
paths
|
169
|
+
else
|
170
|
+
ActionView::PathSet.new(Array(paths))
|
171
|
+
end
|
156
172
|
end
|
157
173
|
|
158
174
|
# Compute details hash and key according to user options (e.g. passed from #render).
|
@@ -250,12 +266,12 @@ module ActionView
|
|
250
266
|
values.concat(default_formats) if values.delete "*/*"
|
251
267
|
values.uniq!
|
252
268
|
|
253
|
-
|
254
|
-
|
269
|
+
unless Template::Types.valid_symbols?(values)
|
270
|
+
invalid_values = values - Template::Types.symbols
|
255
271
|
raise ArgumentError, "Invalid formats: #{invalid_values.map(&:inspect).join(", ")}"
|
256
272
|
end
|
257
273
|
|
258
|
-
if values == [:js
|
274
|
+
if (values.length == 1) && (values[0] == :js)
|
259
275
|
values << :html
|
260
276
|
@html_fallback_for_js = true
|
261
277
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView # :nodoc:
|
4
|
+
module PathRegistry # :nodoc:
|
5
|
+
@view_paths_by_class = {}
|
6
|
+
@file_system_resolvers = {}
|
7
|
+
@file_system_resolver_mutex = Mutex.new
|
8
|
+
@file_system_resolver_hooks = []
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_reader :file_system_resolver_hooks
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get_view_paths(klass)
|
15
|
+
@view_paths_by_class[klass] || get_view_paths(klass.superclass)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.set_view_paths(klass, paths)
|
19
|
+
@view_paths_by_class[klass] = paths
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.cast_file_system_resolvers(paths)
|
23
|
+
paths = Array(paths)
|
24
|
+
|
25
|
+
@file_system_resolver_mutex.synchronize do
|
26
|
+
built_resolver = false
|
27
|
+
paths = paths.map do |path|
|
28
|
+
case path
|
29
|
+
when String, Pathname
|
30
|
+
path = File.expand_path(path)
|
31
|
+
@file_system_resolvers[path] ||=
|
32
|
+
begin
|
33
|
+
built_resolver = true
|
34
|
+
FileSystemResolver.new(path)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
path
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
file_system_resolver_hooks.each(&:call) if built_resolver
|
42
|
+
end
|
43
|
+
|
44
|
+
paths
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.all_resolvers
|
48
|
+
resolvers = [all_file_system_resolvers]
|
49
|
+
resolvers.concat @view_paths_by_class.values.map(&:to_a)
|
50
|
+
resolvers.flatten.uniq
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.all_file_system_resolvers
|
54
|
+
@file_system_resolvers.values
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/action_view/path_set.rb
CHANGED
@@ -13,14 +13,14 @@ module ActionView # :nodoc:
|
|
13
13
|
|
14
14
|
attr_reader :paths
|
15
15
|
|
16
|
-
delegate :[], :include?, :
|
16
|
+
delegate :[], :include?, :size, :each, to: :paths
|
17
17
|
|
18
18
|
def initialize(paths = [])
|
19
|
-
@paths = typecast
|
19
|
+
@paths = typecast(paths).freeze
|
20
20
|
end
|
21
21
|
|
22
22
|
def initialize_copy(other)
|
23
|
-
@paths = other.paths.dup
|
23
|
+
@paths = other.paths.dup.freeze
|
24
24
|
self
|
25
25
|
end
|
26
26
|
|
@@ -32,18 +32,11 @@ module ActionView # :nodoc:
|
|
32
32
|
PathSet.new paths.compact
|
33
33
|
end
|
34
34
|
|
35
|
-
def +(
|
35
|
+
def +(other)
|
36
|
+
array = Array === other ? other : other.paths
|
36
37
|
PathSet.new(paths + array)
|
37
38
|
end
|
38
39
|
|
39
|
-
%w(<< concat push insert unshift).each do |method|
|
40
|
-
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
41
|
-
def #{method}(*args)
|
42
|
-
paths.#{method}(*typecast(args))
|
43
|
-
end
|
44
|
-
METHOD
|
45
|
-
end
|
46
|
-
|
47
40
|
def find(path, prefixes, partial, details, details_key, locals)
|
48
41
|
find_all(path, prefixes, partial, details, details_key, locals).first ||
|
49
42
|
raise(MissingTemplate.new(self, path, prefixes, partial, details, details_key, locals))
|
@@ -75,9 +68,15 @@ module ActionView # :nodoc:
|
|
75
68
|
paths.map do |path|
|
76
69
|
case path
|
77
70
|
when Pathname, String
|
78
|
-
|
79
|
-
|
71
|
+
# This path should only be reached by "direct" users of
|
72
|
+
# ActionView::Base (not using the ViewPaths or Renderer modules).
|
73
|
+
# We can't cache/de-dup the file system resolver in this case as we
|
74
|
+
# don't know which compiled_method_container we'll be rendering to.
|
75
|
+
FileSystemResolver.new(path)
|
76
|
+
when Resolver
|
80
77
|
path
|
78
|
+
else
|
79
|
+
raise TypeError, "#{path.inspect} is not a valid path: must be a String, Pathname, or Resolver"
|
81
80
|
end
|
82
81
|
end
|
83
82
|
end
|
data/lib/action_view/railtie.rb
CHANGED
@@ -13,6 +13,7 @@ module ActionView
|
|
13
13
|
config.action_view.image_loading = nil
|
14
14
|
config.action_view.image_decoding = nil
|
15
15
|
config.action_view.apply_stylesheet_media_default = true
|
16
|
+
config.action_view.prepend_content_exfiltration_prevention = false
|
16
17
|
|
17
18
|
config.eager_load_namespaces << ActionView
|
18
19
|
|
@@ -40,6 +41,17 @@ module ActionView
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
44
|
+
config.after_initialize do |app|
|
45
|
+
prepend_content_exfiltration_prevention = app.config.action_view.delete(:prepend_content_exfiltration_prevention)
|
46
|
+
ActionView::Helpers::ContentExfiltrationPreventionHelper.prepend_content_exfiltration_prevention = prepend_content_exfiltration_prevention
|
47
|
+
end
|
48
|
+
|
49
|
+
config.after_initialize do |app|
|
50
|
+
if klass = app.config.action_view.delete(:sanitizer_vendor)
|
51
|
+
ActionView::Helpers::SanitizeHelper.sanitizer_vendor = klass
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
43
55
|
config.after_initialize do |app|
|
44
56
|
button_to_generates_button_tag = app.config.action_view.delete(:button_to_generates_button_tag)
|
45
57
|
unless button_to_generates_button_tag.nil?
|
@@ -67,6 +79,10 @@ module ActionView
|
|
67
79
|
end
|
68
80
|
end
|
69
81
|
|
82
|
+
initializer "action_view.deprecator", before: :load_environment_config do |app|
|
83
|
+
app.deprecators[:action_view] = ActionView.deprecator
|
84
|
+
end
|
85
|
+
|
70
86
|
initializer "action_view.logger" do
|
71
87
|
ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
|
72
88
|
end
|
@@ -74,7 +90,7 @@ module ActionView
|
|
74
90
|
initializer "action_view.caching" do |app|
|
75
91
|
ActiveSupport.on_load(:action_view) do
|
76
92
|
if app.config.action_view.cache_template_loading.nil?
|
77
|
-
ActionView::Resolver.caching = app.config.
|
93
|
+
ActionView::Resolver.caching = !app.config.reloading_enabled?
|
78
94
|
end
|
79
95
|
end
|
80
96
|
end
|
@@ -91,13 +107,20 @@ module ActionView
|
|
91
107
|
|
92
108
|
config.after_initialize do |app|
|
93
109
|
enable_caching = if app.config.action_view.cache_template_loading.nil?
|
94
|
-
app.config.
|
110
|
+
!app.config.reloading_enabled?
|
95
111
|
else
|
96
112
|
app.config.action_view.cache_template_loading
|
97
113
|
end
|
98
114
|
|
99
115
|
unless enable_caching
|
100
|
-
|
116
|
+
view_reloader = ActionView::CacheExpiry::ViewReloader.new(watcher: app.config.file_watcher)
|
117
|
+
|
118
|
+
app.reloaders << view_reloader
|
119
|
+
view_reloader.execute
|
120
|
+
app.reloader.to_run do
|
121
|
+
require_unload_lock!
|
122
|
+
view_reloader.execute
|
123
|
+
end
|
101
124
|
end
|
102
125
|
end
|
103
126
|
|
@@ -4,6 +4,8 @@ require "active_support/core_ext/module"
|
|
4
4
|
require "action_view/model_naming"
|
5
5
|
|
6
6
|
module ActionView
|
7
|
+
# = Action View \Record \Identifier
|
8
|
+
#
|
7
9
|
# RecordIdentifier encapsulates methods used by various ActionView helpers
|
8
10
|
# to associate records with DOM elements.
|
9
11
|
#
|
@@ -31,6 +33,8 @@ module ActionView
|
|
31
33
|
# automatically generated, following naming conventions encapsulated by the
|
32
34
|
# RecordIdentifier methods #dom_id and #dom_class:
|
33
35
|
#
|
36
|
+
# dom_id(Post) # => "new_post"
|
37
|
+
# dom_class(Post) # => "post"
|
34
38
|
# dom_id(Post.new) # => "new_post"
|
35
39
|
# dom_class(Post.new) # => "post"
|
36
40
|
# dom_id(Post.find 42) # => "post_42"
|
@@ -79,18 +83,21 @@ module ActionView
|
|
79
83
|
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
|
80
84
|
# If no id is found, prefix with "new_" instead.
|
81
85
|
#
|
82
|
-
# dom_id(Post.find(45))
|
83
|
-
# dom_id(Post
|
86
|
+
# dom_id(Post.find(45)) # => "post_45"
|
87
|
+
# dom_id(Post) # => "new_post"
|
84
88
|
#
|
85
89
|
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
|
86
90
|
#
|
87
91
|
# dom_id(Post.find(45), :edit) # => "edit_post_45"
|
88
|
-
# dom_id(Post
|
89
|
-
def dom_id(
|
90
|
-
|
91
|
-
|
92
|
+
# dom_id(Post, :custom) # => "custom_post"
|
93
|
+
def dom_id(record_or_class, prefix = nil)
|
94
|
+
raise ArgumentError, "dom_id must be passed a record_or_class as the first argument, you passed #{record_or_class.inspect}" unless record_or_class
|
95
|
+
|
96
|
+
record_id = record_key_for_dom_id(record_or_class) unless record_or_class.is_a?(Class)
|
97
|
+
if record_id
|
98
|
+
"#{dom_class(record_or_class, prefix)}#{JOIN}#{record_id}"
|
92
99
|
else
|
93
|
-
dom_class(
|
100
|
+
dom_class(record_or_class, prefix || NEW)
|
94
101
|
end
|
95
102
|
end
|
96
103
|
|
@@ -102,10 +109,10 @@ module ActionView
|
|
102
109
|
# on the default implementation (which just joins all key attributes with '_') or on your own
|
103
110
|
# overwritten version of the method. By default, this implementation passes the key string through a
|
104
111
|
# 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
|
112
|
+
# make sure yourself that your dom ids are valid, in case you override this method.
|
106
113
|
def record_key_for_dom_id(record) # :doc:
|
107
114
|
key = convert_to_model(record).to_key
|
108
|
-
key ? key.join(JOIN) :
|
115
|
+
key && key.all? ? key.join(JOIN) : nil
|
109
116
|
end
|
110
117
|
end
|
111
118
|
end
|
@@ -51,6 +51,10 @@ module ActionView
|
|
51
51
|
def length
|
52
52
|
@collection.respond_to?(:length) ? @collection.length : size
|
53
53
|
end
|
54
|
+
|
55
|
+
def preload!
|
56
|
+
# no-op
|
57
|
+
end
|
54
58
|
end
|
55
59
|
|
56
60
|
class SameCollectionIterator < CollectionIterator # :nodoc:
|
@@ -84,9 +88,13 @@ module ActionView
|
|
84
88
|
|
85
89
|
def each_with_info
|
86
90
|
return super unless block_given?
|
87
|
-
|
91
|
+
preload!
|
88
92
|
super
|
89
93
|
end
|
94
|
+
|
95
|
+
def preload!
|
96
|
+
@relation.preload_associations(@collection)
|
97
|
+
end
|
90
98
|
end
|
91
99
|
|
92
100
|
class MixedCollectionIterator < CollectionIterator # :nodoc:
|
@@ -59,6 +59,7 @@ module ActionView
|
|
59
59
|
seed = callable_cache_key? ? @options[:cached] : ->(i) { i }
|
60
60
|
|
61
61
|
digest_path = view.digest_path_from_template(template)
|
62
|
+
collection.preload! if callable_cache_key?
|
62
63
|
|
63
64
|
collection.each_with_object([{}, []]) do |item, (hash, ordered_keys)|
|
64
65
|
key = expanded_cache_key(seed.call(item), view, template, digest_path)
|
@@ -88,15 +89,32 @@ module ActionView
|
|
88
89
|
# If the partial is not already cached it will also be
|
89
90
|
# written back to the underlying cache store.
|
90
91
|
def fetch_or_cache_partial(cached_partials, template, order_by:)
|
91
|
-
|
92
|
+
entries_to_write = {}
|
93
|
+
|
94
|
+
keyed_partials = order_by.index_with do |cache_key|
|
92
95
|
if content = cached_partials[cache_key]
|
93
96
|
build_rendered_template(content, template)
|
94
97
|
else
|
95
|
-
|
96
|
-
|
98
|
+
rendered_partial = yield
|
99
|
+
body = rendered_partial.body
|
100
|
+
|
101
|
+
# We want to cache buffers as raw strings. This both improve performance and
|
102
|
+
# avoid creating forward compatibility issues with the internal representation
|
103
|
+
# of these two types.
|
104
|
+
if body.is_a?(ActionView::OutputBuffer) || body.is_a?(ActiveSupport::SafeBuffer)
|
105
|
+
body = body.to_str
|
97
106
|
end
|
107
|
+
|
108
|
+
entries_to_write[cache_key] = body
|
109
|
+
rendered_partial
|
98
110
|
end
|
99
111
|
end
|
112
|
+
|
113
|
+
unless entries_to_write.empty?
|
114
|
+
collection_cache.write_multi(entries_to_write)
|
115
|
+
end
|
116
|
+
|
117
|
+
keyed_partials
|
100
118
|
end
|
101
119
|
end
|
102
120
|
end
|
@@ -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:
|
@@ -246,7 +246,8 @@ module ActionView
|
|
246
246
|
ActiveSupport::Notifications.instrument(
|
247
247
|
"render_partial.action_view",
|
248
248
|
identifier: template.identifier,
|
249
|
-
layout: layout && layout.virtual_path
|
249
|
+
layout: layout && layout.virtual_path,
|
250
|
+
locals: locals
|
250
251
|
) do |payload|
|
251
252
|
content = template.render(view, locals, add_to_stack: !block) do |*name|
|
252
253
|
view._layout_for(*name, &block)
|
@@ -46,7 +46,7 @@ module ActionView
|
|
46
46
|
return [super.body] unless layout_name && template.supports_streaming?
|
47
47
|
|
48
48
|
locals ||= {}
|
49
|
-
layout =
|
49
|
+
layout = find_layout(layout_name, locals.keys, [formats.first])
|
50
50
|
|
51
51
|
Body.new do |buffer|
|
52
52
|
delayed_render(buffer, template, layout, view, locals)
|
@@ -65,7 +65,8 @@ module ActionView
|
|
65
65
|
ActiveSupport::Notifications.instrument(
|
66
66
|
"render_template.action_view",
|
67
67
|
identifier: template.identifier,
|
68
|
-
layout: layout && layout.virtual_path
|
68
|
+
layout: layout && layout.virtual_path,
|
69
|
+
locals: locals
|
69
70
|
) do
|
70
71
|
outer_config = I18n.config
|
71
72
|
fiber = Fiber.new do
|
@@ -49,7 +49,7 @@ module ActionView
|
|
49
49
|
@lookup_context.find_template(options[:template], options[:prefixes], false, keys, @details)
|
50
50
|
end
|
51
51
|
else
|
52
|
-
raise ArgumentError, "You invoked render but did not give any of :
|
52
|
+
raise ArgumentError, "You invoked render but did not give any of :body, :file, :html, :inline, :partial, :plain, :renderable, or :template option."
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
@@ -60,7 +60,8 @@ module ActionView
|
|
60
60
|
ActiveSupport::Notifications.instrument(
|
61
61
|
"render_template.action_view",
|
62
62
|
identifier: template.identifier,
|
63
|
-
layout: layout && layout.virtual_path
|
63
|
+
layout: layout && layout.virtual_path,
|
64
|
+
locals: locals
|
64
65
|
) do
|
65
66
|
template.render(view, locals) { |*name| view._layout_for(*name) }
|
66
67
|
end
|
@@ -10,7 +10,8 @@ module ActionView
|
|
10
10
|
|
11
11
|
def initialize(original_config, lookup_context)
|
12
12
|
original_config = original_config.original_config if original_config.respond_to?(:original_config)
|
13
|
-
@original_config
|
13
|
+
@original_config = original_config
|
14
|
+
@lookup_context = lookup_context
|
14
15
|
end
|
15
16
|
|
16
17
|
def locale
|
@@ -33,8 +34,8 @@ module ActionView
|
|
33
34
|
super
|
34
35
|
end
|
35
36
|
|
36
|
-
#
|
37
|
-
def process(
|
37
|
+
# Override process to set up I18n proxy.
|
38
|
+
def process(...) # :nodoc:
|
38
39
|
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
|
39
40
|
super
|
40
41
|
ensure
|
@@ -48,7 +49,18 @@ module ActionView
|
|
48
49
|
def _helpers
|
49
50
|
end
|
50
51
|
|
52
|
+
def inherit_view_context_class?
|
53
|
+
superclass.respond_to?(:view_context_class) &&
|
54
|
+
supports_path? == superclass.supports_path? &&
|
55
|
+
_routes.equal?(superclass._routes) &&
|
56
|
+
_helpers.equal?(superclass._helpers)
|
57
|
+
end
|
58
|
+
|
51
59
|
def build_view_context_class(klass, supports_path, routes, helpers)
|
60
|
+
if inherit_view_context_class?
|
61
|
+
return superclass.view_context_class
|
62
|
+
end
|
63
|
+
|
52
64
|
Class.new(klass) do
|
53
65
|
if routes
|
54
66
|
include routes.url_helpers(supports_path)
|
@@ -61,8 +73,14 @@ module ActionView
|
|
61
73
|
end
|
62
74
|
end
|
63
75
|
|
76
|
+
def eager_load!
|
77
|
+
super
|
78
|
+
view_context_class
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
|
64
82
|
def view_context_class
|
65
|
-
klass = ActionView::LookupContext::DetailsKey.view_context_class
|
83
|
+
klass = ActionView::LookupContext::DetailsKey.view_context_class
|
66
84
|
|
67
85
|
@view_context_class ||= build_view_context_class(klass, supports_path?, _routes, _helpers)
|
68
86
|
|
@@ -129,8 +147,8 @@ module ActionView
|
|
129
147
|
lookup_context.formats = [format.to_sym] if format.to_sym
|
130
148
|
end
|
131
149
|
|
132
|
-
# Normalize args by converting render "foo" to render :
|
133
|
-
# render "foo/bar" to render :
|
150
|
+
# Normalize args by converting render "foo" to render action: "foo" and
|
151
|
+
# render "foo/bar" to render template: "foo/bar".
|
134
152
|
def _normalize_args(action = nil, options = {})
|
135
153
|
options = super(action, options)
|
136
154
|
case action
|