Skip to content

Instantly share code, notes, and snippets.

@kaorukobo
Last active August 17, 2024 03:52
Show Gist options
  • Save kaorukobo/85e7c48b6ad6122257d1f75c553a6932 to your computer and use it in GitHub Desktop.
Save kaorukobo/85e7c48b6ad6122257d1f75c553a6932 to your computer and use it in GitHub Desktop.
A shim for capture_haml method dropped in Haml 6+

The capture_haml method has been removed as of HAML 6.0.0:

https://github.com/haml/haml/blob/main/CHANGELOG.md#600 Most Haml helpers are removed. For helpers generating general HTML tags, also consider using what your framework provides, e.g. Rails content_tag. Same applies to capture_haml, e.g. Rails capture.

This change is breaking for the code that works outside of Rails. So I have implemented capture_haml for Haml 6

Note that this does not work with block capturing support so you need to set the option {disable_capture: true}.

require "bundler/inline"
gemfile(false) do
source "https://rubygems.org"
gem "haml"
gem "temple"
gem "rspec"
end
require "haml"
require "temple/html/safe"
module Haml
module Capture
module HelperExtension
def capture_haml(&block)
with_buf =
begin
buf = Haml::Capture.buffer_stack.last
if buf
buf.method(:then)
else
Haml::Capture.method(:with_new_buffer)
end
end
with_buf.call { |buf|
length_before_capture = buf.length
begin
block.call(buf)
buf[length_before_capture..-1].html_safe
ensure
buf.slice!(length_before_capture..-1)
end
}
end
end
module BufferExtension
def create_buffer
"Haml::Capture.with_new_buffer do |#{buffer}|;"
end
def return_buffer
"#{buffer.to_s};end;"
end
end
::Haml::Helpers.prepend(HelperExtension)
::Temple::Generators::StringBuffer.prepend(BufferExtension)
class << self
# @return [Array<String>]
def buffer_stack
th = Thread.current
th.thread_variable_get(:capture_haml_buffer_stack) ||
[].tap { |v|
th.thread_variable_set(:capture_haml_buffer_stack, v)
}
end
def with_new_buffer(&block)
buf = ''.dup
buffer_stack << buf
begin
yield buf
ensure
last_buf = buffer_stack.pop
unless last_buf.equal?(buf)
raise StandardError, "inconsistent buffer stack push/pop"
end
end
end
end
end
end
require "rspec/autorun"
RSpec.describe "capture_haml for Haml6+ outside Rails" do
let :context do
Object.new
end
it "allows us to define a helper Proc that returns HAML output" do
haml = <<~HAML
- @fragment = -> do
- capture_haml do
%div fragment
%body= @fragment.call
HAML
expect(render(haml)).to eq(<<~HTML)
<body><div>fragment</div>\n</body>
HTML
end
it "allows us to define a method that returns HAML output" do
haml = <<~HAML
- def self.fragment
- capture_haml do |_buf| # needs to initialize `_buf` variable
%div fragment
HAML
render(haml)
expect(context.fragment).to eq(<<~HTML)
<div>fragment</div>
HTML
end
def render(haml)
template = Haml::Template.new(use_html_safe: true) { haml }
# decomment to print the generated code:
# puts template.send(:precompiled, {})
template.render(context)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment