Question

Handling Currency and Money in Ruby on Rails

rubyruby-on-railscurrency

Question

I'm building a basic shopping cart system in Ruby on Rails.

I have an items table with a price column stored as an integer.

I'm having trouble displaying prices correctly in my views when the value should include both euros and cents. Is there a standard or recommended way to handle currency values in Rails? Am I missing something obvious about how money should be stored and displayed?

Short Answer

By the end of this page, you'll understand the standard Rails approach to handling money: store values in the smallest currency unit (such as cents) using integers, then format them for display in views. You'll also learn why this is safer than using floating-point numbers, how to convert values correctly, and how this pattern is used in real Rails applications.

Concept

Money should be handled carefully in software because even tiny rounding errors can cause incorrect totals, taxes, or payment values.

In Rails applications, a very common approach is:

  • Store money as an integer in the smallest unit of the currency
    • Euros -> store cents
    • Dollars -> store cents
    • Pounds -> store pence
  • Format the integer for display when showing it to users

For example:

  • 1999 in the database means €19.99
  • 500 means €5.00
  • 49 means €0.49

This matters because floating-point numbers like 19.99 are not always stored exactly in binary. That can lead to surprising errors when adding or comparing values.

For example, in many languages:

0.1 + 0.2
# => 0.30000000000000004

That is fine for scientific calculations, but it is dangerous for money.

So the core idea is:

  1. Store exact values as integers
  2. Convert to a human-friendly currency string in the view
  3. Keep calculations in integers whenever possible

Rails helps with formatting through helpers such as number_to_currency.

Mental Model

Think of money like counting coins in a jar.

Instead of writing "19.99 euros" inside the database, you count the smallest pieces:

  • 19 euros and 99 cents
  • total = 1999 cents

The database stores the exact coin count.

Later, when you want to show the amount to a user, Rails acts like a cashier printing a receipt:

  • 1999 becomes €19.99

So:

  • Database value = exact internal amount
  • View output = readable currency format

This separation makes your code safer and easier to maintain.

Take Quiz

Syntax and Examples

A common Rails pattern is to store an integer column such as price_cents instead of price.

Model data example

Suppose your items table stores this:

Item.create(name: "Book", price_cents: 1299)

That means the book costs €12.99.

Displaying the value in a view

Rails provides the number_to_currency helper:

<%= number_to_currency(@item.price_cents / 100.0, unit: "€") %>

Output:

€12.99

Better model method

Instead of repeating conversion logic in views, define a method in the model:

class Item < ApplicationRecord
  def price_in_euros
    price_cents / 100.0
  end
end

Then in the view:

<%= number_to_currency(@item.price_in_euros, unit: "€") %>

Even better naming

Use a column name that makes the unit clear:

# good
price_cents

# less clear
price

When the column is called price_cents, developers immediately know the value is stored as cents, not euros.

Example with multiple items

@items = [
  OpenStruct.new(name: "Pen", price_cents: 150),
  OpenStruct.new(name: "Notebook", price_cents: 499),
  OpenStruct.new(name: "Bag", price_cents: 2599)
]
<% @items.each do |item| %>
  <p>
    <%= item.name %> - <%= number_to_currency(item.price_cents / 100.0, unit: "€") %>
  </p>
<% end %>

This would display readable prices while keeping the stored values exact.

Step by Step Execution

Consider this Ruby code:

price_cents = 1299
price_euros = price_cents / 100.0
formatted = ActionController::Base.helpers.number_to_currency(price_euros, unit: "€")

puts formatted

Step-by-step

  1. price_cents = 1299

    • The internal stored value is 1299
    • This means 1299 cents
  2. price_euros = price_cents / 100.0

    • 1299 / 100.0 becomes 12.99
    • Using 100.0 is important because it produces a decimal result
  3. number_to_currency(price_euros, unit: "€")

    • Rails formats the number as currency
    • It adds the currency symbol and correct decimal places
  4. puts formatted

    • Output is:
€12.99

Why not divide by 100?

In older Ruby code or certain integer-only situations:

1299 / 100
# => 12

That would lose the cents.

Using 100.0 ensures the result keeps the decimal part:

1299 / 100.0
# => 12.99

Real World Use Cases

Handling money this way appears in many real applications:

  • Shopping carts

    • Product prices are stored in cents
    • Cart totals are calculated exactly
  • Subscriptions

    • Monthly plans might be stored as 999 for €9.99
  • Invoices

    • Line items, tax, and totals must be precise
  • Payment gateways

    • Many APIs such as Stripe expect amounts in the smallest unit
    • Example: charge 2500 for €25.00
  • Discount systems

    • A coupon can subtract 500 cents instead of 5.00
  • Reports and exports

    • Internal reports can keep exact integer values
    • UI and PDFs can format them for human reading

This pattern is popular because it reduces ambiguity and prevents rounding surprises.

Take Quiz

Real Codebase Usage

In real Rails codebases, developers usually combine integer storage with a few clean design patterns.

Clear column naming

Use names like:

  • price_cents
  • subtotal_cents
  • tax_cents
  • total_cents

This avoids confusion about the unit.

Model helper methods

Developers often wrap conversion logic inside model methods:

class Item < ApplicationRecord
  def price
    price_cents / 100.0
  end
end

That keeps views simpler.

Guard clauses for missing values

class Item < ApplicationRecord
  def price
    return 0 if price_cents.nil?

    price_cents / 100.0
  end
end

This avoids errors when data is incomplete.

Validation

class Item < ApplicationRecord
  validates :price_cents, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
