Debugging a money-rails migration

By Robert Kaufman, February 23, 2015 20:43

While working on a project that uses the money-rails gem, I ran in to an issue with a migration helper method. The error I got was not very helpful, and while it was tempting to give up and not use the helper, especially since it didn’t do very much, I decided to figure out what was going on.

I was creating a table that would have a money attribute, so the migration looked like this:

class CreateOrders < ActiveRecord::Migration
  def change
    create_table :orders do |t| :total
       # other columns

When I tried to run the migration, I got an error like this:

undefined method `to_sym' for nil:NilClass/Users/rob/project/db/migrate/20150204131758_create_orders.rb:4:in `block in change'

I looked up the definition of money in the gem. expands to a couple of t.column calls, in this file:

Note line 7, where OptionsExtractor.extract is called. I figured out that table.rb is expecting an array of length 5 to be returned here, but we’re actually getting an array of length 3. So it’s returning something like this: [:no_table, "spend_amount", {}], but really, it really should be [true, :no_table, “spend_amount", :integer, {:null=>false}]

This should be generating code like this:

t.column “spend_amount", :integer, {}

But actually it's doing

t.column {}

Which is not quite correct.

So now check out the OptionsExtractor class here:

On line 6, it starts with a default hash of values, makes a few changes, and then returns a subset of values. The problem is that there are no keys for :present and :type in the incoming hash. I tracked this down to our rails-money initializer written in the past:

MoneyRails.configure do |config|
  config.amount_column = {
    postfix: '_amount'

  config.currency_column = {
    postfix: '_currency_code'

This was set up so we our amount columns would be named like total_amount and total_currency_code rather than the default total_cents and total_currency.

Note that we specify only the postfix options for both the amount and currency columns. These options are overriding defaults in money-rails in configuration.rb lines 63-68 here:

Perhaps money-rails should be merging our custom options hash with their default options, but they aren’t. So, we can either copy the other default values to our initializer, or we could only redefine the keys we want to customize, rather than the whole hash. I opted for the latter, updating our initializer to the following:

MoneyRails.configure do |config|
  config.amount_column[:postfix] =  '_amount'
  config.currency_column[:postfix] =  '_currency_code'

And now the helper works!