view_component 2.52.0 → 2.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of view_component might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/controllers/concerns/view_component/preview_actions.rb +101 -0
- data/app/controllers/view_components_controller.rb +1 -87
- data/app/helpers/preview_helper.rb +3 -3
- data/docs/CHANGELOG.md +285 -1
- data/lib/rails/generators/abstract_generator.rb +4 -4
- data/lib/view_component/base.rb +95 -33
- data/lib/view_component/collection.rb +9 -2
- data/lib/view_component/compile_cache.rb +1 -2
- data/lib/view_component/compiler.rb +31 -18
- data/lib/view_component/content_areas.rb +1 -1
- data/lib/view_component/docs_builder_component.rb +1 -1
- data/lib/view_component/engine.rb +6 -21
- data/lib/view_component/polymorphic_slots.rb +22 -1
- data/lib/view_component/preview.rb +12 -9
- data/lib/view_component/render_component_to_string_helper.rb +1 -1
- data/lib/view_component/render_preview_helper.rb +50 -0
- data/lib/view_component/render_to_string_monkey_patch.rb +1 -1
- data/lib/view_component/rendering_component_helper.rb +1 -1
- data/lib/view_component/rendering_monkey_patch.rb +1 -1
- data/lib/view_component/slotable.rb +5 -6
- data/lib/view_component/slotable_v2.rb +35 -17
- data/lib/view_component/test_helpers.rb +39 -7
- data/lib/view_component/translatable.rb +13 -14
- data/lib/view_component/version.rb +1 -1
- data/lib/view_component.rb +0 -2
- metadata +7 -7
- data/lib/view_component/global_output_buffer.rb +0 -99
- data/lib/view_component/output_buffer_stack.rb +0 -67
@@ -4,7 +4,7 @@ module ViewComponent
|
|
4
4
|
module RenderingMonkeyPatch # :nodoc:
|
5
5
|
def render(options = {}, args = {})
|
6
6
|
if options.respond_to?(:render_in)
|
7
|
-
self.response_body = options.render_in(
|
7
|
+
self.response_body = options.render_in(view_context)
|
8
8
|
else
|
9
9
|
super
|
10
10
|
end
|
@@ -30,7 +30,7 @@ module ViewComponent
|
|
30
30
|
|
31
31
|
slot_names.each do |slot_name|
|
32
32
|
# Ensure slot_name isn't already declared
|
33
|
-
if
|
33
|
+
if slots.key?(slot_name)
|
34
34
|
raise ArgumentError.new("#{slot_name} slot declared multiple times")
|
35
35
|
end
|
36
36
|
|
@@ -73,7 +73,7 @@ module ViewComponent
|
|
73
73
|
class_name = "ViewComponent::Slot" unless class_name.present?
|
74
74
|
|
75
75
|
# Register the slot on the component
|
76
|
-
|
76
|
+
slots[slot_name] = {
|
77
77
|
class_name: class_name,
|
78
78
|
instance_variable_name: instance_variable_name,
|
79
79
|
collection: collection
|
@@ -84,7 +84,7 @@ module ViewComponent
|
|
84
84
|
def inherited(child)
|
85
85
|
# Clone slot configuration into child class
|
86
86
|
# see #test_slots_pollution
|
87
|
-
child.slots =
|
87
|
+
child.slots = slots.clone
|
88
88
|
|
89
89
|
super
|
90
90
|
end
|
@@ -106,7 +106,7 @@ module ViewComponent
|
|
106
106
|
#
|
107
107
|
def slot(slot_name, **args, &block)
|
108
108
|
# Raise ArgumentError if `slot` doesn't exist
|
109
|
-
unless slots.
|
109
|
+
unless slots.key?(slot_name)
|
110
110
|
raise ArgumentError.new "Unknown slot '#{slot_name}' - expected one of '#{slots.keys}'"
|
111
111
|
end
|
112
112
|
|
@@ -123,8 +123,7 @@ module ViewComponent
|
|
123
123
|
slot_instance = args.present? ? slot_class.new(**args) : slot_class.new
|
124
124
|
|
125
125
|
# Capture block and assign to slot_instance#content
|
126
|
-
|
127
|
-
slot_instance.content = view_context.capture(&block).to_s.strip.html_safe if block_given?
|
126
|
+
slot_instance.content = view_context.capture(&block).to_s.strip.html_safe if block
|
128
127
|
|
129
128
|
if slot[:collection]
|
130
129
|
# Initialize instance variable as an empty array
|
@@ -9,7 +9,7 @@ module ViewComponent
|
|
9
9
|
|
10
10
|
RESERVED_NAMES = {
|
11
11
|
singular: %i[content render].freeze,
|
12
|
-
plural: %i[contents renders].freeze
|
12
|
+
plural: %i[contents renders].freeze
|
13
13
|
}.freeze
|
14
14
|
|
15
15
|
# Setup component slot state
|
@@ -61,20 +61,26 @@ module ViewComponent
|
|
61
61
|
# = Setting sub-component content
|
62
62
|
#
|
63
63
|
# Consumers of the component can render a sub-component by calling a
|
64
|
-
# helper method with the same name as the slot
|
64
|
+
# helper method with the same name as the slot prefixed with `with_`.
|
65
65
|
#
|
66
66
|
# <%= render_inline(MyComponent.new) do |component| %>
|
67
|
-
# <% component.
|
67
|
+
# <% component.with_header(classes: "Foo") do %>
|
68
68
|
# <p>Bar</p>
|
69
69
|
# <% end %>
|
70
70
|
# <% end %>
|
71
71
|
def renders_one(slot_name, callable = nil)
|
72
72
|
validate_singular_slot_name(slot_name)
|
73
73
|
|
74
|
+
define_method :"with_#{slot_name}" do |*args, &block|
|
75
|
+
set_slot(slot_name, nil, *args, &block)
|
76
|
+
end
|
77
|
+
ruby2_keywords(:"with_#{slot_name}") if respond_to?(:ruby2_keywords, true)
|
78
|
+
|
74
79
|
define_method slot_name do |*args, &block|
|
75
80
|
if args.empty? && block.nil?
|
76
81
|
get_slot(slot_name)
|
77
82
|
else
|
83
|
+
# Deprecated: Will remove in 3.0
|
78
84
|
set_slot(slot_name, nil, *args, &block)
|
79
85
|
end
|
80
86
|
end
|
@@ -112,15 +118,15 @@ module ViewComponent
|
|
112
118
|
# = Setting sub-component content
|
113
119
|
#
|
114
120
|
# Consumers of the component can set the content of a slot by calling a
|
115
|
-
# helper method with the same name as the slot
|
116
|
-
# called multiple times to append to the slot.
|
121
|
+
# helper method with the same name as the slot prefixed with `with_`. The
|
122
|
+
# method can be called multiple times to append to the slot.
|
117
123
|
#
|
118
124
|
# <%= render_inline(MyComponent.new) do |component| %>
|
119
|
-
# <% component.
|
125
|
+
# <% component.with_item(name: "Foo") do %>
|
120
126
|
# <p>One</p>
|
121
127
|
# <% end %>
|
122
128
|
#
|
123
|
-
# <% component.
|
129
|
+
# <% component.with_item(name: "Bar") do %>
|
124
130
|
# <p>two</p>
|
125
131
|
# <% end %>
|
126
132
|
# <% end %>
|
@@ -132,17 +138,31 @@ module ViewComponent
|
|
132
138
|
# Define setter for singular names
|
133
139
|
# for example `renders_many :items` allows fetching all tabs with
|
134
140
|
# `component.tabs` and setting a tab with `component.tab`
|
141
|
+
#
|
142
|
+
# Deprecated: Will remove in 3.0
|
135
143
|
define_method singular_name do |*args, &block|
|
136
144
|
set_slot(slot_name, nil, *args, &block)
|
137
145
|
end
|
138
146
|
ruby2_keywords(singular_name.to_sym) if respond_to?(:ruby2_keywords, true)
|
139
147
|
|
148
|
+
define_method :"with_#{singular_name}" do |*args, &block|
|
149
|
+
set_slot(slot_name, nil, *args, &block)
|
150
|
+
end
|
151
|
+
ruby2_keywords(:"with_#{singular_name}") if respond_to?(:ruby2_keywords, true)
|
152
|
+
|
153
|
+
define_method :"with_#{slot_name}" do |collection_args = nil, &block|
|
154
|
+
collection_args.map do |args|
|
155
|
+
set_slot(slot_name, nil, **args, &block)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
140
159
|
# Instantiates and and adds multiple slots forwarding the first
|
141
160
|
# argument to each slot constructor
|
142
161
|
define_method slot_name do |collection_args = nil, &block|
|
143
162
|
if collection_args.nil? && block.nil?
|
144
163
|
get_slot(slot_name)
|
145
164
|
else
|
165
|
+
# Deprecated: Will remove in 3.0
|
146
166
|
collection_args.map do |args|
|
147
167
|
set_slot(slot_name, nil, **args, &block)
|
148
168
|
end
|
@@ -170,20 +190,20 @@ module ViewComponent
|
|
170
190
|
# Clone slot configuration into child class
|
171
191
|
# see #test_slots_pollution
|
172
192
|
def inherited(child)
|
173
|
-
child.registered_slots =
|
193
|
+
child.registered_slots = registered_slots.clone
|
174
194
|
super
|
175
195
|
end
|
176
196
|
|
177
197
|
private
|
178
198
|
|
179
199
|
def register_slot(slot_name, **kwargs)
|
180
|
-
|
200
|
+
registered_slots[slot_name] = define_slot(slot_name, **kwargs)
|
181
201
|
end
|
182
202
|
|
183
203
|
def define_slot(slot_name, collection:, callable:)
|
184
204
|
# Setup basic slot data
|
185
205
|
slot = {
|
186
|
-
collection: collection
|
206
|
+
collection: collection
|
187
207
|
}
|
188
208
|
return slot unless callable
|
189
209
|
|
@@ -234,7 +254,7 @@ module ViewComponent
|
|
234
254
|
end
|
235
255
|
|
236
256
|
def raise_if_slot_registered(slot_name)
|
237
|
-
if
|
257
|
+
if registered_slots.key?(slot_name)
|
238
258
|
# TODO remove? This breaks overriding slots when slots are inherited
|
239
259
|
raise ArgumentError.new(
|
240
260
|
"#{self} declares the #{slot_name} slot multiple times.\n\n" \
|
@@ -246,8 +266,8 @@ module ViewComponent
|
|
246
266
|
def raise_if_slot_ends_with_question_mark(slot_name)
|
247
267
|
if slot_name.to_s.ends_with?("?")
|
248
268
|
raise ArgumentError.new(
|
249
|
-
"#{self} declares a slot named #{slot_name}, which ends with a question mark.\n\n"\
|
250
|
-
"This is not allowed because the ViewComponent framework already provides predicate "\
|
269
|
+
"#{self} declares a slot named #{slot_name}, which ends with a question mark.\n\n" \
|
270
|
+
"This is not allowed because the ViewComponent framework already provides predicate " \
|
251
271
|
"methods ending in `?`.\n\n" \
|
252
272
|
"To fix this issue, choose a different name."
|
253
273
|
)
|
@@ -267,8 +287,6 @@ module ViewComponent
|
|
267
287
|
|
268
288
|
if slot[:collection]
|
269
289
|
[]
|
270
|
-
else
|
271
|
-
nil
|
272
290
|
end
|
273
291
|
end
|
274
292
|
|
@@ -285,7 +303,7 @@ module ViewComponent
|
|
285
303
|
# 2. Since we've to pass block content to components when calling
|
286
304
|
# `render`, evaluating the block here would require us to call
|
287
305
|
# `view_context.capture` twice, which is slower
|
288
|
-
slot.__vc_content_block = block if
|
306
|
+
slot.__vc_content_block = block if block
|
289
307
|
|
290
308
|
# If class
|
291
309
|
if slot_definition[:renderable]
|
@@ -301,7 +319,7 @@ module ViewComponent
|
|
301
319
|
# methods like `content_tag` as well as parent component state.
|
302
320
|
renderable_function = slot_definition[:renderable_function].bind(self)
|
303
321
|
renderable_value =
|
304
|
-
if
|
322
|
+
if block
|
305
323
|
renderable_function.call(*args) do |*rargs|
|
306
324
|
view_context.capture(*rargs, &block)
|
307
325
|
end
|
@@ -1,13 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "view_component/render_preview_helper"
|
4
|
+
|
3
5
|
module ViewComponent
|
4
6
|
module TestHelpers
|
5
7
|
begin
|
6
8
|
require "capybara/minitest"
|
9
|
+
|
7
10
|
include Capybara::Minitest::Assertions
|
8
11
|
|
9
12
|
def page
|
10
|
-
Capybara::Node::Simple.new(
|
13
|
+
@page ||= Capybara::Node::Simple.new(rendered_content)
|
11
14
|
end
|
12
15
|
|
13
16
|
def refute_component_rendered
|
@@ -19,8 +22,8 @@ module ViewComponent
|
|
19
22
|
# :nocov:
|
20
23
|
if ENV["DEBUG"]
|
21
24
|
warn(
|
22
|
-
"WARNING in `ViewComponent::TestHelpers`:
|
23
|
-
"to
|
25
|
+
"WARNING in `ViewComponent::TestHelpers`: Add `capybara` " \
|
26
|
+
"to Gemfile to use Capybara assertions."
|
24
27
|
)
|
25
28
|
end
|
26
29
|
|
@@ -28,7 +31,19 @@ module ViewComponent
|
|
28
31
|
end
|
29
32
|
|
30
33
|
# @private
|
31
|
-
attr_reader :
|
34
|
+
attr_reader :rendered_content
|
35
|
+
|
36
|
+
# Returns the result of a render_inline call.
|
37
|
+
#
|
38
|
+
# @return [String]
|
39
|
+
def rendered_component
|
40
|
+
ViewComponent::Deprecation.warn(
|
41
|
+
"`rendered_component` is deprecated and will be removed in v3.0.0. " \
|
42
|
+
"Use `page` instead."
|
43
|
+
)
|
44
|
+
|
45
|
+
rendered_content
|
46
|
+
end
|
32
47
|
|
33
48
|
# Render a component inline. Internally sets `page` to be a `Capybara::Node::Simple`,
|
34
49
|
# allowing for Capybara assertions to be used:
|
@@ -41,14 +56,31 @@ module ViewComponent
|
|
41
56
|
# @param component [ViewComponent::Base, ViewComponent::Collection] The instance of the component to be rendered.
|
42
57
|
# @return [Nokogiri::HTML]
|
43
58
|
def render_inline(component, **args, &block)
|
44
|
-
@
|
59
|
+
@page = nil
|
60
|
+
@rendered_content =
|
45
61
|
if Rails.version.to_f >= 6.1
|
46
62
|
controller.view_context.render(component, args, &block)
|
47
63
|
else
|
48
64
|
controller.view_context.render_component(component, &block)
|
49
65
|
end
|
50
66
|
|
51
|
-
Nokogiri::HTML.fragment(@
|
67
|
+
Nokogiri::HTML.fragment(@rendered_content)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Execute the given block in the view context. Internally sets `page` to be a
|
71
|
+
# `Capybara::Node::Simple`, allowing for Capybara assertions to be used:
|
72
|
+
#
|
73
|
+
# ```ruby
|
74
|
+
# render_in_view_context do
|
75
|
+
# render(MyComponent.new)
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# assert_text("Hello, World!")
|
79
|
+
# ```
|
80
|
+
def render_in_view_context(&block)
|
81
|
+
@page = nil
|
82
|
+
@rendered_content = controller.view_context.instance_exec(&block)
|
83
|
+
Nokogiri::HTML.fragment(@rendered_content)
|
52
84
|
end
|
53
85
|
|
54
86
|
# @private
|
@@ -121,7 +153,7 @@ module ViewComponent
|
|
121
153
|
|
122
154
|
request.path_info = path
|
123
155
|
request.path_parameters = Rails.application.routes.recognize_path(path)
|
124
|
-
request.set_header("action_dispatch.request.query_parameters", Rack::Utils.
|
156
|
+
request.set_header("action_dispatch.request.query_parameters", Rack::Utils.parse_nested_query(path.split("?")[1]))
|
125
157
|
request.set_header(Rack::QUERY_STRING, path.split("?")[1])
|
126
158
|
yield
|
127
159
|
ensure
|
@@ -3,14 +3,13 @@
|
|
3
3
|
require "erb"
|
4
4
|
require "set"
|
5
5
|
require "i18n"
|
6
|
-
require "action_view/helpers/translation_helper"
|
7
6
|
require "active_support/concern"
|
8
7
|
|
9
8
|
module ViewComponent
|
10
9
|
module Translatable
|
11
10
|
extend ActiveSupport::Concern
|
12
11
|
|
13
|
-
HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z
|
12
|
+
HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z/
|
14
13
|
|
15
14
|
included do
|
16
15
|
class_attribute :i18n_backend, instance_writer: false, instance_predicate: false
|
@@ -21,19 +20,16 @@ module ViewComponent
|
|
21
20
|
@i18n_scope ||= virtual_path.sub(%r{^/}, "").gsub(%r{/_?}, ".")
|
22
21
|
end
|
23
22
|
|
24
|
-
def
|
25
|
-
super
|
26
|
-
|
23
|
+
def build_i18n_backend
|
27
24
|
return if CompileCache.compiled? self
|
28
25
|
|
29
|
-
if (translation_files = _sidecar_files(%w[yml yaml])).any?
|
30
|
-
|
26
|
+
self.i18n_backend = if (translation_files = _sidecar_files(%w[yml yaml])).any?
|
27
|
+
# Returning nil cleans up if translations file has been removed since the last compilation
|
28
|
+
|
29
|
+
I18nBackend.new(
|
31
30
|
i18n_scope: i18n_scope,
|
32
|
-
load_paths: translation_files
|
31
|
+
load_paths: translation_files
|
33
32
|
)
|
34
|
-
else
|
35
|
-
# Cleanup if translations file has been removed since the last compilation
|
36
|
-
self.i18n_backend = nil
|
37
33
|
end
|
38
34
|
end
|
39
35
|
end
|
@@ -53,7 +49,7 @@ module ViewComponent
|
|
53
49
|
|
54
50
|
def scope_data(data)
|
55
51
|
@i18n_scope.reverse_each do |part|
|
56
|
-
data = {
|
52
|
+
data = {part => data}
|
57
53
|
end
|
58
54
|
data
|
59
55
|
end
|
@@ -68,7 +64,10 @@ module ViewComponent
|
|
68
64
|
return key.map { |k| translate(k, **options) } if key.is_a?(Array)
|
69
65
|
|
70
66
|
locale = options.delete(:locale) || ::I18n.locale
|
67
|
+
scope = options.delete(:scope)
|
68
|
+
scope = scope.join(".") if scope.is_a? Array
|
71
69
|
key = key&.to_s unless key.is_a?(String)
|
70
|
+
key = "#{scope}.#{key}" if scope
|
72
71
|
key = "#{i18n_scope}#{key}" if key.start_with?(".")
|
73
72
|
|
74
73
|
if HTML_SAFE_TRANSLATION_KEY.match?(key)
|
@@ -95,7 +94,7 @@ module ViewComponent
|
|
95
94
|
super(key, locale: locale, **options)
|
96
95
|
end
|
97
96
|
end
|
98
|
-
|
97
|
+
alias_method :t, :translate
|
99
98
|
|
100
99
|
# Exposes .i18n_scope as an instance method
|
101
100
|
def i18n_scope
|
@@ -109,7 +108,7 @@ module ViewComponent
|
|
109
108
|
# It's assumed here that objects loaded by the i18n backend will respond to `#html_safe?`.
|
110
109
|
# It's reasonable that if we're in Rails, `active_support/core_ext/string/output_safety.rb`
|
111
110
|
# will provide this to `Object`.
|
112
|
-
translation.html_safe
|
111
|
+
translation.html_safe
|
113
112
|
end
|
114
113
|
end
|
115
114
|
|
data/lib/view_component.rb
CHANGED
@@ -11,9 +11,7 @@ module ViewComponent
|
|
11
11
|
autoload :CompileCache
|
12
12
|
autoload :ComponentError
|
13
13
|
autoload :Deprecation
|
14
|
-
autoload :GlobalOutputBuffer
|
15
14
|
autoload :Instrumentation
|
16
|
-
autoload :OutputBufferStack
|
17
15
|
autoload :Preview
|
18
16
|
autoload :PreviewTemplateError
|
19
17
|
autoload :TestHelpers
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.62.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -185,19 +185,19 @@ dependencies:
|
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: '13.0'
|
187
187
|
- !ruby/object:Gem::Dependency
|
188
|
-
name:
|
188
|
+
name: standard
|
189
189
|
requirement: !ruby/object:Gem::Requirement
|
190
190
|
requirements:
|
191
191
|
- - "~>"
|
192
192
|
- !ruby/object:Gem::Version
|
193
|
-
version:
|
193
|
+
version: '1'
|
194
194
|
type: :development
|
195
195
|
prerelease: false
|
196
196
|
version_requirements: !ruby/object:Gem::Requirement
|
197
197
|
requirements:
|
198
198
|
- - "~>"
|
199
199
|
- !ruby/object:Gem::Version
|
200
|
-
version:
|
200
|
+
version: '1'
|
201
201
|
- !ruby/object:Gem::Dependency
|
202
202
|
name: simplecov
|
203
203
|
requirement: !ruby/object:Gem::Requirement
|
@@ -293,6 +293,7 @@ files:
|
|
293
293
|
- README.md
|
294
294
|
- app/assets/vendor/prism.css
|
295
295
|
- app/assets/vendor/prism.min.js
|
296
|
+
- app/controllers/concerns/view_component/preview_actions.rb
|
296
297
|
- app/controllers/view_components_controller.rb
|
297
298
|
- app/helpers/preview_helper.rb
|
298
299
|
- app/views/test_mailer/test_email.html.erb
|
@@ -333,9 +334,7 @@ files:
|
|
333
334
|
- lib/view_component/docs_builder_component.html.erb
|
334
335
|
- lib/view_component/docs_builder_component.rb
|
335
336
|
- lib/view_component/engine.rb
|
336
|
-
- lib/view_component/global_output_buffer.rb
|
337
337
|
- lib/view_component/instrumentation.rb
|
338
|
-
- lib/view_component/output_buffer_stack.rb
|
339
338
|
- lib/view_component/polymorphic_slots.rb
|
340
339
|
- lib/view_component/preview.rb
|
341
340
|
- lib/view_component/preview_template_error.rb
|
@@ -344,6 +343,7 @@ files:
|
|
344
343
|
- lib/view_component/render_component_helper.rb
|
345
344
|
- lib/view_component/render_component_to_string_helper.rb
|
346
345
|
- lib/view_component/render_monkey_patch.rb
|
346
|
+
- lib/view_component/render_preview_helper.rb
|
347
347
|
- lib/view_component/render_to_string_monkey_patch.rb
|
348
348
|
- lib/view_component/rendering_component_helper.rb
|
349
349
|
- lib/view_component/rendering_monkey_patch.rb
|
@@ -1,99 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ViewComponent
|
4
|
-
module GlobalOutputBuffer
|
5
|
-
def render_in(view_context, &block)
|
6
|
-
unless view_context.output_buffer.is_a?(OutputBufferStack)
|
7
|
-
# use instance_variable_set here to avoid triggering the code in the #output_buffer= method below
|
8
|
-
view_context.instance_variable_set(:@output_buffer, OutputBufferStack.new(view_context.output_buffer))
|
9
|
-
end
|
10
|
-
|
11
|
-
@output_buffer = view_context.output_buffer
|
12
|
-
@global_buffer_in_use = true
|
13
|
-
|
14
|
-
super(view_context, &block)
|
15
|
-
end
|
16
|
-
|
17
|
-
def perform_render
|
18
|
-
# HAML unhelpfully assigns to @output_buffer directly, so we hold onto a reference to
|
19
|
-
# it and restore @output_buffer when the HAML engine is finished. In non-HAML cases,
|
20
|
-
# @output_buffer and orig_buf will point to the same object, making the reassignment
|
21
|
-
# statements no-ops.
|
22
|
-
orig_buf = @output_buffer
|
23
|
-
@output_buffer.push
|
24
|
-
result = render_template_for(@__vc_variant).to_s + _output_postamble
|
25
|
-
@output_buffer = orig_buf
|
26
|
-
@output_buffer.pop
|
27
|
-
result
|
28
|
-
end
|
29
|
-
|
30
|
-
def output_buffer=(other_buffer)
|
31
|
-
@output_buffer.replace(other_buffer)
|
32
|
-
end
|
33
|
-
|
34
|
-
def with_output_buffer(buf = nil)
|
35
|
-
unless buf
|
36
|
-
buf = ActionView::OutputBuffer.new
|
37
|
-
if output_buffer && output_buffer.respond_to?(:encoding)
|
38
|
-
buf.force_encoding(output_buffer.encoding)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
output_buffer.push(buf)
|
43
|
-
result = nil
|
44
|
-
|
45
|
-
begin
|
46
|
-
yield
|
47
|
-
ensure
|
48
|
-
# assign result here to avoid a return statement, which will
|
49
|
-
# immediately return to the caller and swallow any errors
|
50
|
-
result = output_buffer.pop
|
51
|
-
end
|
52
|
-
|
53
|
-
result
|
54
|
-
end
|
55
|
-
|
56
|
-
module ActionViewMods
|
57
|
-
def output_buffer=(other_buffer)
|
58
|
-
if @output_buffer.is_a?(OutputBufferStack)
|
59
|
-
@output_buffer.replace(other_buffer)
|
60
|
-
else
|
61
|
-
super
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def with_output_buffer(buf = nil)
|
66
|
-
unless buf
|
67
|
-
buf = ActionView::OutputBuffer.new
|
68
|
-
if @output_buffer && @output_buffer.respond_to?(:encoding)
|
69
|
-
buf.force_encoding(@output_buffer.encoding)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
result = nil
|
74
|
-
|
75
|
-
if @output_buffer.is_a?(OutputBufferStack)
|
76
|
-
@output_buffer.push(buf)
|
77
|
-
|
78
|
-
begin
|
79
|
-
yield
|
80
|
-
ensure
|
81
|
-
result = @output_buffer.pop
|
82
|
-
end
|
83
|
-
|
84
|
-
result
|
85
|
-
else
|
86
|
-
@output_buffer, old_buffer = buf, output_buffer
|
87
|
-
|
88
|
-
begin
|
89
|
-
yield
|
90
|
-
ensure
|
91
|
-
@output_buffer = old_buffer
|
92
|
-
end
|
93
|
-
|
94
|
-
buf
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ViewComponent
|
4
|
-
class OutputBufferStack
|
5
|
-
delegate_missing_to :@current_buffer
|
6
|
-
delegate :presence, :present?, :html_safe?, to: :@current_buffer
|
7
|
-
|
8
|
-
attr_reader :buffer_stack
|
9
|
-
|
10
|
-
def self.make_frame(*args)
|
11
|
-
ActionView::OutputBuffer.new(*args)
|
12
|
-
end
|
13
|
-
|
14
|
-
def initialize(initial_buffer = nil)
|
15
|
-
if initial_buffer.is_a?(self.class)
|
16
|
-
@current_buffer = self.class.make_frame(initial_buffer.current)
|
17
|
-
@buffer_stack = [*initial_buffer.buffer_stack[0..-2], @current_buffer]
|
18
|
-
else
|
19
|
-
@current_buffer = initial_buffer || self.class.make_frame
|
20
|
-
@buffer_stack = [@current_buffer]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def replace(buffer)
|
25
|
-
return if self == buffer
|
26
|
-
|
27
|
-
@current_buffer = buffer.current
|
28
|
-
@buffer_stack = buffer.buffer_stack
|
29
|
-
end
|
30
|
-
|
31
|
-
def append=(arg)
|
32
|
-
@current_buffer.append = arg
|
33
|
-
end
|
34
|
-
|
35
|
-
def safe_append=(arg)
|
36
|
-
@current_buffer.safe_append = arg
|
37
|
-
end
|
38
|
-
|
39
|
-
def safe_concat(arg)
|
40
|
-
# rubocop:disable Rails/OutputSafety
|
41
|
-
@current_buffer.safe_concat(arg)
|
42
|
-
# rubocop:enable Rails/OutputSafety
|
43
|
-
end
|
44
|
-
|
45
|
-
def length
|
46
|
-
@current_buffer.length
|
47
|
-
end
|
48
|
-
|
49
|
-
def push(buffer = nil)
|
50
|
-
buffer ||= self.class.make_frame
|
51
|
-
@buffer_stack.push(buffer)
|
52
|
-
@current_buffer = buffer
|
53
|
-
end
|
54
|
-
|
55
|
-
def pop
|
56
|
-
@buffer_stack.pop.tap do
|
57
|
-
@current_buffer = @buffer_stack.last
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def to_s
|
62
|
-
@current_buffer
|
63
|
-
end
|
64
|
-
|
65
|
-
alias_method :current, :to_s
|
66
|
-
end
|
67
|
-
end
|