Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 2 additions & 25 deletions actionmailer/test/caching_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,31 +221,8 @@ def self.output_buffer=; end

cache_helper.stub :controller, controller do
cache_helper.stub :output_buffer, output_buffer do
assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do
assert_nothing_raised do
cache_helper.send :fragment_for, "Test fragment name", "Test fragment", &Proc.new { nil }
end
end
end
end
end

def test_safe_buffer
output_buffer = ActiveSupport::SafeBuffer.new
controller = MockController.new
cache_helper = Class.new do
def self.controller; end
def self.output_buffer; end
def self.output_buffer=; end
end
cache_helper.extend(ActionView::Helpers::CacheHelper)

cache_helper.stub :controller, controller do
cache_helper.stub :output_buffer, output_buffer do
assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do
assert_nothing_raised do
cache_helper.send :fragment_for, "Test fragment name", "Test fragment", &Proc.new { nil }
end
assert_nothing_raised do
cache_helper.send :fragment_for, "Test fragment name", "Test fragment", &Proc.new { nil }
end
end
end
Expand Down
27 changes: 2 additions & 25 deletions actionpack/test/controller/caching_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -344,31 +344,8 @@ def self.output_buffer=; end

cache_helper.stub :controller, controller do
cache_helper.stub :output_buffer, output_buffer do
assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do
assert_nothing_raised do
cache_helper.send :fragment_for, "Test fragment name", "Test fragment", &Proc.new { nil }
end
end
end
end
end

def test_safe_buffer
output_buffer = ActiveSupport::SafeBuffer.new
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any legit reason for using SafeBuffer as @output_buffer and its holding us back.

controller = MockController.new
cache_helper = Class.new do
def self.controller; end
def self.output_buffer; end
def self.output_buffer=; end
end
cache_helper.extend(ActionView::Helpers::CacheHelper)

cache_helper.stub :controller, controller do
cache_helper.stub :output_buffer, output_buffer do
assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do
assert_nothing_raised do
cache_helper.send :fragment_for, "Test fragment name", "Test fragment", &Proc.new { nil }
end
assert_nothing_raised do
cache_helper.send :fragment_for, "Test fragment name", "Test fragment", &Proc.new { nil }
end
end
end
Expand Down
83 changes: 70 additions & 13 deletions actionview/lib/action_view/buffers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@ module ActionView
#
class OutputBuffer # :nodoc:
def initialize(buffer = "")
@buffer = String.new(buffer)
@buffer.encode!
@raw_buffer = String.new(buffer)
@raw_buffer.encode!
end

delegate :length, :blank?, :encoding, :encode!, :force_encoding, to: :@buffer
delegate :length, :blank?, :encoding, :encode!, :force_encoding, to: :@raw_buffer

def to_s
@buffer.html_safe
@raw_buffer.html_safe
end
alias_method :html_safe, :to_s

def to_str
@buffer.dup
@raw_buffer.dup
end

def html_safe?
Expand All @@ -42,7 +42,7 @@ def html_safe?
def <<(value)
unless value.nil?
value = value.to_s
@buffer << if value.html_safe?
@raw_buffer << if value.html_safe?
value
else
CGI.escapeHTML(value)
Expand All @@ -53,28 +53,54 @@ def <<(value)
alias :append= :<<

def safe_concat(value)
@buffer << value
@raw_buffer << value
self
end
alias :safe_append= :safe_concat

def safe_expr_append=(val)
return self if val.nil?
@buffer << val.to_s
@raw_buffer << val.to_s
self
end

def initialize_copy(other)
@buffer = other.to_str
@raw_buffer = other.to_str
end

# Don't use this
def slice!(range)
@buffer.slice!(range)
def capture
new_buffer = +""
old_buffer, @raw_buffer = @raw_buffer, new_buffer
yield
new_buffer.html_safe
ensure
@raw_buffer = old_buffer
end

def ==(other)
other.class == self.class && @buffer == other.to_str
other.class == self.class && @raw_buffer == other.to_str
end

def raw
RawOutputBuffer.new(self)
end

attr_reader :raw_buffer
end

class RawOutputBuffer # :nodoc:
def initialize(buffer)
@buffer = buffer
end

def <<(value)
unless value.nil?
@buffer.raw_buffer << value.to_s
end
end

