Skip to content

Commit 541ddf9

Browse files
committed
Add global rendering buffer
1 parent ea44b33 commit 541ddf9

18 files changed

+263
-79
lines changed

Gemfile.lock

+78-75
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,105 @@
1-
PATH
2-
remote: .
3-
specs:
4-
view_component (2.48.0)
5-
activesupport (>= 5.0.0, < 8.0)
6-
method_source (~> 1.0)
7-
8-
GEM
9-
remote: https://rubygems.org/
1+
GIT
2+
remote: https://github.com/rails/rails
3+
revision: 532c413e8e709ab7f12997e035c166d7974e1c83
4+
ref: main
105
specs:
11-
actioncable (7.0.1)
12-
actionpack (= 7.0.1)
13-
activesupport (= 7.0.1)
6+
actioncable (7.1.0.alpha)
7+
actionpack (= 7.1.0.alpha)
8+
activesupport (= 7.1.0.alpha)
149
nio4r (~> 2.0)
1510
websocket-driver (>= 0.6.1)
16-
actionmailbox (7.0.1)
17-
actionpack (= 7.0.1)
18-
activejob (= 7.0.1)
19-
activerecord (= 7.0.1)
20-
activestorage (= 7.0.1)
21-
activesupport (= 7.0.1)
11+
actionmailbox (7.1.0.alpha)
12+
actionpack (= 7.1.0.alpha)
13+
activejob (= 7.1.0.alpha)
14+
activerecord (= 7.1.0.alpha)
15+
activestorage (= 7.1.0.alpha)
16+
activesupport (= 7.1.0.alpha)
2217
mail (>= 2.7.1)
2318
net-imap
2419
net-pop
2520
net-smtp
26-
actionmailer (7.0.1)
27-
actionpack (= 7.0.1)
28-
actionview (= 7.0.1)
29-
activejob (= 7.0.1)
30-
activesupport (= 7.0.1)
21+
actionmailer (7.1.0.alpha)
22+
actionpack (= 7.1.0.alpha)
23+
actionview (= 7.1.0.alpha)
24+
activejob (= 7.1.0.alpha)
25+
activesupport (= 7.1.0.alpha)
3126
mail (~> 2.5, >= 2.5.4)
3227
net-imap
3328
net-pop
3429
net-smtp
3530
rails-dom-testing (~> 2.0)
36-
actionpack (7.0.1)
37-
actionview (= 7.0.1)
38-
activesupport (= 7.0.1)
31+
actionpack (7.1.0.alpha)
32+
actionview (= 7.1.0.alpha)
33+
activesupport (= 7.1.0.alpha)
3934
rack (~> 2.0, >= 2.2.0)
4035
rack-test (>= 0.6.3)
4136
rails-dom-testing (~> 2.0)
4237
rails-html-sanitizer (~> 1.0, >= 1.2.0)
43-
actiontext (7.0.1)
44-
actionpack (= 7.0.1)
45-
activerecord (= 7.0.1)
46-
activestorage (= 7.0.1)
47-
activesupport (= 7.0.1)
38+
actiontext (7.1.0.alpha)
39+
actionpack (= 7.1.0.alpha)
40+
activerecord (= 7.1.0.alpha)
41+
activestorage (= 7.1.0.alpha)
42+
activesupport (= 7.1.0.alpha)
4843
globalid (>= 0.6.0)
4944
nokogiri (>= 1.8.5)
50-
actionview (7.0.1)
51-
activesupport (= 7.0.1)
45+
actionview (7.1.0.alpha)
46+
activesupport (= 7.1.0.alpha)
5247
builder (~> 3.1)
5348
erubi (~> 1.4)
5449
rails-dom-testing (~> 2.0)
5550
rails-html-sanitizer (~> 1.1, >= 1.2.0)
56-
activejob (7.0.1)
57-
activesupport (= 7.0.1)
51+
activejob (7.1.0.alpha)
52+
activesupport (= 7.1.0.alpha)
5853
globalid (>= 0.3.6)
59-
activemodel (7.0.1)
60-
activesupport (= 7.0.1)
61-
activerecord (7.0.1)
62-
activemodel (= 7.0.1)
63-
activesupport (= 7.0.1)
64-
activestorage (7.0.1)
65-
actionpack (= 7.0.1)
66-
activejob (= 7.0.1)
67-
activerecord (= 7.0.1)
68-
activesupport (= 7.0.1)
54+
activemodel (7.1.0.alpha)
55+
activesupport (= 7.1.0.alpha)
56+
activerecord (7.1.0.alpha)
57+
activemodel (= 7.1.0.alpha)
58+
activesupport (= 7.1.0.alpha)
59+
activestorage (7.1.0.alpha)
60+
actionpack (= 7.1.0.alpha)
61+
activejob (= 7.1.0.alpha)
62+
activerecord (= 7.1.0.alpha)
63+
activesupport (= 7.1.0.alpha)
6964
marcel (~> 1.0)
7065
mini_mime (>= 1.1.0)
71-
activesupport (7.0.1)
66+
activesupport (7.1.0.alpha)
7267
concurrent-ruby (~> 1.0, >= 1.0.2)
7368
i18n (>= 1.6, < 2)
7469
minitest (>= 5.1)
7570
tzinfo (~> 2.0)
71+
rails (7.1.0.alpha)
72+
actioncable (= 7.1.0.alpha)
73+
actionmailbox (= 7.1.0.alpha)
74+
actionmailer (= 7.1.0.alpha)
75+
actionpack (= 7.1.0.alpha)
76+
actiontext (= 7.1.0.alpha)
77+
actionview (= 7.1.0.alpha)
78+
activejob (= 7.1.0.alpha)
79+
activemodel (= 7.1.0.alpha)
80+
activerecord (= 7.1.0.alpha)
81+
activestorage (= 7.1.0.alpha)
82+
activesupport (= 7.1.0.alpha)
83+
bundler (>= 1.15.0)
84+
railties (= 7.1.0.alpha)
85+
railties (7.1.0.alpha)
86+
actionpack (= 7.1.0.alpha)
87+
activesupport (= 7.1.0.alpha)
88+
method_source
89+
rake (>= 12.2)
90+
thor (~> 1.0)
91+
zeitwerk (~> 2.5)
92+
93+
PATH
94+
remote: .
95+
specs:
96+
view_component (2.49.0)
97+
activesupport (>= 5.0.0, < 8.0)
98+
method_source (~> 1.0)
99+
100+
GEM
101+
remote: https://rubygems.org/
102+
specs:
76103
addressable (2.8.0)
77104
public_suffix (>= 2.0.2, < 5.0)
78105
ansi (1.5.0)
@@ -120,13 +147,13 @@ GEM
120147
temple (>= 0.8.0)
121148
tilt
122149
html_tokenizer (0.0.7)
123-
i18n (1.8.11)
150+
i18n (1.9.1)
124151
concurrent-ruby (~> 1.0)
125152
io-wait (0.2.1)
126153
jbuilder (2.11.5)
127154
actionview (>= 5.0.0)
128155
activesupport (>= 5.0.0)
129-
loofah (2.13.0)
156+
loofah (2.14.0)
130157
crass (~> 1.0.2)
131158
nokogiri (>= 1.5.9)
132159
mail (2.7.1)
@@ -167,32 +194,11 @@ GEM
167194
rack (2.2.3)
168195
rack-test (1.1.0)
169196
rack (>= 1.0, < 3)
170-
rails (7.0.1)
171-
actioncable (= 7.0.1)
172-
actionmailbox (= 7.0.1)
173-
actionmailer (= 7.0.1)
174-
actionpack (= 7.0.1)
175-
actiontext (= 7.0.1)
176-
actionview (= 7.0.1)
177-
activejob (= 7.0.1)
178-
activemodel (= 7.0.1)
179-
activerecord (= 7.0.1)
180-
activestorage (= 7.0.1)
181-
activesupport (= 7.0.1)
182-
bundler (>= 1.15.0)
183-
railties (= 7.0.1)
184197
rails-dom-testing (2.0.3)
185198
activesupport (>= 4.2.0)
186199
nokogiri (>= 1.6)
187200
rails-html-sanitizer (1.4.2)
188201
loofah (~> 2.3)
189-
railties (7.0.1)
190-
actionpack (= 7.0.1)
191-
activesupport (= 7.0.1)
192-
method_source
193-
rake (>= 12.2)
194-
thor (~> 1.0)
195-
zeitwerk (~> 2.5)
196202
rainbow (3.1.1)
197203
rake (13.0.6)
198204
regexp_parser (2.2.0)
@@ -259,7 +265,7 @@ GEM
259265
webrick (~> 1.7.0)
260266
yard-activesupport-concern (0.0.1)
261267
yard (>= 0.8)
262-
zeitwerk (2.5.3)
268+
zeitwerk (2.5.4)
263269

