Rails now validates options used in migration functions


There are some aspects of Rails that seem like they shouldn’t have existed in the first place! One such neglected piece of code is the fact that Rails silently ignores options that are not valid for a given migration function.

Before

For example, the following migration would have worked fine in Rails 7.0 or lower:

  # db/migrate/20221018030445_add_gender_to_patients.rb
  class AddGenderToPatients < ActiveRecord::Migration[7.0]
    def change
      add_column :patients, :gender, :integer, default: 10, hola: :bola
    end
  end

Though the option “hola” is completely invalid for the “add_column” function, Rails would have happily accepted it and created the migration file. This is because the “add_column” function accepts a variable number of options, and Rails would have simply ignored the invalid option.

This is a problem because it can lead to a lot of confusion. For example, if one misspells an option, Rails would not raise an error, and the knowledge of the option not being applied would not have been communicated back to the user. This can lead to wasted debugging time or even potential security risks among other issues.

After

Thanks to this PR Rails now raises an error if an invalid option is passed to a migration function. This is a much better experience for the user, as they will be immediately notified of the error and can fix it.

It must be noted that validation of the options will only be applied for new migrations that are created after the upgrade to Rails 7.1. For existing migrations, the validation will not be applied, and the migration will continue to work as before.

Existing migrations are identified by the version of the migration class. For example, the migration class in the example above is “ActiveRecord::Migration[7.0]”, where the version is “7.0”. If the migration class is “ActiveRecord::Migration[7.1]”, where the version is “7.1”, then the validation will be applied.

Let’s change the migration class to “ActiveRecord::Migration[7.1]” and run the migration again:

  class AddGenderToPatients < ActiveRecord::Migration[7.1]
    def change
      add_column :patients, :gender, :integer, default: 10, hola: :bola
    end
  end

To redo a migration simply run:

   rails db:migrate:redo VERSION=20221018030445 

Now an error is raised indicating the use of an invalid migration option.

  == 20221018030445 AddGenderToPatients: migrating ==============================
  -- add_column(:patients, :gender, :integer, {:default=>10, :hola=>:bola})
  rails aborted!
  StandardError: An error has occurred, this and all later migrations canceled:

  Unknown key: :hola. Valid keys are: :limit, :precision, :scale, :default, :null, :collation, :comment, :primary_key, :if_exists, :if_not_exists, :array, :using, :cast_as, :as, :type, :enum_type, :stored
  /Users/swaathi/code/work/edge_rails/db/migrate/20221018030445_add_gender_to_patients.rb:3:in `change'

  Caused by:
  ArgumentError: Unknown key: :hola. Valid keys are: :limit, :precision, :scale, :default, :null, :collation, :comment, :primary_key, :if_exists, :if_not_exists, :array, :using, :cast_as, :as, :type, :enum_type, :stored
  /Users/swaathi/code/work/edge_rails/db/migrate/20221018030445_add_gender_to_patients.rb:3:in `change'
  Tasks: TOP => db:migrate:redo
  (See full trace by running task with --trace)

The same validation is also applied to the “create_table” function. The valid migration options as of now are,

  • :limit
  • :precision
  • :scale
  • :default
  • :null
  • :collation
  • :comment
  • :primary_key
  • :if_exists
  • :if_not_exists

Right now the check is a complete manual effort and is not automated. This means that if a new option is added to a migration function, the validation will not be applied until the Rails team manually adds it to the list of valid options. This is a limitation of the current implementation, and we hope it improves in the future.

Join Our Newsletter