def raw
self
end
end

Expand All @@ -96,12 +122,43 @@ def safe_concat(value)
end
alias :safe_append= :safe_concat

def capture
buffer = +""
old_block, @block = @block, ->(value) { buffer << value }
yield
buffer.html_safe
ensure
@block = old_block
end

def html_safe?
true
end

def html_safe
self
end

def raw
RawStreamingBuffer.new(self)
end

attr_reader :block
end

class RawStreamingBuffer # :nodoc:
def initialize(buffer)
@buffer = buffer
end

def <<(value)
unless value.nil?
@buffer.block.call(value.to_s)
end
end

def raw
self
end
end
end
13 changes: 2 additions & 11 deletions actionview/lib/action_view/helpers/cache_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -281,17 +281,8 @@ def read_fragment_for(name, options)
controller.read_fragment(name, options)
end

def write_fragment_for(name, options)
pos = output_buffer.length
yield
output_safe = output_buffer.html_safe?
# We need to modify the buffer in place to be deal with the view handlers
# like `builder` that don't access the buffer through `@output_buffer` but
# keep the initial reference.
fragment = output_buffer.slice!(pos..-1)
if output_safe
self.output_buffer = output_buffer.class.new(output_buffer)
end
def write_fragment_for(name, options, &block)
fragment = output_buffer.capture(&block)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

controller.write_fragment(name, fragment, options)
end

Expand Down
7 changes: 3 additions & 4 deletions actionview/lib/action_view/template/handlers/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ class Builder

def call(template, source)
require_engine
"xml = ::Builder::XmlMarkup.new(:indent => 2);" \
"self.output_buffer = xml.target!;" +
source +
";xml.target!;"
"xml = ::Builder::XmlMarkup.new(indent: 2, target: output_buffer.raw);" \
"#{source};" \
"output_buffer.to_s"
end

private
Expand Down
89 changes: 89 additions & 0 deletions actionview/test/buffers_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# frozen_string_literal: true

require "abstract_unit"

module SharedBufferTests
def self.included(test_case)
test_case.test "#<< maintains HTML safety" do
@buffer << "<script>alert('pwned!')</script>"
assert_predicate @buffer, :html_safe?
assert_predicate output, :html_safe?
assert_equal "&lt;script&gt;alert(&#39;pwned!&#39;)&lt;/script&gt;", output
end

test_case.test "#safe_append= bypasses HTML safety" do
@buffer.safe_append = "<p>This is fine</p>"
assert_predicate @buffer, :html_safe?
assert_predicate output, :html_safe?
assert_equal "<p>This is fine</p>", output
end

test_case.test "#raw allow to bypass HTML escaping" do
raw_buffer = @buffer.raw
raw_buffer << "<script>alert('pwned!')</script>"
assert_predicate @buffer, :html_safe?
assert_predicate output, :html_safe?
assert_equal "<script>alert('pwned!')</script>", output
end

test_case.test "#capture allow to intercept writes" do
@buffer << "Hello"
result = @buffer.capture do
@buffer << "George!"
end
assert_equal "George!", result
assert_predicate result, :html_safe?

@buffer << " World!"
assert_equal "Hello World!", output
end

test_case.test "#raw respects #capture" do
@buffer << "Hello"
raw_buffer = @buffer.raw
result = @buffer.capture do
raw_buffer << "George!"
end
assert_equal "George!", result
assert_predicate result, :html_safe?

@buffer << " World!"
assert_equal "Hello World!", output
end
end
end

class TestOutputBuffer < ActiveSupport::TestCase
include SharedBufferTests

setup do
@buffer = ActionView::OutputBuffer.new
end

test "can be duped" do
@buffer << "Hello"
copy = @buffer.dup
copy << " World!"
assert_equal "Hello World!", copy.to_s
assert_equal "Hello", output
end

private
def output
@buffer.to_s
end
end

class TestStreamingBuffer < ActiveSupport::TestCase
include SharedBufferTests

setup do
@raw_buffer = +""
@buffer = ActionView::StreamingBuffer.new(@raw_buffer.method(:<<))
end

private
def output
@raw_buffer.html_safe
end
end
31 changes: 0 additions & 31 deletions actionview/test/output_buffer_test.rb

This file was deleted.