Rails partial template rendering repeatedly when a helper method uses the yield keyword
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:
- Explain 开发者_StackOverflow社区what's going on and why I get the duplicate rendering and maybe
- Suggest an alternate approach, hopefully other than just re-coding my helper method to be a simple function that returns a list (I've already done that as an interim workaround)
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.
I was experiencing a similar problem which only appeared in production, not development. I narrowed this down to
config.action_view.cache_template_loading = true
in my development.rb. When set to true, I saw a elements in my haml file. By adding and removing blocks I narrowed down the offending code to
= if @user.bio
= @user.bio
which was causing the entire block which preceded this to be re-printed. Simply replacing = with - solves this. It was awkward to debug simply because I had to do in debug in production mode.
My advice is to look for errant = outputs on logical operators.
- if @user.bio
= @user.bio
Using content_for within a helper will append content on each iteration. You can write a ApplicationHelper method to yield content within helpers like so:
def yield_content(content_key)
view_flow.content.delete(content_key)
end
Then use yield_content instead of content_for within your other helper files.
精彩评论