Help writing better block helpers
I basically use block helpers to avoid writing out conditions in my views. Sometimes I’d also like to avoid looping statements, but I don’t know how. For instance, in the case of displaying a table and its multiple rows. Currently, I might do something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # helper def table_rows_for(contents, &block) unless contents.blank? yield else text = "Currently no contents for this view." tr = content_tag(:tr, content_tag(:td, text, :colspan => '3')) concat(tr, block.binding) end end # view <table> <tr> <th>Title</th><th>Changed By</th><th>Changed When</th> </tr> <% table_rows_for somethings do %> <% somethings.each do |something| %> <!-- more ERB stuff --> <% end -%> <% end -%> </table> |
I would like to include the loop inside of the helper as well, since the contents are being passed, anyway. However, everything I’ve tried results in nil object errors. In a standard loop, the methods are qualified with an object variable (“something” in the example above), and that’s a lot of what I don’t know how to handle. I’d like to just pass the contents to a table_for helper, instead of a table_rows_for helper. I could do that by avoiding the block helper altogether, and construct the html manually in a regular helper, but I still want the html block visible in my view.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def table_for(contents, &block) unless contents.blank? start = "<table><tr><th>Title</th><th>Changed by</th><th>Changed when</th></tr>" concat(start, block.binding) contents.each do # don't know what to do here concat(capture(&block), block.binding) end concat('</table>', block.binding) else text = "Currently no contents for this view." tr = content_tag(:tr, content_tag(:td, text, :colspan => '3')) concat(tr, block.binding) end end |
That’s probably not even close, but inside of the contents loop is what I’m having trouble with. Concatenating the captured block seems logical to me. But even if it was right, I don’t know what to do in the ERB (the block I’d be passing). I can no longer do something.title because something now belongs to the helper. If anyone can decipher my issue, please feel free to enlighten me.

fallenrogue Saturday, 09 Jun, 2007 Posted at 06:33AM
I’m not entirely sure exactly what you’re trying to accomplish here but you can certainly pass a block to another block and have it execute there. It’s one of the magic points of Ruby. I usually provide instance variables when I’m going to forward them around in Rails (so @something instead of something) and by calling “yield” in the
block, the
yieldshould execute the next block… so if you wanted to do this…the helper might look like this…
Am I way off on what you were looking to do? Maybe you’ve already gone in another direction. Either way, good luck solving the problem!
Ryan Saturday, 09 Jun, 2007 Posted at 10:31PM
It’s close, but not quite. What you’ve explained is how I’m currently doing it. I’ll use your example to help explain…
OK… my problem lies within the inner_pass looping in the view code you’ve posted above. Ignore the outer_pass part, for now (altogether, actually). You’re passing @boxes to the inner_pass method, along with a block, which is what I’m doing. Now, instead of doing @boxes.each in the view, I wanted to do that in the helper, since @boxes was already being passed in, anyway. Essentially leaving me with:
I was trying to extract the looping to within the helper, basically. I’ve never tried passing a parameter back to the block via yield, but I think it can be done… something like…
I’ve actually never tried this—think it would work? Anyway, I won’t be working on this again until Monday, so we’ll see how it goes then. Thanks for the help…
fallenrogue Monday, 11 Jun, 2007 Posted at 08:08AM
OH!!!! Dude, you’re so close then… just establish that you want to accept a param back! Try this…
I believe that’s what you mean, right? Sorry for my confusion! :)
Ryan Monday, 11 Jun, 2007 Posted at 10:23AM
Works perfectly. I think the part that I was missing was the
|pass_to_me|param going back into the block. It’s hard to imagine me not trying that, though. I guess I had the wrong combination of attempts. Anyway, I have a bunch of fields I need, so I’m just returning the object as a param and using it as I was originally. Thanks again for the help.fallenrogue Monday, 11 Jun, 2007 Posted at 12:43PM
no problem, man. Good luck with the rest of the app!