Class: RuboCop::Cop::DevDoc::Migration::AmountColumnInCents

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/dev_doc/migration/amount_column_in_cents.rb

Overview

Monetary columns must be stored as integer cents with an ‘_in_cents` suffix.

## Rationale Storing monetary values as floats or decimals introduces floating-point precision issues. The safe approach is to store values as integer cents and convert to dollars only in user-facing forms and displays.

This cop is heuristic: it matches column names whose last segment is a known monetary word (configurable via ‘MonetaryNames`). Extend the list in your `.rubocop.yml` if your domain uses different names.


t.float :amount
t.decimal :price
add_column :orders, :total, :decimal

✔️
t.integer :amount_in_cents
t.integer :price_in_cents
add_column :orders, :total_in_cents, :integer

To extend the monetary names list:

DevDoc/Migration/AmountColumnInCents:
  Enabled: true
  MonetaryNames:
    - amount
    - price
    - balance
    - cost
    - fee
    - total
    - subtotal
    - discount
    - tax
    - revenue   # custom addition

Examples:

# bad
t.float :amount
t.decimal :price
add_column :orders, :total, :decimal

# good
t.integer :amount_in_cents
t.integer :price_in_cents
add_column :orders, :total_in_cents, :integer

Constant Summary collapse

DEFAULT_MONETARY_NAMES =
%w[amount price balance cost fee total subtotal discount tax].freeze
MSG =
'Store monetary values as integer cents — ' \
'rename `%<name>s` to `%<name>s_in_cents` and use `t.integer`.'.freeze
COLUMN_METHODS =
%i[float decimal integer].freeze

Instance Method Summary collapse

Instance Method Details

#on_send(node) ⇒ Object



60
61
62
63
64
65
66
67
68
69
# File 'lib/rubocop/cop/dev_doc/migration/amount_column_in_cents.rb', line 60

def on_send(node)
  col_name_node = column_name_node(node)
  return unless col_name_node&.sym_type?

  col_name = col_name_node.value.to_s
  return if col_name.end_with?('_in_cents')
  return unless monetary_suffix?(col_name)

  add_offense(col_name_node, message: format(MSG, name: col_name))
end