# How would you refactor this case statement?

I've got this code in my ApplicationHelper file:

def new_button
case controller_name
when 'cars'
"<li class='has-form'><a class='button' href='#{new_car_path}'>New Car</a></li>".html_safe
when 'trucks'
"<li class='has-form'><a class='button' href='#{new_truck_path}'>New Truck</a></li>".html_safe
when 'mopeds'
"<li class='has-form'><a class='button' href='#{new_moped_path}'>New Moped</a></li>".html_safe
else
nil
end
end


So I can just put <%= new_button %> in the view, to display the appropriate button based on the controller_name being accessed.

I have about 10 different controllers to select from (and I'm sure that collection will grow), so the code is getting a bit lengthy.

Is there a better way to accomplish this?

You can use the name of your current controller to dynamically generate your buttons. Controllers names are plural by convention in Rails, so you will want to get the singular version of your controller name.

singular = controller_name.singularize  # get the controller name & make it singular


You can generate path to the "new" action by using send to call a dynamic method name. If you are not using Rails resources, you can use url_for(controller: controller_name, action: "new") to accomplish the same thing.

path = send("new_#{singular}_path") # generate the URL for that item's new action


Similarly, you can dynamically generate the text for your buttons.

title = "New #{singular.titleize}"   # generate the text for the link


And finally, put it all together to generate your HTML:

"<li class='has-form'><a class='button' href='#{path}'>#{title}</a></li>".html_safe # return

• I also have an edit button with similar syntax & I found that the link could be generated with path = send("edit_#{singular}_path", item_id) if I send item_id into the method. – James Chevalier Jun 13 '14 at 16:00

I haven't run this code, but maybe something along these lines:

def new_button
content_tag(:li, class: "has-form") do
text = "New #{controller_name.singularize.titleize}"
path = { controller: controller_name, action: "new" }
end
end


Using content_tag and link_to is arguably cleaner, and they handle escaping automatically.

Letting the router generate the URL for you from a hash is preferable to doing string manipulation.

The text generation is perhaps inelegant, but simple. If you're using I18n elsewhere in this app I'd consider using that:

model_slug = controller_name.singularize
text = t("new_x", x: t("activerecord.models.#{model_slug}"))


But it's overkill if you're not using it for anything else.

For links, I prefer to use link_to as it is really clear to read. However, once more view code is concerned, instead of going all content_tag or typing in strings and using interpolation, which could lead to hard to find errors, and definitely hard to read code, I tend to prefer to use partials.

So your code would become something like

def new_button
single_item = controller_name.singular
render 'shared/new_button', title: "New #{single_item.capitalize}"
end


and in app/views/shared/_new_button.html.haml you write

%li.has-form
= link_to title, {action: :new}, class: 'button'


Notice for the path I only need to specify the action (which is in this case always the same), since rails will automatically fill in the current controller if it is missing.

Notice how simple it would now be to make the title dependent on translations, or give it as the only parameter to new_button as it is (in the communication to the client/user) the only thing that matters (or could change easily).

• Nice. I do agree content_tag can get a little noisy. – Henrik N Jun 27 '14 at 18:01