-
Notifications
You must be signed in to change notification settings - Fork 459
Use a global output buffer #1307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
8effb71
Use a global output buffer
camertron b2bbac2
Fix tests for Rails < 6
camertron 28439b6
Make rubocop happy
camertron f5a1bb5
Test fragment caching when the global output buffer is enabled
camertron 2a66952
Fix test coverage
camertron ec31136
Add an additional test for nested HAML/ERB/components
camertron 962301b
Run test suite with GOB and without
camertron 8815444
Explicitly skip HAML tests for rails < 6.1
camertron 9cc3d86
Fixed final line endings with Logerfo/newline-action.
Logerfo f870462
Support helper methods; fix rubocop issues
camertron bbc04f4
Merge branch 'output_buffer_stack' of github.com:github/view_componen…
camertron 446ce79
Fix double render issue when rendering the same component instance tw…
camertron 0d2c23a
Try a different strategy to get helpers to work
camertron 46577c6
Fix code coverage
camertron 60188b9
Make actionview monkeypatch a little less invasive
camertron 0379827
Use instance variable instead of method arg for indicating use of a g…
camertron 02c7bc7
Use human-readable name for GoB so CI jobs make sense
camertron 6074b1e
Remove unnecessary test helper
camertron b7903ed
Merging latest main
camertron e5a2e4f
Fix CHANGELOG
camertron 14b5aa3
Add tests for several common helpers
camertron 96c3232
Fixed final line endings with Logerfo/newline-action.
Logerfo 348f56d
Fix helpers used outside of components
camertron 39a8d78
Merging upstream
camertron 9fe51ad
Fix linting issues
camertron 15f2948
Merge branch 'main' into output_buffer_stack
camertron File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# frozen_string_literal: true | ||
|
||
module ViewComponent | ||
module GlobalOutputBuffer | ||
def render_in(view_context, &block) | ||
unless view_context.output_buffer.is_a?(OutputBufferStack) | ||
# use instance_variable_set here to avoid triggering the code in the #output_buffer= method below | ||
view_context.instance_variable_set(:@output_buffer, OutputBufferStack.new(view_context.output_buffer)) | ||
end | ||
|
||
@output_buffer = view_context.output_buffer | ||
@global_buffer_in_use = true | ||
|
||
super(view_context, &block) | ||
end | ||
|
||
def perform_render | ||
# HAML unhelpfully assigns to @output_buffer directly, so we hold onto a reference to | ||
# it and restore @output_buffer when the HAML engine is finished. In non-HAML cases, | ||
# @output_buffer and orig_buf will point to the same object, making the reassignment | ||
# statements no-ops. | ||
orig_buf = @output_buffer | ||
@output_buffer.push | ||
result = render_template_for(@__vc_variant).to_s + _output_postamble | ||
@output_buffer = orig_buf | ||
@output_buffer.pop | ||
result | ||
end | ||
|
||
def output_buffer=(other_buffer) | ||
@output_buffer.replace(other_buffer) | ||
end | ||
|
||
def with_output_buffer(buf = nil) | ||
unless buf | ||
buf = ActionView::OutputBuffer.new | ||
if output_buffer && output_buffer.respond_to?(:encoding) | ||
buf.force_encoding(output_buffer.encoding) | ||
end | ||
end | ||
|
||
output_buffer.push(buf) | ||
result = nil | ||
|
||
begin | ||
yield | ||
ensure | ||
# assign result here to avoid a return statement, which will | ||
# immediately return to the caller and swallow any errors | ||
result = output_buffer.pop | ||
end | ||
|
||
result | ||
end | ||
|
||
module ActionViewMods | ||
def with_output_buffer(buf = nil) | ||
# save a reference to the stack-based buffer | ||
orig_buf = output_buffer | ||
result = nil | ||
|
||
super do | ||
if orig_buf.respond_to?(:push) | ||
camertron marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# super will have set self.output_buffer to the new buffer | ||
new_buf = self.output_buffer | ||
orig_buf.push(new_buf) | ||
|
||
# set output_buffer back to the original, stack-based buffer we | ||
# saved a reference to earlier | ||
self.output_buffer = orig_buf | ||
|
||
begin | ||
yield | ||
ensure | ||
result = orig_buf.pop | ||
end | ||
else | ||
# :nocov: | ||
yield | ||
result = output_buffer | ||
# :nocov: | ||
end | ||
end | ||
|
||
result | ||
end | ||
end | ||
end | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# frozen_string_literal: true | ||
|
||
module ViewComponent | ||
class OutputBufferStack | ||
delegate_missing_to :@current_buffer | ||
delegate :presence, :present?, :html_safe?, to: :@current_buffer | ||
|
||
attr_reader :buffer_stack | ||
|
||
def self.make_frame(*args) | ||
ActionView::OutputBuffer.new(*args) | ||
end | ||
|
||
def initialize(initial_buffer = nil) | ||
if initial_buffer.is_a?(self.class) | ||
@current_buffer = self.class.make_frame(initial_buffer.current) | ||
@buffer_stack = [*initial_buffer.buffer_stack[0..-2], @current_buffer] | ||
else | ||
@current_buffer = initial_buffer || self.class.make_frame | ||
@buffer_stack = [@current_buffer] | ||
end | ||
end | ||
|
||
def replace(buffer) | ||
return if self == buffer | ||
|
||
@current_buffer = buffer.current | ||
@buffer_stack = buffer.buffer_stack | ||
end | ||
|
||
def append=(arg) | ||
@current_buffer.append = arg | ||
end | ||
|
||
def safe_append=(arg) | ||
@current_buffer.safe_append = arg | ||
end | ||
|
||
def safe_concat(arg) | ||
# rubocop:disable Rails/OutputSafety | ||
@current_buffer.safe_concat(arg) | ||
# rubocop:enable Rails/OutputSafety | ||
end | ||
|
||
def length | ||
@current_buffer.length | ||
end | ||
|
||
def push(buffer = nil) | ||
buffer ||= self.class.make_frame | ||
@buffer_stack.push(buffer) | ||
@current_buffer = buffer | ||
end | ||
|
||
def pop | ||
@buffer_stack.pop.tap do | ||
@current_buffer = @buffer_stack.last | ||
end | ||
end | ||
|
||
def to_s | ||
@current_buffer | ||
end | ||
|
||
alias_method :current, :to_s | ||
end | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<h1>hello <%= @name %></h1> | ||
|
||
<% 50.times do %> | ||
<%= render Performance::NestedNameComponentWithGOB.new(name: @name) %> | ||
<% end %> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# frozen_string_literal: true | ||
|
||
class Performance::NameComponentWithGOB < Performance::NameComponent | ||
prepend ViewComponent::GlobalOutputBuffer | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<p>nested hello <%= @name %></p> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# frozen_string_literal: true | ||
|
||
class Performance::NestedNameComponentWithGOB < Performance::NestedNameComponent | ||
prepend ViewComponent::GlobalOutputBuffer | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# frozen_string_literal: true | ||
|
||
# Run `bundle exec rake benchmark` to execute benchmark. | ||
# This is very much a work-in-progress. Please feel free to make/suggest improvements! | ||
|
||
require "benchmark/ips" | ||
|
||
# Configure Rails Environment | ||
ENV["RAILS_ENV"] = "production" | ||
require File.expand_path("../test/sandbox/config/environment.rb", __dir__) | ||
|
||
module Performance | ||
require_relative "components/name_component.rb" | ||
require_relative "components/nested_name_component.rb" | ||
require_relative "components/name_component_with_gob.rb" | ||
require_relative "components/nested_name_component_with_gob.rb" | ||
end | ||
|
||
class BenchmarksController < ActionController::Base | ||
end | ||
|
||
BenchmarksController.view_paths = [File.expand_path("./views", __dir__)] | ||
controller_view = BenchmarksController.new.view_context | ||
|
||
Benchmark.ips do |x| | ||
x.time = 10 | ||
x.warmup = 2 | ||
|
||
x.report("component") { controller_view.render(Performance::NameComponent.new(name: "Fox Mulder")) } | ||
x.report("component with GOB") { controller_view.render(Performance::NameComponentWithGOB.new(name: "Fox Mulder")) } | ||
|
||
x.compare! | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<%= helpers.my_helper_method({ foo: :bar }) do %> | ||
<p>Content inside helper method</p> | ||
<% end %> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# frozen_string_literal: true | ||
|
||
class ContentTagComponent < ViewComponent::Base | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<%= form_for Post.new, url: "/" do |form| %> | ||
<%= render LabelComponent.new(form: form) %> | ||
<% end %> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# frozen_string_literal: true | ||
|
||
class FormForComponent < ViewComponent::Base | ||
def initialize | ||
end | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<%= form_with model: Post.new, url: "/" do |form| %> | ||
<%= render LabelComponent.new(form: form) %> | ||
<% end %> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# frozen_string_literal: true | ||
|
||
class FormWithComponent < ViewComponent::Base | ||
def initialize | ||
end | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.