Inspired by DRY up your views (slides) at last week’s RailsConf, I’m trying to improve a view that contains far too much code.
The view needs to display a menu of tasks. Whether and how each task is displayed depends on a combination of the current user’s rights as well as what they had done previously.
With a big block of code for each task, the view file quickly became a mess:
<div>
<ul class='menu'>
<% if current_user.has_right_for?('foos') %>
<li>
<% if current_user.foo %>
<%= link_to 'Stop Foo', foo_url, :method => :delete %>
<% else %>
<% form_tag(foos_url, :name=>'add_foo',
:style=>"margin:0;padding:0;display:inline;") do %>
<%= hidden_field_tag :bar_id, @bar.id %>
<%= link_to_function "Start Foo", "document.add_foo.submit()" %>
<% end %>
<% end %>
</li>
</ul>
</div>
OK, so let’s move each task into a helper:
<div>
<ul class='menu'>
<%= start_or_stop_foo -%>
</ul>
</div>
def start_or_stop_foo(tag='li')
if current_user.has_right_for?('foos')
content_tag(tag, link_to('Stop Foo', foo_url, :method => :delete))
else
content_tag(tag) do
form_tag(foos_url, :name=>'add_foo',
:style=>"margin:0;padding:0;display:inline;") do
hidden_field_tag :bar_id, @bar.id
link_to_function "Start Foo", "document.add_foo.submit()"
end
end
end
end
This unfortunately doesn’t work. Both content_tag and form_tag assume that you’re calling them from an action view template. A fix has been applied to edge rails for content_tag, but we need one for form_tag:
From form_tag_helper.rb, replace:
concat(tag(:form, html_options, true) + method_tag, block.binding)
concat(content, block.binding)
concat("</form>", block.binding)
with:
form_tag = tag(:form, html_options, true) + method_tag
if block_is_within_action_view?(block)
concat(form_tag, block.binding)
concat(content, block.binding)
concat("</form>", block.binding)
else
form_tag + content + "</form>"
end
def block_is_within_action_view?(block)
eval("defined? _erbout", block.binding)
end
I don’t like patching Rails because it’s fragile. I have to remember to remove or re-apply the patch the next time I upgrade my Rails installation.
Is there another way of moving this code out of the view and into a helper?