Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
489 views
in Technique[技术] by (71.8m points)

ruby - Calling ERB recursively with binding results in lost output text

(Note: I am using Ruby's erb, not that of Rails.)

When using the current binding to resolve an ERB template from within another ERB template, the text of the outer template that precedes the call to the inner one is lost.

For example:

inner.erb file content:

inner

outer.erb file content:

outer
<%= ERB.new(File.read('inner.erb')).result(binding) %>
outer

Test script erb-test (must do chmod +x erb-test):

#!/usr/bin/env ruby

require 'erb'

puts ERB.new(File.read('outer.erb')).result

The output of ./erb-test is:

inner

outer

As shown, the first 'outer' string specified in the outer.erb template file is lost. If we remove the binding from the ERB call in outer.erb:

<%= ERB.new(File.read('inner.erb')).result %>

...then we now see the first outer specified in the outer template:

outer
inner

outer

Is this a bug? How do you recommend addressing this? I realize it's better to limit the state available to templates, and usually I do that using ERB#result_with_hash, but in the case of a sub-template, that is, a template called from another template, it seems reasonable to me that the inner template inherit the binding of the outer template, since it is already limited.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

This can be fixed by using the eoutvar parameter to ERB.new, as follows:

<%= ERB.new(File.read('inner.erb'), eoutvar: '@inner').result(binding) %>

Comments in the ERB source code explain it further:

# _eoutvar_ can be used to set the name of the variable ERB will build up
# its output in.  This is useful when you need to run multiple ERB
# templates through the same binding and/or when you want to control where
# output ends up.  Pass the name of the variable to be used inside a String.

After changing the content of outer.erb to:

outer
<%= ERB.new(File.read('inner.erb'), eoutvar: '@inner').result(binding) %>
outer

...the result is:

outer

inner

outer

Do be aware that if you were passing unnamed parameters in the constructor call, you will need to use the named approach instead, e.g. for trim mode:

    ERB.new(File.read(erb_filespec), eoutvar: eoutvar, trim_mode: '-')

instead of:

    template = ERB.new(File.read(erb_filespec), 0, '-')

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...