Minting::Rails

Minting::Rails brings Minting money objects to Active Record models.

It adds a money_attribute model helper, registers a :mint_money Active Record type, and includes small convenience methods such as 12.to_money(:USD), 12.dollars, and '12.00'.mint(:BRL).

What it does

  • Stores and reads model attributes as Mint::Money objects.
  • Supports composed money attributes backed by amount and currency columns.
  • Normalizes numeric, string, and Mint::Money assignments.
  • Validates currencies against the currencies enabled in Minting.

Requirements

  • Ruby 3.3 or newer.
  • Rails 7.1.3.2 or newer.
  • Minting 1.0.0 or newer.

Installation

Add the gem to your Rails application's Gemfile:

gem 'minting-rails'

Install it:

bundle install

Generate the initializer:

bin/rails g mint:initializer

Configuration

Configure Minting in config/initializers/minting.rb:

Mint.configure do |config|
  config.enabled_currencies = :all
  config.default_currency = 'USD'
end

You can limit the currencies that may be used:

Mint.configure do |config|
  config.enabled_currencies = %w[USD EUR BRL]
  config.default_currency = 'USD'
end

You can also register custom currencies before enabling or using them:

Mint.configure do |config|
  config.added_currencies = [
    { currency: 'CRC', subunit: 2, symbol: 'CRC' },
    { currency: 'NGN', subunit: 2, symbol: 'NGN' }
  ]

  config.enabled_currencies = :all
  config.default_currency = 'CRC'
end

The default currency must be registered and included in enabled_currencies.

Usage

Declare money attributes in your Active Record models with money_attribute.

Single-column fixed currency

Use this when a column always stores one currency, such as a price column that is always USD.

Migration:

class CreateProducts < ActiveRecord::Migration[7.1]
  def change
    create_table :products do |t|
      t.decimal :price
      t.decimal :discount

      t.timestamps
    end
  end
end

Model:

class Product < ApplicationRecord
  money_attribute :price, currency: 'USD'
  money_attribute :discount, currency: 'USD'
end

Assignments are normalized to Mint::Money:

product = Product.new(price: 12, discount: '3.50')

product.price
# => #<Mint::Money ... USD 12.00>

product.discount
# => #<Mint::Money ... USD 3.50>

Assigning a Mint::Money with a different currency raises ArgumentError:

Product.new(price: 12.to_money(:EUR))
# raises ArgumentError because the attribute only accepts USD

Amount and currency columns

Use this when each row can store a different currency per record.

Migration:

class CreateOffers < ActiveRecord::Migration[7.1]
  def change
    create_table :offers do |t|
      t.decimal :price_amount
      t.string :price_currency

      t.timestamps
    end
  end
end

Model:

class Offer < ApplicationRecord
  money_attribute :price
end

The attribute is composed from price_amount and price_currency:

offer = Offer.new(price: 15.to_money(:EUR))

offer.price
# => #<Mint::Money ... EUR 15.00>

offer.price_amount
# => 15.0

offer.price_currency
# => "EUR"

When you assign a plain number or string, Minting::Rails uses Mint.default_currency:

offer = Offer.new(price: '12')

offer.price.currency_code
# => "USD"

Custom column names

If your amount and currency columns do not follow the <name>_amount and <name>_currency convention, pass a mapping:

class Invoice < ApplicationRecord
  money_attribute :total, mapping: {
    total_amount: :amount,
    currency_code: :currency
  }
end

The mapping keys are your database columns. The values must identify which column stores the :amount and which stores the :currency.

Querying

Fixed-currency attributes can be queried with Mint::Money values:

Product.where(price: 15.to_money(:USD))

Composed attributes can also be queried with a money object:

Offer.where(price: 15.to_money(:EUR))

You can still query the backing columns directly when that is clearer:

Offer.where(price_amount: 15, price_currency: 'EUR')

Convenience methods

Minting::Rails adds a few small helpers:

12.to_money(:USD)
12.mint(:BRL)
12.dollars
12.euros
'12.50'.to_money(:USD)
'12.50'.mint(:BRL)

These return Mint::Money instances.

Development

Clone the repository and install dependencies:

bundle install

Run the test suite:

bundle exec rake test

The repository includes a dummy Rails application under test/dummy for exercising the engine in a Rails environment.

Releasing

Update the version in lib/minting/money_attribute/version.rb, update release notes, and build the gem:

gem build minting-rails.gemspec

Publishing is configured for RubyGems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at gferraz/minting-rails.

Before opening a pull request, please run:

bundle exec rake test

License

The gem is available as open source under the terms of the MIT License.