Skip to content

Commit

Permalink
Make capture_erb plugin call integrate better with erubi/capture_block
Browse files Browse the repository at this point in the history
This has capture_erb call capture on the output buffer object if
it responds to it.  This can fix issues when inside the block
passed to capture_erb, there is a call to capture directly on the
buffer object.
  • Loading branch information
jeremyevans committed Jun 17, 2024
1 parent 37a6f7d commit 8ce9eee
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
= master

* Make capture_erb plugin call integrate better with erubi/capture_block (jeremyevans)

= 3.81.0 (2024-06-12)

* Make assets plugin :early_hints option follow Rack 3 SPEC if using Rack 3 (jeremyevans)
Expand Down
22 changes: 17 additions & 5 deletions lib/roda/plugins/capture_erb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ module RodaPlugins
# inside templates. It can be combined with the inject_erb plugin
# to wrap template blocks with arbitrary output and then inject the
# wrapped output into the template.
#
# If the output buffer object responds to +capture+ (e.g. when
# +erubi/capture_block+ is being used as the template engine),
# this will call +capture+ on the output buffer object, instead
# of setting the output buffer object temporarily to a new object.
module CaptureERB
def self.load_dependencies(app)
app.plugin :render
Expand All @@ -25,13 +30,20 @@ module InstanceMethods
# with an empty string, and then yield to the block.
# Return the value of the block, converted to a string.
# Restore the previous ERB output buffer before returning.
def capture_erb
def capture_erb(&block)
outvar = render_opts[:template_opts][:outvar]
buf_was = instance_variable_get(outvar)
instance_variable_set(outvar, String.new)
yield.to_s
ensure
instance_variable_set(outvar, buf_was) if outvar && buf_was

if buf_was.respond_to?(:capture)
buf_was.capture(&block)
else
begin
instance_variable_set(outvar, String.new)
yield.to_s
ensure
instance_variable_set(outvar, buf_was) if outvar && buf_was
end
end
end
end
end
Expand Down
54 changes: 53 additions & 1 deletion spec/plugin/capture_erb_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require_relative "../spec_helper"

begin
require 'tilt/erb'
require 'tilt'
rescue LoadError
warn "tilt not installed, skipping capture_erb plugin test"
else
Expand Down Expand Up @@ -52,3 +52,55 @@ def some_method(&block)
end
end
end

begin
require 'tilt/erubi'
require 'erubi/capture_block'
rescue LoadError
warn "tilt/erubi or erubi/capture not installed, skipping capture_erb plugin test for erubi/capture_block"
else
describe "capture_erb plugin with erubi/capture_block" do
before do
app(:bare) do
plugin :render, :views => './spec/views', template_opts: {engine_class: Erubi::CaptureBlockEngine}
plugin :capture_erb
plugin :inject_erb

route do |r|
r.root do
render(:inline => "<% value = capture_erb do %>foo<% end %>bar<%= value %>")
end
r.get 'rescue' do
render(:inline => "<% value = capture_erb do %>foo<% raise %><% end rescue (value = 'baz') %>bar<%= value %>")
end
r.get 'inject' do
render(:inline => "<%= some_method do %>foo<% end %>")
end
r.get 'outside' do
capture_erb{1}
end
end

def some_method(&block)
"bar#{capture_erb(&block).upcase}baz"
end
end
end

it "should capture erb output" do
body.strip.must_equal "barfoo"
end

it "should handle exceptions in captured blocks" do
body('/rescue').strip.must_equal "barbaz"
end

it "should work with the inject_erb plugin" do
body('/inject').strip.must_equal "barFOObaz"
end

it "should return result of block converted to string when used outside template" do
body('/outside').must_equal "1"
end
end
end

0 comments on commit 8ce9eee

Please sign in to comment.