Introduction

The Rails app I am building is in the finance sector and as a result has lots of currencies and percentages. The challenge is that the database stores these values as floats (i.e. decimals) and as a result I need to convert these raw values into something presentable on the frontend.

For example, a currency value in the database of 50.12 becomes $50 when presented to the user in the HTML. Similarly, the percentage value of 0.0508 becomes 5.1% when presented to the user on the frontend.

The Wrong Solution

In my days as a novice, I would perform this conversion right in the view like so:

app/views/companies/show.html.erb

<ul>
  <li>Budget: <%= number_to_currency(@company.budget, precision: 0) %></li>
  <li>Actual: <%= number_to_currency(@company.actual, precision: 0) %></li>
  <li>Percent Spent: <%= number_to_percentage(@company.percent_budget_spent * 100, precision: 1) %></li>
</ul>

Which produces:

Budget: $12,000
Actual: $11,485
Percent Spent: 5.1%

While this solves the problem, it is a poor solution. Anytime I want to display any of these three values on the page, I’ll need to duplicate the formatting logic, including each data’s:

When my client calls up and says “Let’s display all percentages in the application as 2 decimal places” (by the way this actually happened), I would need to find all the places in my app using the number_to_percentage helper method and adjust the precision.

A Better Solution: Decorators

Code With Me

If you’re like me, learning is best accomplished through actually writing code. I’ve setup a basic Rails app for you to code alongside me. Just fork it and clone it down: Decorator App

Then in terminal run:

bundle
bundle exec rake db:create db:migrate

Draper

Nowadays I use a gem called Draper so that I don’t have to put these helper methods all throughout my views. Here’s how it works:

Gemfile:

gem 'draper'

In terminal:

bundle
rails g decorator Company

This generates the company decorator file, which we update to include budget, actual, and percent_budget_spent methods:

# app/decorators/company_decorator.rb

class CompanyDecorator < Draper::Decorator
  delegate_all

  def budget
    h.number_to_currency(object.budget, precision: 0)
  end

  def actual
    h.number_to_currency(object.actual, precision: 0)
  end

  def percent_budget_spent
    h.number_to_percentage(object.percent_budget_spent * 100, precision: 1)
  end
end

The last step is to “decorate” our ActiveRecord object. Find companies_helper.rb and change it like so:

module CompaniesHelper
  def company
    @decorated_company ||= @company.decorate
  end
end

Now instead of referencing @company in our views, we simply call company to get the decorated company.

Change app/views/companies/show.html.erb to look like:

<ul>
  <li>Budget: <%= company.budget %></li>
  <li>Actual: <%= company.actual %></li>
  <li>Percent Spent: <%= company.percent_budget_spent %></li>
</ul>

Which turns into…

Budget: $12,000
Actual: $11,485
Percent Spent: 5.1%

Voila! This is a much cleaner and scalable approach. If we ever need to use budget, actual, or percent_budget_spent elsewhere on the page, we can do so without duplicating the helper method.

However, we run into trouble when we go to create our first form…

Forms Trouble

<%= simple_form_for company do |f| %>
  <%= f.input :name %>
  <%= f.input :budget, as: :string %>
  <%= f.input :actual, as: :string %>
  <%= f.submit %>
<% end %>

I want the budget field to look like this:

But it actually looks like this…

Do you see the dollar sign sitting inside the text field? This will pose a problem when we submit the form, as Rails will try to convert $15,000 to a float which results in nil. So why is it there?

Simple Form, like form_for, looks for a method on our object called budget. Since company is actually a CompanyDecorator object, it calls the budget method that was defined there first (if it doesn’t find that method on the decorator, then it goes on to call the method on the ActiveRecord object).

Remember, we did define a budget method on the decorator:

def budget
  h.number_to_currency(object.budget, precision: 0)
end

So our form is calling this method which is returning a currency formatted string. Unfortunately, this is not what we want.

As it turns out, we don’t always want to format budget as a currency. In this case of the form field, we want to format it as a decimal.

My solution to this is to use two decorator methods for each column that needs formatted. So for budget we have a budget method that returns a formatted decimal and a display_budget method that returns a formatted currency.

# app/decorators/company_decorator.rb

class CompanyDecorator < Draper::Decorator
  delegate_all

  def budget
    h.number_with_precision(object.budget, precision: 2)
  end

  def display_budget
    h.number_to_currency(object.budget, precision: 0)
  end

  def actual
    h.number_with_precision(object.actual, precision: 2)
  end

  def display_actual
    h.number_to_currency(object.actual, precision: 0)
  end

  def display_percent_budget_spent
    h.number_to_percentage(object.percent_budget_spent, precision: 1)
  end

  def percent_budget_spent
    h.number_with_precision(object.percent_budget_spent, precision: 1)
  end
end

This allows me to use the following methods and receive the following corresponding outputs:

<%= company.budget %>  =>  1200.00
<%= company.display_budget %>  =>  $1,200

