# RubyMoney money-rails for storing monetary values in Rails

> RubyMoney money-rails connects the money gem to Rails models, migrations, validations, and Rack applications. It suits applications that store minor currency units such as cents and need a Money object at the model boundary.

**RubyMoney/money-rails** — Integration of RubyMoney - Money with Rails

- Repository: https://github.com/RubyMoney/money-rails
- Stars: 1,892 · Forks: 386
- Language: Ruby
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/rubymoney-money-rails

## Why money-rails belongs in a Rails persistence layer

Money arithmetic carries more state than a plain integer. A Money object includes a monetary amount and a currency, so code that receives only cents has to depend on an external convention if it wants to format, compare, or convert that value correctly. money-rails addresses that gap by connecting the RubyMoney money gem to Rails.

The intended users are Ruby and Rails applications whose database records contain monetary values in minor units. The README uses a Product model with a `price_cents` column as its primary example. Once `monetize :price_cents` is declared, the record receives a `price` attribute containing a Money object. The stored representation remains based on the database column, while application code can use the attribute as Money.

This is narrower than a general currency conversion service. money-rails does not introduce exchange rates, payment processing, or a ledger by itself. Its documented scope is the boundary between Rails persistence and the money gem. That distinction matters for project selection: applications handling payment execution need additional systems, while applications storing and manipulating prices can evaluate this integration on its model and migration behavior.

## How monetize maps Rails fields to Money values

For ActiveRecord, the `monetize` macro derives a Money attribute name by removing the `_cents` suffix from the field name. Thus, `price_cents` produces `price`. This is a naming convention, not a change to the database column. The database continues to store the integer value, and the generated model attribute wraps it in Money.

A model can select a different attribute name with the `as` option:

```ruby
monetize :discount_subunit, as: "discount"
```

The `discount` attribute then wraps the value from `discount_subunit`. The README also documents monetizing calculated methods rather than database columns. In the Transaction example, `price_cents`, `tax_cents`, and `total_cents` are monetized, while `total_cents` is calculated from the first two values. The generated `total` method consequently returns Money.

Validation behavior is configurable at several levels. A field can use normal numericality options, use `subunit_numericality` for the stored minor-unit value, disable validation entirely with `disable_validation: true`, or disable only one validation by setting an option such as `numericality: false`. This flexibility is useful, but it also means schema and model declarations should be reviewed together. A valid Money-facing attribute is not enough if its persisted subunit value has unsuitable bounds.

## Installing money-rails and monetizing a Product

For a Rails application, the documented installation is to add the gem to the application Gemfile:

```bash
bundle add money-rails
```

Alternatively, install the gem directly:

```bash
gem install money-rails
```

Generate the money configuration initializer with:

```bash
bin/rails generate money_rails:initializer
```

The initializer is the documented place to define the default currency and other configuration parameters. After installation, a first ActiveRecord use requires a monetary column. The following example declares an integer-backed price field on a Product model:

```ruby
class Product < ActiveRecord::Base
  monetize :price_cents
end
```

Based on the README, the Product object should then expose `price` as a Money object. The underlying `price_cents` field remains available, and the README says the Money attribute supports comparisons and conversions. The generated initializer is also relevant to this first use because monetized fields use the Money library's global default currency unless configured otherwise.

For a Rack application without Rails, the README calls `MoneyRails::Hooks.init` during initialization. It does not provide a more complete standalone Rack setup in the displayed documentation, so that single initialization call is the documented starting point.

## ActiveRecord migrations for price, currency, and nil values

money-rails includes migration helpers intended to keep the storage design consistent. The `add_monetize` helper can add amount and currency columns, and it can be customized inside a `MoneyRails.configure` block. The README recommends configuring the helper for the application's common case and then reusing it across migrations.

A price field can be added directly to a migration:

```ruby
class MonetizeProduct < ActiveRecord::Migration
  def change
    add_monetize :products, :price
  end
end
```

The helper is reversible and can therefore be used inside `change`. When separate `up` and `down` methods are used, the documentation identifies `remove_monetize` as the corresponding removal helper. A `change_table` form is also available:

```ruby
def change
  change_table :products do |t|
    t.monetize :price
  end
end
```

By default, the generated currency field has a default of USD. The README says to override this by changing `default_currency` in the initializer and then running migrations. This is a migration concern, not merely a runtime setting.

Nullable monetary fields require explicit schema and model choices:

```ruby
monetize :optional_price_cents, allow_nil: true
```

The corresponding migration can apply nullability to amount, currency, or both:

```ruby
def change
  add_monetize :products,
    :optional_price,
    amount: { null: true, default: nil },
    currency: { null: true, default: nil }
end
```

Per-column options take precedence over top-level `null: true`. This is important because a field that accepts a nil Money value must also have a database representation that permits nil. The README shows that both `optional_price` and `optional_price_cents` then return nil after a blank assignment and save.

## Currency configuration, ranges, and Mongoid storage

Every monetized conversion uses the Money library's global default currency by default. The generated configuration can set it explicitly:

```ruby
MoneyRails.configure do |config|
  config.default_currency = :usd
end
```

