Benito Serna Tips and tools for Ruby on Rails developers

Create a new controller for the hidden abstraction and reduce conditionals

October 6, 2020

Have you ever implemented a feature that does work, but has resulted in using multiple conditionals in your controller and views… Wondering if there is a better way to do what you just did?

Maybe your problem is that you have a hidden abstraction and you need a new controller…

Do you have a hidden abstraction?

Not always, but some times, you will be able to simplify your code by creating a new abstraction… and a lot of the times you already know the name of it, and you are calling it by its name, but is not expressed in the code.

For example imagine that you are building an app that lets you create “Invoices”, and you want to add the possibility to create “Estimates"… but those estimates are not a new kind of record they are "just simple invoice records”, but with an estimate boolean attribute set to true.

Something like this…

class Invoice < ActiveRecord::Base
  has_many :line_items
end
invoice = Invoice.new
estimate = Invoice.new(estimate: true)

And to implement the feature you will have, in you views… a “New invoice” button and a “New estimate” button and both send you to the invoice controller like this (not a big trouble yet)…

<%= link_to "New invoice", new_invoice_path %>
<%= link_to "New estimate", new_invoice_path(estimate: true) %>

In the controller something like this…

def new
  if params[:estimate] == "true"
    @invoice = Invoice.new(estimate: true)
  else
    @invoice = Invoice.new
  end
end

And on your other views (show, form, and mailer views)…. You will have this all over the place…

<%= if @invoice.estimate? %>
  <h1>Estimate</h1>
<% else %>
  <h1>Invoice</h1>
<% end %>
<%= if @invoice.estimate? %>
  <%= link_to "Cancel", invoices_path(estimate: true) %>
<% else %>
  <%= link_to "Cancel", invoices_path %>
<% end %>

Is this your case?

If this is your case, then create a new controller

If you already are have a name for this thing, “Estimates”, you already have an abstraction, and maybe the first step that you can do is to create a new controller for it…

For example in this case you could have…

A “New estimate” button to a new dedicated route…

resources :invoices
resources :estimates
<%= link_to "New estimate", new_estimate_path %>

In the controller you will need just one object, and you will be able to use the expected name…

def new
  @estimate = Invoice.new(estimate: true)
end

And for you other views (show, form, and mailer views)…. You won’t need the conditionals any more…

<h1>Estimate</h1>
<%= link_to "Cancel", estimates_path %>

Maybe you can think that you are repeating to much… but normally duplication is far cheaper than the wrong abstraction.

Give it a try =)

Related articles

Weekly tips and tools for Ruby on Rails developers

I send an email each week, trying to share knowledge and fixes to common problems and struggles for ruby on rails developers, like How to fetch the latest-N-of-each record or How to test that an specific mail was sent or a Capybara cheatsheet. You can see more examples on Most recent posts or All post by topic.