money-rails

repository·main·Indexed 23 days ago

https://github.com/rubymoney/money-rails

An integration library that connects the `money` gem with Ruby on Rails and Mongoid. It provides the `monetize` macro to treat database columns as Money objects, reversible migration helpers like `add_monetize` and `remove_monetize`, view helpers for currency formatting, and a specialized `MoneyValidator` for numericality and currency format validation.

Tokens
5.6K
Snippets
10
Records
41
Agent score
83%

What's inside money-rails

  1. What is money-rails?

    main
    money-rails is an integration library that connects the money gem with Ruby on Rails. It allows you to use the monetize method to designate specific database fields to be handled as Money objects, providing specialized helpers and logic for currency management within your Rails models.
  2. Configure currencies in MoneyRails

    main

    Money-Rails allows you to manage currency settings at several levels of granularity:

    1. Global Default: Set via config.default_currency in an initializer. You can also use a lambda for multi-tenant apps.
    2. Model Level: Use register_currency :iso_code within an ActiveRecord model to set a default currency for all monetized attributes in that model.
    3. Attribute Level: Use the with_currency option in the monetize macro to specify a currency for a specific attribute. This can be a symbol, string, or a callable object (lambda/class) that accepts the model instance.
    4. Instance Level: If your table has a currency column, use with_model_currency: :currency in the monetize macro. This gives the highest precedence, allowing each record to have its own currency.
    # 1. Global Default (config/initializers/money.rb)
    MoneyRails.configure do |config|
      config.default_currency = :usd
      # For multi-tenant
      config.default_currency = -> { Tenant.current.default_currency }
    end
    
    # 2. Model Level
    class Product < ActiveRecord::Base
      register_currency :eur
      monetize :price_cents
    end
    
    # 3. Attribute Level
    class Product < ActiveRecord::Base
      register_currency :eur
      monetize :discount_subunit, as: "discount"
      monetize :bonus_cents, with_currency: :gbp
      
      # Using a callable for dynamic attribute currency
      monetize :delivery_fee_cents, with_currency: ->(product) { :gbp }
    end
    
    # 4. Instance Level
    class Transaction < ActiveRecord::Base
      register_currency :gbp
      monetize :amount_cents, with_model_currency: :currency
    end
    
    t = Transaction.new(amount_cents: 2500, currency: "CAD")
    t.amount # => Money object with CAD
  3. Install money-rails

    main

    To add money-rails to your Rails application, add the gem to your Gemfile using Bundler or install it directly via the gem command.

    For non-Rails rack-based applications, you must manually initialize the library during your application's boot process by calling MoneyRails::Hooks.init.

    bundle add money-rails

    Or

    $ gem install money-rails

    For rack-based applications without Rails

    MoneyRails::Hooks.init
  4. Use `add_monetize` migration helpers

    main

    Money-Rails provides reversible migration helpers to add monetized columns to your database. You can use add_monetize to add a money field or remove_monetize if writing separate up and down methods. These helpers can be customized in a MoneyRails.configure block.

    When using add_monetize, you can specify options for the currency column, such as currency: { present: false } if you do not want a separate currency column.

    class MonetizeProduct < ActiveRecord::Migration
      def change
        add_monetize :products, :price
    
        # OR
    
        change_table :products do |t|
          t.monetize :price
        end
      end
    end
    
    # Example without a currency column
    class MonetizeItem < ActiveRecord::Migration
      def change
        add_monetize :items, :price, currency: { present: false }
      end
    end
  5. Test monetized attributes with RSpec or Minitest

    main

    Money-Rails provides test helpers to verify that attributes are correctly monetized.

    RSpec: Require money-rails/test_helpers in your spec_helper.rb. Minitest: Require money-rails/test_helpers and include MoneyRails::TestHelpers in your test class.

    You can use the monetize matcher to check for column mapping, nil allowance, custom names, specific currencies, or model-based currency columns.

  6. Configure money-rails with an initializer

    main

    You can generate a configuration initializer to define the default currency and other global settings for your Rails application using the Rails generator:

    bin/rails generate money_rails:initializer

    This creates a file where you can manage configuration parameters such as the default currency value.

    $ bin/rails generate money_rails:initializer
  7. Configure MoneyRails via `MoneyRails.configure`

    main

    The MoneyRails.configure block in an initializer is used to set global behaviors. Key configuration parameters include:

    • default_currency: The application-wide default currency.
    • include_validations: Whether to automatically include numericality validations (default: true).
    • amount_column: Configuration for the subunit column (prefix, postfix, type, etc.).
    • currency_column: Configuration for the currency column (prefix, postfix, type, etc.).
    • rounding_mode: Set the BigDecimal rounding mode.
    • raise_error_on_money_parsing: Whether to raise errors when assigning wrong currencies.
    # config/initializers/money.rb
    MoneyRails.configure do |config|
      config.default_currency = :usd
      config.include_validations = true
      config.rounding_mode = BigDecimal::ROUND_HALF_UP
      config.raise_error_on_money_parsing = true
    
      config.amount_column = {
        prefix: '',
        postfix: '_cents',
        type: :integer,
        present: true,
        null: false,
        default: 0
      }
    
      config.currency_column = {
        prefix: '',
        postfix: '_currency',
        type: :string,
        present: true,
        null: false,
        default: 'USD'
      }
    end
  8. How Money objects are stored in Mongoid

    main

    When using Money-Rails with Mongoid, Money objects are converted into a database-friendly hash format. This allows Mongoid to store the monetary value and its currency within a single document field.

    An instance of Money is converted to a hash containing:

    • cents: The amount converted to a float.
    • currency_iso: The ISO currency code.

    Example of the stored structure:

    {
      "cents": 100.0,
      "currency_iso": "USD"
    }
  9. Integrate Money with RailsAdmin

    main

    The money-rails gem provides a custom field type for RailsAdmin that allows money fields to be handled as Decimal types while supporting humanized formatting. Once the gem is installed, the Money field type is automatically registered with RailsAdmin.

    You can use the :pretty_value instance option in your RailsAdmin configuration to display money values using humanized formatting (including the currency symbol) instead of the raw decimal value.

  10. Use MoneyRails view helpers

    main

    Money-Rails provides several helpers to format money objects for display in views:

    HelperResult Example
    currency_symbol<span>$</span>
    humanized_money(money)6.50
    humanized_money_with_symbol(money)$6.50
    money_without_cents(money)6
    money_without_cents_and_with_symbol(money)$6
    money_only_cents(money)50

    Note: humanized_money and humanized_money_with_symbol will hide cents if they are zero, unless config.no_cents_if_whole is set to false in your configuration.

  11. Use Money fields in Mongoid

    main

    In Mongoid, you can use Money directly as a field type in your document definitions. This allows the field to store both the cents and the currency information.

    class Product
      include Mongoid::Document
    
      field :price, type: Money
    end
    
    obj = Product.new
    obj.price = Money.new(100, 'EUR')
    obj.save
    
    # Accessing the underlying hash
    obj[:price] # => {cents: 100, currency_iso: "EUR"}