Problem using ERB blocks within content_for
njh opened this issue · 4 comments
I have been having trouble with not being able to use ERB blocks within a content_for
block. It is slightly tricky to describe in words, so I have created a example, which hopefully illustrates the problem. Please let me know if it isn't clear.
Test App
/config.ru
require 'roda'
require 'erubi'
class App < Roda
plugin :render, :escape => true
plugin :content_for
route do |r|
r.root do
@hash = {:a => 1, :b => 2, :c => 3}
view('homepage')
end
end
end
run App.freeze.app
/views/homepage.erb
<% content_for :page_title, 'Home' %>
<% content_for :head do %>
<link rel="canonical" href="http://www.example.com/" />
<% @hash.each_pair do |key,value| %>
<meta name="<%= key %>" content="<%= value %>" />
<% end %>
<% end %>
Hello World
/views/layout.erb
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title><%= content_for :page_title %></title>
<%== content_for :head %>
</head>
<body>
<h1><%= content_for :page_title %></h1>
<%== yield %>
</body>
</html>
Expected Result
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Home</title>
<link rel="canonical" href="http://www.example.com/" />
<meta name="a" content="1" />
<meta name="b" content="2" />
<meta name="c" content="3" />
</head>
<body>
<h1>Home</h1>
Hello World
</body>
</html>
Actual Result
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Home</title>
{:a=>1, :b=>2, :c=>3}
</head>
<body>
<h1>Home</h1>
Hello World
</body>
</html>
Dependency versions:
- erubi (1.7.1)
- rack (2.0.6)
- roda (3.14.0)
- tilt (2.0.8)
content_for
stores the value the block returns, and in your case that is the return value of hash.each_pair
, which is the hash itself. To workaround that, try adding an extra line before the end of the content_for
block and see if that fixes the issue.
It is possible to modify the content_for plugin to support always using the temporary template output buffer as the return value instead of the result of the block, but it would break when a non-ERB template engine is used. I suppose that could be made conditional on the template engine. Can you try this patch and let me know if it fixes your issue?
diff --git a/lib/roda/plugins/content_for.rb b/lib/roda/plugins/content_for.rb
index 993ff1f..974a105 100644
--- a/lib/roda/plugins/content_for.rb
+++ b/lib/roda/plugins/content_for.rb
@@ -63,6 +63,9 @@ class Roda
# Use temporary output buffer for ERB-based rendering systems
instance_variable_set(outvar, String.new)
value = Tilt[render_opts[:engine]].new{yield.to_s}.render
+ if render_opts[:engine] == 'erb'
+ value = instance_variable_get(outvar)
+ end
instance_variable_set(outvar, buf_was)
end
Thanks for the fast response.
Yes, an extra line before the end of the content_for
block does work.
I thought I had tried that before, but clearly not.
And your patch also works for me - I think it will make the behaviour in an ERB template less 'surprising'!
Unfortunately, the patch would break the case where you use a block normally without a template:
content_for(:foo){"bar"}
So I think the documentation needs to be clear that the content stored is the block output, and to discuss this issue and how to work around it.
Thanks Jeremy!
BTW, I built this website using Roda, Sequel and Erubi:
https://www.radiodns.uk/
Thanks for all the nice Open Source things you have made 🙂