end

This ensures bad values are rejected.

Totals using integers

total_cents = cart_items.sum { |item| item.price_cents * item.quantity }

Then format at the end:

number_to_currency(total_cents / 100.0, unit: "€")

Presenter or helper methods

In larger apps, formatting may move into helpers or presenters:

module ItemsHelper
  def display_price(item)
    number_to_currency(item.price_cents / 100.0, unit: "€")
  end
end

This keeps formatting rules consistent across the app.

Money libraries

Some Rails projects use gems like money or money-rails when currency handling becomes more complex, especially for:

  • multiple currencies
  • exchange rates
  • localized formatting
  • taxes and financial reporting

For a basic shopping cart, integer cents plus Rails formatting is often enough.

Common Mistakes

1. Storing money as float

Broken approach:

# bad
price = 19.99

Why it is a problem:

  • floats can introduce rounding errors
  • totals may become inaccurate

Better:

price_cents = 1999

2. Using a vague column name

Broken approach:

# unclear
price

Why it is a problem:

  • other developers may assume price means euros, not cents

Better:

price_cents

3. Dividing integers incorrectly

Broken code:

price_cents = 1299
price_cents / 100
# => 12

Problem:

  • cents are lost

Better:

price_cents / 100.0
# => 12.99

4. Doing formatting logic everywhere in views

Messy approach:

<%= number_to_currency(item.price_cents / 100.0, unit: "€") %>
<%= number_to_currency(order.total_cents / 100.0, unit: "€") %>
<%= number_to_currency(invoice.tax_cents / 100.0, unit: "€") %>

Problem:

  • repeated logic
  • easy to make inconsistent changes later

Better:

  • move conversion into model methods or helpers

5. Mixing storage and display values

Problem example:

  • storing 19.99 in one place
  • storing 1999 in another

Why this is bad:

  • calculations become inconsistent
  • bugs become harder to trace

Better:

  • choose one internal format
  • for most Rails apps, use integer cents consistently

6. Ignoring nil values

Broken code:

item.price_cents / 100.0

If price_cents is nil, this raises an error.

Safer version:

(item.price_cents || 0) / 100.0

Comparisons

ApproachExample stored valueGood for money?Notes
Integer smallest unit1999YesBest common choice for carts and payments
Float19.99NoCan introduce rounding errors
Decimal / numeric19.99SometimesBetter than float, but integer cents is often simpler for app logic

Integer cents vs decimal columns

OptionAdvantageDisadvantage
Integer centsExact, simple, matches many payment APIsRequires dividing by 100 for display
Decimal columnHuman-readable in databaseStill requires careful handling and consistency

Plain output vs Rails helper

MethodExampleResult
Manual string building"€#{price_cents / 100.0}"Works, but formatting may be inconsistent
number_to_currencynumber_to_currency(price_cents / 100.0, unit: "€")Better formatting and clearer intent

For a beginner-friendly Rails app, the usual recommendation is:

  • store as integer cents
  • calculate using integers
  • display with number_to_currency
Take Quiz

Cheat Sheet

Recommended pattern

  • Store money as an integer in the smallest unit
  • Use names like price_cents
  • Convert only when displaying to users
  • Use Rails helpers for formatting

Example database meaning

  • 100 -> €1.00
  • 250 -> €2.50
  • 1999 -> €19.99

View formatting

<%= number_to_currency(@item.price_cents / 100.0, unit: "€") %>

Model method

def price_in_euros
  price_cents / 100.0
end

Validation

validates :price_cents, numericality: { only_integer: true, greater_than_or_equal_to: 0 }

Good rules

  • Use integer cents internally
  • Keep calculations in cents
  • Format at the edge of the app, usually in the view
  • Prefer clear names like total_cents

Avoid

  • float for money
  • unclear names like price when the value is really cents
  • integer division by 100
  • mixing formatted strings with calculations

FAQ

Why should I store money as cents in Rails?

Because integers are exact. This avoids floating-point rounding errors and makes totals more reliable.

Can I use a decimal column instead of an integer?

Yes, a decimal column is safer than a float. But storing cents as integers is often simpler and very common in shopping carts and payment systems.

How do I display 1999 as €19.99 in Rails?

Divide by 100.0 and use number_to_currency:

<%= number_to_currency(1999 / 100.0, unit: "€") %>

Should the database column be called price or price_cents?

price_cents is better because it clearly tells you what unit is stored.

Why is my division removing the cents?

You may be doing integer division, such as 1299 / 100. Use 1299 / 100.0 instead.

Does Rails have built-in money support?

Rails provides formatting helpers like number_to_currency, but it does not automatically solve all money storage rules. You still need a good storage strategy.

When should I use a gem for money handling?

Use a gem when you need features like multiple currencies, exchange rates, or more advanced financial logic.

Is number_to_currency enough for a simple cart?

Yes, for many simple apps it is enough when combined with integer cent storage.

Take Quiz

Mini Project

Description

Build a tiny Rails-style product pricing example that stores prices in cents and displays them in euros. This demonstrates the standard pattern used in shopping carts: exact integer storage for calculations, and formatted output for the user.

Goal

Create a product model with cent-based pricing, validate the value, and display formatted euro prices cleanly.

Requirements

  • Create a product object with a price_cents value stored as an integer.
  • Add a method that converts cents to euros.
  • Validate that price_cents is a non-negative integer.
  • Display the formatted price using a Rails currency helper.
  • Calculate a total for multiple quantities using cents first, then format the result.
Take Quiz

Keep learning

Related questions

View more