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:
1999in the database means €19.99500means €5.0049means €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:
- Store exact values as integers
- Convert to a human-friendly currency string in the view
- 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:
1999becomes€19.99
So:
- Database value = exact internal amount
- View output = readable currency format
This separation makes your code safer and easier to maintain.
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
-
price_cents = 1299- The internal stored value is
1299 - This means 1299 cents
- The internal stored value is
-
price_euros = price_cents / 100.01299 / 100.0becomes12.99- Using
100.0is important because it produces a decimal result
-
number_to_currency(price_euros, unit: "€")- Rails formats the number as currency
- It adds the currency symbol and correct decimal places
-
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
999for €9.99
- Monthly plans might be stored as
-
Invoices
- Line items, tax, and totals must be precise
-
Payment gateways
- Many APIs such as Stripe expect amounts in the smallest unit
- Example: charge
2500for €25.00
-
Discount systems
- A coupon can subtract
500cents instead of5.00
- A coupon can subtract
-
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.
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_centssubtotal_centstax_centstotal_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
pricemeans 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.99in one place - storing
1999in 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
| Approach | Example stored value | Good for money? | Notes |
|---|---|---|---|
| Integer smallest unit | 1999 | Yes | Best common choice for carts and payments |
| Float | 19.99 | No | Can introduce rounding errors |
| Decimal / numeric | 19.99 | Sometimes | Better than float, but integer cents is often simpler for app logic |
Integer cents vs decimal columns
| Option | Advantage | Disadvantage |
|---|---|---|
| Integer cents | Exact, simple, matches many payment APIs | Requires dividing by 100 for display |
| Decimal column | Human-readable in database | Still requires careful handling and consistency |
Plain output vs Rails helper
| Method | Example | Result |
|---|---|---|
| Manual string building | "€#{price_cents / 100.0}" | Works, but formatting may be inconsistent |
number_to_currency | number_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
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.00250-> €2.501999-> €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
floatfor money- unclear names like
pricewhen 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.
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_centsvalue stored as an integer. - Add a method that converts cents to euros.
- Validate that
price_centsis 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.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.