I have seen some strange behavior when using rails with partial layouts plus a helper method coded as an iterator using the yield keyword. I am hoping someone can:
So if I create the following 3 things in my rails 3 app, I get unexpected output.
[UPDATE] I have tested the following combinations:
Rails 3.0.0 + erb (has this issue)
Rails 3.0.0 + haml (OK)
Rails 3.0.3 + erb (has this issue)
Rails 3.0.3 + haml (OK)
So maybe it's an erb vs. haml thing, but when I initially discovered this it was on haml templates. Hmmm....anyone know what's going on???
A) A main template that looks like this (app/views/main/index.html.erb)
<h1>Main#index</h1>
<p>This is content from main#index before the partial template rendering
<%= render :partial => "partial" %>
<p>This is content from main#index after the partial template rendering.</p>
B) A helper method like this (app/helpers/main_helper.rb)
module MainHelper
def my_iterator
yield 1
yield 2
yield 3
yield 4
end
end
C) A partial template like this (app/views/main/_partial.html.erb)
<% my_iterator do |x| %>
<p>iterator running with <%= x %></p>
<% end %>
When I view the result in the browser, I see the "iterator running with" block a total of 8 times (1 2 3 4 1 2 3 4). I have determined it is the yield within my_iterator screwing with the rails partial template mechanism. If I code my_iterator as follows, the output is as I would expect. (I also need to change my partial template to do my_iterator.each)
def my_iterator
logger.debug("my_iterator called")
return [1, 2, 3, 4]
end
Is there a way to code this such that I don't screw with rails and get duplicate rendering but can still code a helper method as an iterator using yield? Also, can someone explain exactly how the duplicate rendering happens?
I was just having a very similar issue and just managed to solve it. I believe the original helper above would work as expected if rewritten it as such...
module MainHelper
def my_iterator(&block)
block.call(1)
block.call(2)
block.call(3)
block.call(4)
end
end
The same problem seems to occur in Rails 3 if your calling concat(capture(&block))
or concat(block.call)
. In both cases, just drop the concat()
and the duplicate rendering vanishes.