Handling Currency: Best Practices in Ruby on Rails

Handling Currency in Ruby on Rails: Best Practices

Patrick Karsh
3 min readSep 19, 2023

Currency handling in software applications is deceptively complex. Given the nuances of financial arithmetic, small mistakes can culminate into significant monetary discrepancies. Especially for businesses operating across borders, accurate and consistent currency handling becomes paramount. When working with Ruby on Rails, a popular web application framework, there are several best practices developers should follow. This article delves into these practices, supplemented with code examples.

Storing Currency: Opt for Integer

Currency values are often represented with decimals (e.g., $19.99). A common pitfall is to store such values as floats or even decimals. However, these types are susceptible to inaccuracies due to the nature of floating point arithmetic.

Instead, consider storing currency values as integers, representing the smallest currency unit (like cents for USD):

# Instead of 19.99, store 1999
price_in_cents = 1999

Arithmetic Precision with BigDecimal

For accurate arithmetic, Ruby’s `BigDecimal` class is invaluable. It offers precision which is especially crucial for financial calculations:

price = BigDecimal(price_in_cents.to_s) / 100

Database Storage: Choose Wisely

When setting up your database schema, use the `:integer` type to store currency values in the smallest units. If there’s a genuine need for decimal types, ensure you specify both precision and scale:

create_table :products do |t|
t.integer :price_cents
# or, if necessary:
t.decimal :price, precision: 10, scale: 2
end

Leverage Money Management Gems

The Rails community has blessed us with gems tailored to abstract money handling complexities:

Money gem: An abstraction for safely handling and converting money.

money = Money.new(1999, "USD")
money.format # => $19.99

Money-rails: If the Money gem forms the core of your money handling strategy, integrate it into Rails seamlessly with this gem. It automates many processes, including model validations and field mappings.

Formatting Currency for Display

Rails provides a handy method, `number_to_currency`, tailored for displaying currency values:


number_to_currency(19.99) # => $19.99

Validation is Key

Always validate user input to ensure the entered currency values conform to expected formats and ranges:

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

Embrace Localization

Different regions have distinct ways of representing currency. Ensure you respect these nuances:

# For a user in the U.S.
number_to_currency(19.99, locale: :us) # => $19.99
# For a user in Germany
number_to_currency(19.99, locale: :de) # => 19,99 €

Consistency Across Currencies

When managing multiple currencies, always couple the value with its respective currency:

price = Money.new(1999, “USD”)

Navigating Exchange Rates

If you venture into currency conversion, remain updated with exchange rates. Several third-party APIs facilitate this. But be vigilant about the rate’s timestamp. Historic rates might not reflect current market values.

The Float Caveat

Although floats are convenient, they can introduce imprecision. If you ever find yourself resorting to floats for money, tread carefully.

Rounding: A Strategy is Vital

Define a consistent rounding strategy. Remember, different methods have distinct implications:

price = 1.005
price.round(2) # => 1.01 using round half up

Audits and Logs

Financial discrepancies can be nightmarish. Maintain thorough logs of all monetary operations to trace any anomalies.

Testing: Your Financial Safety Net

Cement your currency handling logic with robust tests. Ensure every edge case is covered, and every operation results in expected outcomes.

# RSpec example
it "accurately calculates the product total" do
product = Product.new(price_cents: 1999, quantity: 2)
expect(product.total_cents).to eq(3998)
end

In conclusion, currency management in Ruby on Rails, while intricate, can be seamlessly handled by following the practices outlined above. Adopting these guidelines can safeguard your application from costly monetary errors, ensuring trustworthiness and precision in all financial dealings.

--

--

Patrick Karsh
Patrick Karsh

Written by Patrick Karsh

NYC-based Ruby on Rails and Javascript Engineer leveraging AI to explore Engineering. https://linktr.ee/patrickkarsh

Responses (1)