'Ruby ERB yield inside another template
Note: I'm not using rails, sinatra, or tilt, just ruby's built in ERB.
Suppose I have two erb files:
file1.erb:
Start
<%= yield if block_given? -%>
End
and file2.erb:
<% render('file1.erb') do %>
Some text
<% end %>
I'd like to get output that looks like:
Start
Some text
End
However, with the following ruby code:
require 'erb'
def render(file)
content = File.read(file)
b = binding
ERB.new(content, trim_mode: '-').result b
end
res = render('file2.erb')
I only get "Some Text". And if I change the first line of file2.erb to use <%= instead of <% then I get a syntax error:
Traceback (most recent call last):
3: from test.rb:9:in `<main>'
2: from test.rb:6:in `render'
1: from /usr/lib/ruby/2.7.0/erb.rb:905:in `result'
/usr/lib/ruby/2.7.0/erb.rb:905:in `eval': (erb):1: syntax error, unexpected ')' (SyntaxError)
...t.<<(( render('file1.erb') do ).to_s); _erbout.<< "\\n Some T...
... ^
(erb):3: syntax error, unexpected `end', expecting ')'
; end ; _erbout.<< "\\n".freeze
^~~
(erb):4: syntax error, unexpected end-of-input, expecting ')'
Is there some way to get this to work with the 'erb' module?
I can get the yielding to work if I use a block that returns an expression directly, but that won't work for my case.
Another answer has a solution where the calling code can render a template inside a different layout. But that doesn't really work for me, because I need to define the layout to use inside the template itself (there isn't really an equivalent of a controller in my code).
I thought of maybe doing something like, changing .result to .run, and running it like:
$stdout = outstream
render('file2.erb')
But for some reason the results in the output:
Start
Some Text
End
Some Text
Note the extra "Some Text" at the end.
Solution 1:[1]
As you already poined, since we are not outputing the render method return value with <%= the in first case we are only getting Some text in output the rest is just used in processing.
In second when you used run with template which is causing the result to run twice, first time for template which is called in first script file and then second time for template which is called inside file2.erb render.
To solve the problem you can capture the output in a variable and then print the output from the captured variable as below:
file2.erb
<% @result = render('file1.erb') do -%>
<% "Some text\n" -%>
<% end -%>
<%= @result -%>
Output
Start
Some Text
End
Note:
You can use HEREDOC to put large amount of text between <% -%>
<% @result = render('file1.erb') do -%>
<%
<<~EOF
Some Text
EOF
-%>
<% end -%>
<%= @result -%>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
