Archives for category: Ruby & Rails

How many times have you done a Find in Project (Command-Shift-F) in TextMate and accidentally searched through your Rails development.log or your Xcode build files?

TextMate tries to read these massive files and your whole machine grinds to a halt. I finally got tired of this and figured out how to configure TextMate to automatically ignore these folders.

Go to TextMate’s Preferences -> Advanced -> Folder Pattern

Replace this default pattern:
!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$

with this pattern that adds an exclusion for folders named ‘build’or ‘log’:
!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))|build|log$

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?

Follow

Get every new post delivered to your Inbox.