Add reversible syntax for change_column_default

Passing `:from` and `:to` to `change_column_default` makes this command
reversible as user has defined its previous state.

So, instead of having the migration command as:

    change_column_default(:posts, :state, "draft")

They can write it as:

    change_column_default(:posts, :state, from: nil, to: "draft")
This commit is contained in:
Prem Sichanugrist 2015-05-04 16:11:15 -04:00
parent 0bee4100f1
commit a4128725f5
9 changed files with 64 additions and 7 deletions

@ -1,3 +1,15 @@
* Add alternate syntax to make `change_column_default` reversible.
User can pass in `:from` and `:to` to make `change_column_default` command
become reversible.
Example:
change_column_default :posts, :status, from: nil, to: "draft"
change_column_default :users, authorized, from: true, to: false
*Prem Sichanugrist*
* Prevent error when using `force_reload: true` on an unassigned polymorphic
belongs_to association.

@ -592,10 +592,11 @@ def change(column_name, type, options = {})
#
# t.change_default(:qualification, 'new')
# t.change_default(:authorized, 1)
# t.change_default(:status, from: nil, to: "draft")
#
# See SchemaStatements#change_column_default
def change_default(column_name, default)
@base.change_column_default(name, column_name, default)
def change_default(column_name, default_or_changes)
@base.change_column_default(name, column_name, default_or_changes)
end
# Removes the column(s) from the table definition.

@ -460,7 +460,12 @@ def change_column(table_name, column_name, type, options = {})
#
# change_column_default(:users, :email, nil)
#
def change_column_default(table_name, column_name, default)
# Passing a hash containing +:from+ and +:to+ will make this change
# reversible in migration:
#
# change_column_default(:posts, :state, from: nil, to: "draft")
#
def change_column_default(table_name, column_name, default_or_changes)
raise NotImplementedError, "change_column_default is not implemented"
end
@ -1068,6 +1073,14 @@ def validate_index_length!(table_name, new_name) # :nodoc:
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
end
end
def extract_new_default_value(default_or_changes)
if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
default_or_changes[:to]
else
default_or_changes
end
end
end
end
end

@ -629,7 +629,8 @@ def rename_index(table_name, old_name, new_name)
end
end
def change_column_default(table_name, column_name, default) #:nodoc:
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
default = extract_new_default_value(default_or_changes)
column = column_for(table_name, column_name)
change_column table_name, column_name, column.sql_type, :default => default
end

@ -410,11 +410,12 @@ def change_column(table_name, column_name, type, options = {}) #:nodoc:
end
# Changes the default value of a table column.
def change_column_default(table_name, column_name, default) # :nodoc:
def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
clear_cache!
column = column_for(table_name, column_name)
return unless column
default = extract_new_default_value(default_or_changes)
alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s"
if default.nil?
# <tt>DEFAULT NULL</tt> results in the same behavior as <tt>DROP DEFAULT</tt>. However, PostgreSQL will

@ -422,7 +422,9 @@ def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
end
end
def change_column_default(table_name, column_name, default) #:nodoc:
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
default = extract_new_default_value(default_or_changes)
alter_table(table_name) do |definition|
definition[column_name].default = default
end

@ -72,7 +72,7 @@ def respond_to?(*args) # :nodoc:
[:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
:rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
:change_column_default, :add_reference, :remove_reference, :transaction,
:add_reference, :remove_reference, :transaction,
:drop_join_table, :drop_table, :execute_block, :enable_extension,
:change_column, :execute, :remove_columns, :change_column_null,
:add_foreign_key, :remove_foreign_key
@ -166,6 +166,16 @@ def invert_remove_index(args)
alias :invert_add_belongs_to :invert_add_reference
alias :invert_remove_belongs_to :invert_remove_reference
def invert_change_column_default(args)
table, column, options = *args
unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
raise ActiveRecord::IrreversibleMigration, "change_column_default is only reversible if given a :from and :to option."
end
[:change_column_default, [table, column, from: options[:to], to: options[:from]]]
end
def invert_change_column_null(args)
args[2] = !args[2]
[:change_column_null, args]

@ -267,6 +267,13 @@ def test_change_column_default_to_null
assert_nil TestModel.new.first_name
end
def test_change_column_default_with_from_and_to
add_column "test_models", "first_name", :string
connection.change_column_default "test_models", "first_name", from: nil, to: "Tester"
assert_equal "Tester", TestModel.new.first_name
end
def test_remove_column_no_second_parameter_raises_exception
assert_raise(ArgumentError) { connection.remove_column("funny") }
end

@ -169,6 +169,16 @@ def test_invert_change_column_default
end
end
def test_invert_change_column_default_with_from_and_to
change = @recorder.inverse_of :change_column_default, [:table, :column, from: "old_value", to: "new_value"]
assert_equal [:change_column_default, [:table, :column, from: "new_value", to: "old_value"]], change
end
def test_invert_change_column_default_with_from_and_to_with_boolean
change = @recorder.inverse_of :change_column_default, [:table, :column, from: true, to: false]
assert_equal [:change_column_default, [:table, :column, from: false, to: true]], change
end
def test_invert_change_column_null
add = @recorder.inverse_of :change_column_null, [:table, :column, true]
assert_equal [:change_column_null, [:table, :column, false]], add