Don't redirect $stdout in :ruby filter.

Redirecting $stdout isn't thread safe.

Provide `haml_io`, an IO object that writes to the buffer.
This commit is contained in:
Matt Wildig 2013-01-18 22:26:06 +00:00 committed by Norman Clarke
parent ba8b4c1694
commit 4d3d0b27a7
3 changed files with 36 additions and 9 deletions

View File

@ -5,8 +5,9 @@
* The Haml exectutable now accepts an `--autoclose` option. You can now
specify a list of tags that should be autoclosed
* The `:ruby` filter now runs the generated code with a exclusive lock, to
prevent issues with sharing `$stdout` across threads.
* The `:ruby` filter no longer redirects $stdout to the Haml document, as this
is not thread safe. Instead it provides a `haml_io` local variable, which is
an IO object that writes to the document.
* HTML5 is now the default output format rather than XHTML. This was already
the default on Rails 3+, so many users will notice no difference.

View File

@ -258,9 +258,13 @@ RUBY
end
end
# Parses the filtered text with the normal Ruby interpreter. All output sent
# to `$stdout`, such as with `puts`, is output into the Haml document. Not
# available if the {file:REFERENCE.md#suppress_eval-option `:suppress_eval`}
# Parses the filtered text with the normal Ruby interpreter. Creates an IO
# object named `haml_io`, anything written to it is output into the Haml
# document. In previous version this filter redirected any output to `$stdout`
# to the Haml document, this was not threadsafe and has been removed, you
# should use `haml_io` instead.
#
# Not available if the {file:REFERENCE.md#suppress_eval-option `:suppress_eval`}
# option is set to true. The Ruby code is evaluated in the same context as
# the Haml template.
module Ruby
@ -272,11 +276,13 @@ RUBY
return if compiler.options[:suppress_eval]
compiler.instance_eval do
push_silent <<-FIRST.gsub("\n", ';') + text + <<-LAST.gsub("\n", ';')
_haml_old_stdout = $stdout
$stdout = StringIO.new(_hamlout.buffer, 'a')
begin
haml_io = StringIO.new(_hamlout.buffer, 'a')
FIRST
_haml_old_stdout, $stdout = $stdout, _haml_old_stdout
_haml_old_stdout.close
ensure
haml_io.close
haml_io = nil
end
LAST
end
end

View File

@ -235,4 +235,24 @@ class EscapedFilterTest < MiniTest::Unit::TestCase
haml = ":escaped\n &"
assert_equal(html, render(haml))
end
end
class RubyFilterTest < MiniTest::Unit::TestCase
test "can write to haml_io" do
haml = ":ruby\n haml_io.puts 'hello'\n"
html = "hello\n"
assert_equal(html, render(haml))
end
test "haml_io appends to output" do
haml = "hello\n:ruby\n haml_io.puts 'hello'\n"
html = "hello\nhello\n"
assert_equal(html, render(haml))
end
test "can create local variables" do
haml = ":ruby\n a = 7\n=a"
html = "7\n"
assert_equal(html, render(haml))
end
end