264270
PLATFORMS
265271
ruby
@@ -274,11 +280,8 @@ DEPENDENCIES
274280
haml (~> 5)
275281
jbuilder (~> 2)
276282
minitest (= 5.6.0)
277-
net-imap
278-
net-pop
279-
net-smtp
280283
pry (~> 0.13)
281-
rails (~> 7.0.0)
284+
rails!
282285
rake (~> 13.0)
283286
rubocop-github (~> 0.16.1)
284287
simplecov (~> 0.18.0)
@@ -290,4 +293,4 @@ DEPENDENCIES
290293
yard-activesupport-concern
291294

292295
BUNDLED WITH
293-
2.2.33
296+
2.3.7

lib/view_component.rb

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module ViewComponent
1111
autoload :CompileCache
1212
autoload :ComponentError
1313
autoload :Deprecation
14+
autoload :GlobalBuffer
1415
autoload :Instrumentation
1516
autoload :Preview
1617
autoload :PreviewTemplateError

lib/view_component/base.rb

+15-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,18 @@ def render_in(view_context, &block)
6969
self.class.compile(raise_errors: true)
7070

7171
@view_context = view_context
72+
73+
# TODO flag with initializer
74+
if !self.__vc_original_view_context
75+
view_context.original_output_buffer = ActionView::OutputBuffer.new if !view_context.output_buffer
76+
77+
if !view_context.output_buffer.is_a?(GlobalBuffer)
78+
view_context.original_output_buffer = GlobalBuffer.new(view_context.output_buffer)
79+
end
80+
end
81+
7282
self.__vc_original_view_context ||= view_context
83+
@output_buffer = self.__vc_original_view_context.output_buffer
7384

