view_component 2.52.0 → 2.62.0
Sign up to get free protection for your applications and to get access to all the features.
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
|