<%= company.actual %>  =>  1000.00
<%= company.display_actual %>  =>  $1,000

<%= company.percent_budget_spent %>  =>  5.1
<%= company.display_percent_budget_spent %>  =>  5.1%

Simple Form will use the budget method, and will therefore be formatted with the number_with_precision helper method. Everywhere else in our views we can use company.display_budget to render out the currency value.

Now we update app/views/companies/show.html.erb:

<ul>
  <li>Budget: <%= company.display_budget %></li>
  <li>Actual: <%= company.display_actual %></li>
  <li>Percent Spent: <%= company.display_percent_budget_spent %></li>
</ul>

Which produces:

Budget: $12,000
Actual: $11,485
Percent Spent: 5.1%

While I find this to be quite effective, it is cumbersome to write two decorator methods for every currency or percentage field in our application.

The Best Solution: Base Decorator

Often times the way we format data like currencies and percentages is consistent across the application. Because of this, it would be great to be able to write our decorator like this:

class CompanyDecorator < BaseDecorator
  delegate_all

  currency :budget, :actual
  percent :percent_budget_spent
end

Which should expose the following methods:

<%= company.budget %>  =>  1200.00
<%= company.display_budget %>  =>  $1,200

<%= company.actual %>  =>  1000.00
<%= company.display_actual %>  =>  $1,000

<%= company.percent_budget_spent %>  =>  5.1
<%= company.display_actual %>  =>  5.1%

Looks pretty cool, you say. But how?

Step 1: Create the Base Decorator

Create a Base Decorator file that all of our decorators will inherit from:

# app/decorators/base_decorator.rb

class BaseDecorator < Draper::Decorator
  delegate_all
end

And inherit from it…

# app/decorators/company_decorator.rb

class CompanyDecorator < BaseDecorator
  delegate_all
end

Step 2: Create the Currency Method

The goal is for the following:

class CompanyDecorator < BaseDecorator
  delegate_all

  currency :budget
end

To end up producing something like:

class CompanyDecorator < BaseDecorator
  delegate_all

  def budget
    h.number_with_precision(object.budget, precision: 2)
  end

  def display_budget
    h.number_to_currency(object.budget, precision: 0)
  end
end

With that in mind, let’s take a stab at implementing some meta programming to solve this.

Start by defining a class method currency on our BaseDecorator:

class BaseDecorator < Draper::Decorator
  delegate_all

  def self.currency(*keys)
  end
end

*keys will end up being an array of keys:

[:budget, :actual]

So what we want to do is iterate over each of these keys and define two methods, the first which is just the name of the key and returns a decimal format, and the second which prepends display_ to the name of the key and is a currency format:

class BaseDecorator < Draper::Decorator
  delegate_all

  def self.currency(*keys)
    keys.each do |key|
      define_method(key) do
      end

      define_method("display_#{key}") do
      end
    end
  end
end

In the first define_method block, we want to implement something like:

h.number_with_precision(object.budget, precision: 2)

While the second should look like:

h.number_to_currency(object.budget, precision: 0)

However, since the key is a variable, we’ll need to use the send method:

class BaseDecorator < Draper::Decorator
  delegate_all

  def self.currency(*keys)
    keys.each do |key|
      define_method(key) do
        h.number_with_precision(object.send(key), precision: 2)
      end

      define_method("display_#{key}") do
        h.number_to_currency(object.send(key), precision: 0)
      end
    end
  end
end

Now in our CompanyDecorator, let’s call the newly added currency method:

class CompanyDecorator < BaseDecorator
  delegate_all

  currency :budget, :actual  
end

With that, the following methods produce these results:

<%= company.budget %>  =>  1200.00
<%= company.display_budget %>  =>  $1,200

<%= company.actual %>  =>  1000.00
<%= company.display_actual %>  =>  $1,000

Perfect.

Now only one more step: the percent method.

Step 3: Implement the Percent Method

I encourage you to try to implement this method on your own. The goal is that you come away with a pattern you can use in your Rails application. Likely your formatting rules and preferences will be different, so understanding how to create these Base Decorator methods is important.

In case you get stuck, here is the solution I came up with, which of course goes into the BaseDecorator file:

def self.percent(*keys)
  keys.each do |key|    
    define_method(key) do
      h.number_with_precision(object.send(key) * 100.0, precision: 1)
    end

    define_method("display_#{key}") do
      h.number_to_percentage(object.send(column_name) * 100.0, precision: 1)
    end
  end
end

Ryan Francis

Partner, President, & Head of Sales

As ambitious as he is tall, Ryan has a passion for creation. In 2012, he created Francis Lofts & Bunks, a company in Western Ohio that manufactures aluminum loft beds and bunk beds. Equipped with a burning desire to build things that are useful to others, Ryan has come into his own in web development, combining creativity, logic, and an empathy for others to help clients launch outstanding, easy-to-use products.

Reach Out

Ready to Build Something Great?

Partner with us to develop technology to grow your business.

Get our latest articles delivered to your inbox