Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot capture output of block in Rails helper

I've run into a problem with a custom Rails FormBuilder, which drives me crazy since yesterday evening. Basically I want to have an optional block to one of my builder methods, so that I can show additional content within my main content_tag:

def form_field(method, &block)
  content_tag(:div, class: 'field') do
    concat label(method, "Label #{method}")
    concat text_field(method)
    capture(&block) if block_given?
  end
end

When I call that method in one of my Slim templates, like so:

= f.form_field :email do
  small.help-text
    | Your email will never be public.

it inserts the block (here: the help text within <small>) above the actual output of the content_tag:

<small class="help-text">Your email will never be public.</small>
<div class="field">
    <label for="user_email">Label email</label>
    <input type="text" value="" name="user[email]" id="user_email">
</div>

I tried several other variants, but it seems that I can never capture the output of the block. Any ideas - and maybe even more interesting: explanations on this behaviour? I read several articles about that topic and also had a look at the Rails source, but couldn't really figure out why it's behaving like that.

like image 926
Christoph Lupprich Avatar asked May 04 '15 07:05

Christoph Lupprich


2 Answers

As @Kitto says, :capture, :concat and many more others helpers are implemented to @template.

In my customs FormBuilder, i have this :

module CustomFormBuilder < ActionView::Helpers::FormBuilder
  delegate :content_tag, :concat, to: :@template

  [ ... your code ... ]
end
like image 122
sebthemonster Avatar answered Nov 01 '22 09:11

sebthemonster


So, after some more digging-around, it turns out that the problem lies in the FormBuilder and how itself deals with output buffers. Looking into the source code for ActionView FormHelpers, gives a hint to call capture on @template, like so:

def form_field(method, &block)
  content = @template.capture(&block) if block_given?
  content_tag(:div, class: 'field') do
    concat label(method, "Label #{method}")
    concat text_field(method)
    concat content if content.present?
  end
end
like image 22
Christoph Lupprich Avatar answered Nov 01 '22 11:11

Christoph Lupprich