The displayed README ends before its complete currency-option list, so it confirms the configuration key but does not establish all supported currency strategies. In particular, it does not document whether a particular deployment supports a specific per-record currency selection. Applications that depend on that behavior need to consult the rest of the project documentation before adoption.

Integer range is another concrete boundary. The README cites -2147483648 to +2147483647 for Postgres and recommends changing a column to `bigint` with limit 8 when larger amounts are expected:

```ruby
def change
  change_column :products, :price_cents, :integer, limit: 8
end
```

For Mongoid, money-rails takes a different route. Instead of the ActiveRecord `monetize` macro, Money is supplied as a field type. The documented model declares `field :price, type: Money`. The README shows Money assignment before saving and a stored money hash containing `cents` and `currency_iso` after saving. Normal Mongoid field options, including `index` and `default`, remain available. This support makes the project useful beyond ActiveRecord, although the examples do not establish feature parity between every Mongoid option and every ActiveRecord option.

## Practical limits, maintenance signals, and a direct alternative

The clearest limitation is the amount of schema convention involved. A Rails application adopting money-rails must coordinate a Money attribute, an integer amount field, a currency field where applicable, nullable columns, default currency configuration, and validation options. Getting only the model declaration right can still leave a migration default, integer range, or nullability policy inconsistent.

The project is not archived, and the last push was on 2026-09-29. That is a recent maintenance signal, but the repository has no GitHub releases. The absence of releases means the repository record does not provide a release cadence or a named upgrade path through GitHub release notes. Upgrade planning must therefore account for the fact that changes may be visible through the repository and `CHANGELOG.md` rather than a published GitHub release series. The repository uses the MIT license, so the source is permissively licensed, although compatibility with an application's dependencies and obligations remains a separate review.

A real alternative is to use the money gem without Rails integration. money-rails explicitly builds on money, but the `monetize` macro, migration helpers, Rails initializer, generated model attributes, and Rack hook are supplied by the integration layer. An application that does not use ActiveRecord or Mongoid and manages its own persistence can keep Money objects at its service boundary while handling conversion itself. money-rails is the better fit when schema-level Rails integration is desired. Plain money is the better fit when database mapping belongs outside the model integration.

## Reviewing money-rails before adding it to an application

The adoption decision should begin with the database schema rather than the Money API. Check whether each amount is stored as an integer in minor units, whether the column name follows the `_cents` convention or has an explicit `as` name, and whether the value can exceed the documented integer range. Then inspect the currency columns and their defaults. The migration helper's USD default and the initializer's global currency are separate concerns, and changing the initializer alone does not retroactively change existing migrations.

Validation deserves equal attention. Decide which attributes represent persisted values, which represent calculated methods, and whether each one should allow nil. Use `numericality` or `subunit_numericality` only where the intended unit matches the bound. A `greater_than_or_equal_to: 10000` applied to the Money-facing amount and the same bound applied to a subunit column do not describe the same range.

For Mongoid users, verify the actual `Money` field behavior against the application's Mongoid version and indexing requirements. For Rack users, confirm where `MoneyRails::Hooks.init` runs in the application lifecycle. For all integrations, inspect the dependency files and `CHANGELOG.md` when planning an upgrade, because the repository has no GitHub releases. On the available evidence, money-rails is appropriate for Rails applications that want Money-aware attributes backed by integer persistence, but it is not a substitute for payment processing, exchange-rate management, or a custom persistence mapping that the project does not cover.

## Conclusion

Adopt money-rails when monetary data is represented by integer minor units and the application needs Money-aware model attributes, migrations, and validations. Do not adopt it merely because a project already uses money, since the gem adds a Rails-specific persistence layer. Verify first that your database columns, nullable fields, currency defaults, and validation rules match the conventions configured in the application.

## FAQ

### How do I install money-rails in a Rails application?

The README documents `bundle add money-rails` for an application Gemfile or `gem install money-rails` for a direct installation. It also documents generating the configuration file with `bin/rails generate money_rails:initializer`.

### How does monetize create a Money attribute?

For an ActiveRecord column named `price_cents`, `monetize :price_cents` creates a `price` attribute containing a Money object. Use `as: "discount"` when the Money attribute should have a different name.

### Can money-rails store nullable monetary fields?

Yes. Set `allow_nil: true` on the model declaration and configure the migration with nullable amount and currency options, or use top-level `null: true` to apply nullability to both columns. The README states that blank assignments can then be saved.

### Does money-rails support Mongoid?

Yes. The README shows `field :price, type: Money` inside a Mongoid document and describes storage as a hash containing `cents` and `currency_iso`. Normal Mongoid field options such as `index` and `default` remain available.

## Sources

- [Issues](https://github.com/RubyMoney/money-rails/issues)
- [License: MIT](https://github.com/RubyMoney/money-rails/blob/main/LICENSE)
- [README](https://github.com/RubyMoney/money-rails/blob/main/README.md)
- [RubyMoney/money-rails on GitHub](https://github.com/RubyMoney/money-rails)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rubymoney-money-rails
