Question

I would like to define some helper methods within a block helper but keep them within the scope of the block so that I can have sensible method names and it looks cleaner.

Say I want to do the following (a very basic example), in my view using helpers:

<%= foo_box do |b| %>
    <%= b.title( 'Foo Bar' ) %>
    Lorem Ipsum...
<% end %>

To produce something like

<div class="foo_box">
   <h2>Foo Bar</h2>
   Lorem Ipsum...
</div>

This way I could also have a bar_box block helper which could also have a title method which outputs something totally different.

Currently I have them implemented as different methods, e.g. foo_box and foo_box_title, with foo_box handling the block like so:

def foo_box(&block)
  content_tag(:div, capture(&block), :class => 'foo_box')
end
Was it helpful?

Solution

By the looks of it, capture doesn't let you pass any arguments into the block, which you want to do. The argument to bass in (b) has to be an object of some sort that you define the title etc. methods on. So, lets define a helper class which will output the stuff you need, and pass it to the block passed in from the view, and then capture it and surround it in the div as you did above. I would do something like this:

class FooBoxHelper
  include ActionView::Helpers::TagHelper  
  def title(text)
    content_tag(:h2, text)
  end
  def small(text)
    content_tag(:p, text, :class => "small")
  end
end

def foo_box(&block)

  new_block = Proc.new do 
    helper = FooBoxHelper.new
    block.call(helper)
  end
  content_tag(:div, capture(&new_block), :class => 'foo_box')
end

See how I got around the limitation of the capture method? By binding a new Proc to the current scope, and passing in the helper object there, we get a block we can call with no arguments, and thus acceptable by capture. Also, make certain your helper class includes all the relevant helper methods from ActionView::Helpers. Hope this helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top