7485
@lookup_context ||= view_context.lookup_context
7586

@@ -104,7 +115,9 @@ def render_in(view_context, &block)
104115
before_render
105116

106117
if render?
107-
render_template_for(@__vc_variant).to_s + _output_postamble
118+
capture do
119+
render_template_for(@__vc_variant).to_s + _output_postamble
120+
end
108121
else
109122
""
110123
end
@@ -155,6 +168,7 @@ def initialize(*); end
155168
def render(options = {}, args = {}, &block)
156169
if options.is_a? ViewComponent::Base
157170
options.__vc_original_view_context = __vc_original_view_context
171+
# maybe capture, but only if block given?
158172
super
159173
else
160174
__vc_original_view_context.render(options, args, &block)

lib/view_component/compiler.rb

+1-2
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,8 @@ def compile(raise_errors: false)
6969
component_class.send(:undef_method, method_name.to_sym)
7070
end
7171

72-
component_class.class_eval <<-RUBY, template[:path], -1
72+
component_class.class_eval <<-RUBY, template[:path], 0
7373
def #{method_name}
74-
@output_buffer = ActionView::OutputBuffer.new
7574
#{compiled_template(template[:path])}
7675
end
7776
RUBY

lib/view_component/engine.rb

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require "rails"
4+
require 'view_component/global_buffer'
45

56
module ViewComponent
67
class Engine < Rails::Engine # :nodoc:
@@ -93,6 +94,10 @@ class Engine < Rails::Engine # :nodoc:
9394
end
9495
end
9596

97+
initializer "view_component.global_buffer_patch" do |app|
98+
ActionView::Base.include(ViewComponent::GlobalBuffer::Patch)
99+
end
100+
96101
initializer "view_component.include_render_component" do |app|
97102
next if Rails.version.to_f >= 6.1
98103

lib/view_component/global_buffer.rb

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# frozen_string_literal: true
2+
3+
module ViewComponent
4+
# TODO if things break, log each time an unsafe string method is called
5+
class GlobalBuffer
6+
def initialize(buffer)
7+
@_buffer = buffer
8+
end
9+
10+
def __swap_buffer__(buffer)
11+
# TODO remove once this is debugged
12+
raise 'oh no' if buffer.kind_of?(GlobalBuffer)
13+
@_buffer = buffer
14+
end
15+
16+
def __buffer__
17+
@_buffer
18+
end
19+
20+
def to_s
21+
@_buffer.to_s
22+
end
23+
24+
def html_safe?
25+
@_buffer.html_safe?
26+
end
27+
28+
# Necessary for cases like `output_buffer = output_buffer.class.new(output_buffer)` (yes, this happens)
29+
def class
30+
@_buffer.class
31+
end
32+
33+
def method_missing(symbol, *args, **kwargs)
34+
@_buffer.send(symbol, *args, **kwargs)
35+
end
36+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
37+
38+
def respond_to_missing?(symbol, include_all)
39+
@_buffer.respond_to?(symbol, include_all) || super
40+
end
41+
42+
module Patch
43+
extend ActiveSupport::Concern
44+
45+
module SeparateGlobalModuleForHAMLCompat
46+
def output_buffer=(new_buf)
47+
# TODO make HAML work by falling back to super
48+
@output_buffer.__swap_buffer__(new_buf)
49+
end
50+
51+
def with_output_buffer(buf = nil) # :nodoc:
52+
$count ||= 0
53+
$count += 1
54+
unless buf
55+
buf = ActionView::OutputBuffer.new
56+
if output_buffer && output_buffer.respond_to?(:encoding)
57+
buf.force_encoding(output_buffer.encoding)
58+
end
59+
end
60+
61+
old_buffer = output_buffer.__buffer__
62+
@output_buffer.__swap_buffer__(buf)
63+
yield
64+
@output_buffer.__buffer__
65+
ensure
66+
@output_buffer.__swap_buffer__(old_buffer)
67+
end
68+
end
69+
70+
included do
71+
alias_method(:original_output_buffer=, :output_buffer=)
72+
prepend SeparateGlobalModuleForHAMLCompat
73+
74+
alias_method(:original_with_output_buffer, :with_output_buffer)
75+
end
76+
end
77+
end
78+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<%= form_for Post.new, url: "/" do |form| %>
2+
<%= render LabelComponent.new(form: form) %>
3+
<% end %>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# frozen_string_literal: true
2+
3+
class FormForComponent < ViewComponent::Base
4+
def initialize
5+
end
6+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<%= form_with model: Post.new, url: "/" do |form| %>
2+
<%= render LabelComponent.new(form: form) %>
3+
<% end %>

0 commit comments

Comments
 (0)