Invertable methods should have compatible method signature

Otherwise it is hard to invert non-kwargs method to kwargs method.

This makes table and column options to be synchronized as kwargs.
This commit is contained in:
Ryuta Kamizono 2020-01-20 05:28:08 +09:00
parent 01e248b4d0
commit 5572df92be
8 changed files with 38 additions and 43 deletions

@ -364,7 +364,6 @@ def [](name)
def column(name, type, **options) def column(name, type, **options)
name = name.to_s name = name.to_s
type = type.to_sym if type type = type.to_sym if type
options = options.dup
if @columns_hash[name] if @columns_hash[name]
if @columns_hash[name].primary_key? if @columns_hash[name].primary_key?
@ -550,7 +549,7 @@ def column(column_name, type, **options)
# t.string(:name) unless t.column_exists?(:name, :string) # t.string(:name) unless t.column_exists?(:name, :string)
# #
# See {connection.column_exists?}[rdoc-ref:SchemaStatements#column_exists?] # See {connection.column_exists?}[rdoc-ref:SchemaStatements#column_exists?]
def column_exists?(column_name, type = nil, options = {}) def column_exists?(column_name, type = nil, **options)
@base.column_exists?(name, column_name, type, **options) @base.column_exists?(name, column_name, type, **options)
end end
@ -591,8 +590,8 @@ def rename_index(index_name, new_index_name)
# t.timestamps(null: false) # t.timestamps(null: false)
# #
# See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps] # See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
def timestamps(options = {}) def timestamps(**options)
@base.add_timestamps(name, options) @base.add_timestamps(name, **options)
end end
# Changes the column's definition according to the new options. # Changes the column's definition according to the new options.
@ -622,8 +621,8 @@ def change_default(column_name, default_or_changes)
# t.remove(:qualification, :experience) # t.remove(:qualification, :experience)
# #
# See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns] # See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns]
def remove(*column_names) def remove(*column_names, **options)
@base.remove_columns(name, *column_names) @base.remove_columns(name, *column_names, **options)
end end
# Removes the given index from the table. # Removes the given index from the table.
@ -643,8 +642,8 @@ def remove_index(column_name = nil, options = {})
# t.remove_timestamps # t.remove_timestamps
# #
# See {connection.remove_timestamps}[rdoc-ref:SchemaStatements#remove_timestamps] # See {connection.remove_timestamps}[rdoc-ref:SchemaStatements#remove_timestamps]
def remove_timestamps(options = {}) def remove_timestamps(**options)
@base.remove_timestamps(name, options) @base.remove_timestamps(name, **options)
end end
# Renames a column. # Renames a column.

@ -392,7 +392,7 @@ def create_join_table(table_1, table_2, column_options: {}, **options)
# Although this command ignores the block if one is given, it can be helpful # Although this command ignores the block if one is given, it can be helpful
# to provide one in a migration's +change+ method so it can be reverted. # to provide one in a migration's +change+ method so it can be reverted.
# In that case, the block will be used by #create_join_table. # In that case, the block will be used by #create_join_table.
def drop_join_table(table_1, table_2, options = {}) def drop_join_table(table_1, table_2, **options)
join_table_name = find_join_table_name(table_1, table_2, options) join_table_name = find_join_table_name(table_1, table_2, options)
drop_table(join_table_name) drop_table(join_table_name)
end end
@ -469,7 +469,7 @@ def drop_join_table(table_1, table_2, options = {})
# end # end
# #
# See also Table for details on all of the various column transformations. # See also Table for details on all of the various column transformations.
def change_table(table_name, options = {}) def change_table(table_name, **options)
if supports_bulk_alter? && options[:bulk] if supports_bulk_alter? && options[:bulk]
recorder = ActiveRecord::Migration::CommandRecorder.new(self) recorder = ActiveRecord::Migration::CommandRecorder.new(self)
yield update_table_definition(table_name, recorder) yield update_table_definition(table_name, recorder)
@ -499,7 +499,7 @@ def rename_table(table_name, new_name)
# Although this command ignores most +options+ and the block if one is given, # Although this command ignores most +options+ and the block if one is given,
# it can be helpful to provide these in a migration's +change+ method so it can be reverted. # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
# In that case, +options+ and the block will be used by #create_table. # In that case, +options+ and the block will be used by #create_table.
def drop_table(table_name, options = {}) def drop_table(table_name, **options)
schema_cache.clear_data_source_cache!(table_name.to_s) schema_cache.clear_data_source_cache!(table_name.to_s)
execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}" execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
end end
@ -600,12 +600,11 @@ def add_column(table_name, column_name, type, **options)
# +type+ and other column options can be passed to make migration reversible. # +type+ and other column options can be passed to make migration reversible.
# #
# remove_columns(:suppliers, :qualification, :experience, type: :string, null: false) # remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
def remove_columns(table_name, *column_names) def remove_columns(table_name, *column_names, **options)
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty? raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
column_options = column_names.extract_options! type = options.delete(:type)
type = column_options.delete(:type)
column_names.each do |column_name| column_names.each do |column_name|
remove_column(table_name, column_name, type, column_options) remove_column(table_name, column_name, type, **options)
end end
end end
@ -617,7 +616,7 @@ def remove_columns(table_name, *column_names)
# to provide these in a migration's +change+ method so it can be reverted. # to provide these in a migration's +change+ method so it can be reverted.
# In that case, +type+ and +options+ will be used by #add_column. # In that case, +type+ and +options+ will be used by #add_column.
# Indexes on the column are automatically removed. # Indexes on the column are automatically removed.
def remove_column(table_name, column_name, type = nil, options = {}) def remove_column(table_name, column_name, type = nil, **options)
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}" execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
end end
@ -1156,7 +1155,7 @@ def columns_for_distinct(columns, orders) # :nodoc:
# #
# add_timestamps(:suppliers, null: true) # add_timestamps(:suppliers, null: true)
# #
def add_timestamps(table_name, options = {}) def add_timestamps(table_name, **options)
options[:null] = false if options[:null].nil? options[:null] = false if options[:null].nil?
if !options.key?(:precision) && supports_datetime_with_precision? if !options.key?(:precision) && supports_datetime_with_precision?
@ -1171,7 +1170,7 @@ def add_timestamps(table_name, options = {})
# #
# remove_timestamps(:suppliers) # remove_timestamps(:suppliers)
# #
def remove_timestamps(table_name, options = {}) def remove_timestamps(table_name, **options)
remove_column table_name, :updated_at remove_column table_name, :updated_at
remove_column table_name, :created_at remove_column table_name, :created_at
end end

@ -70,7 +70,7 @@ def indexes(table_name)
end end
end end
def remove_column(table_name, column_name, type = nil, options = {}) def remove_column(table_name, column_name, type = nil, **options)
if foreign_key_exists?(table_name, column: column_name) if foreign_key_exists?(table_name, column: column_name)
remove_foreign_key(table_name, column: column_name) remove_foreign_key(table_name, column: column_name)
end end

@ -252,7 +252,7 @@ def add_column(table_name, column_name, type, **options) #:nodoc:
end end
end end
def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc: def remove_column(table_name, column_name, type = nil, **options) #:nodoc:
alter_table(table_name) do |definition| alter_table(table_name) do |definition|
definition.remove_column column_name definition.remove_column column_name
definition.foreign_keys.delete_if do |_, fk_options| definition.foreign_keys.delete_if do |_, fk_options|

@ -635,6 +635,7 @@ def maintain_test_schema! #:nodoc:
def method_missing(name, *args, &block) #:nodoc: def method_missing(name, *args, &block) #:nodoc:
nearest_delegate.send(name, *args, &block) nearest_delegate.send(name, *args, &block)
end end
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
def migrate(direction) def migrate(direction)
new.migrate direction new.migrate direction
@ -903,14 +904,10 @@ def method_missing(method, *arguments, &block)
end end
end end
return super unless connection.respond_to?(method) return super unless connection.respond_to?(method)
options = arguments.extract_options! connection.send(method, *arguments, &block)
if options.empty?
connection.send(method, *arguments, &block)
else
connection.send(method, *arguments, **options, &block)
end
end end
end end
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
def copy(destination, sources, options = {}) def copy(destination, sources, options = {})
copied = [] copied = []

@ -32,7 +32,8 @@ class Migration
# * rename_index # * rename_index
# * rename_table # * rename_table
class CommandRecorder class CommandRecorder
ReversibleAndIrreversibleMethods = [:create_table, :create_join_table, :rename_table, :add_column, :remove_column, ReversibleAndIrreversibleMethods = [
:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
:rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
:change_column_default, :add_reference, :remove_reference, :transaction, :change_column_default, :add_reference, :remove_reference, :transaction,
:drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension, :drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
@ -113,11 +114,12 @@ def #{method}(*args, &block) # def create_table(*args, &block)
record(:"#{method}", args, &block) # record(:create_table, args, &block) record(:"#{method}", args, &block) # record(:create_table, args, &block)
end # end end # end
EOV EOV
ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
end end
alias :add_belongs_to :add_reference alias :add_belongs_to :add_reference
alias :remove_belongs_to :remove_reference alias :remove_belongs_to :remove_reference
def change_table(table_name, options = {}) # :nodoc: def change_table(table_name, **options) # :nodoc:
yield delegate.update_table_definition(table_name, self) yield delegate.update_table_definition(table_name, self)
end end
@ -289,6 +291,7 @@ def method_missing(method, *args, &block)
super super
end end
end end
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
end end
end end
end end

@ -109,7 +109,7 @@ def method_missing(method, *args, &block)
super super
end end
end end
ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true) ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
end end
module ClassMethods # :nodoc: module ClassMethods # :nodoc:

@ -33,9 +33,13 @@ def create_table(name); end
end end
def test_unknown_commands_delegate def test_unknown_commands_delegate
recorder = Struct.new(:foo) recorder = Class.new do
recorder = CommandRecorder.new(recorder.new("bar")) def foo(kw:)
assert_equal "bar", recorder.foo kw
end
end
recorder = CommandRecorder.new(recorder.new)
assert_equal "bar", recorder.foo(kw: "bar")
end end
def test_inverse_of_raise_exception_on_unknown_commands def test_inverse_of_raise_exception_on_unknown_commands
@ -94,17 +98,10 @@ def test_invert_change_table
t.rename :kind, :cultivar t.rename :kind, :cultivar
end end
end end
if RUBY_VERSION < "2.7" assert_equal [
assert_equal [ [:rename_column, [:fruits, :cultivar, :kind]],
[:rename_column, [:fruits, :cultivar, :kind]], [:remove_column, [:fruits, :name, :string, {}], nil],
[:remove_column, [:fruits, :name, :string, {}], nil], ], @recorder.commands
], @recorder.commands
else
assert_equal [
[:rename_column, [:fruits, :cultivar, :kind]],
[:remove_column, [:fruits, :name, :string], nil],
], @recorder.commands
end
assert_raises(ActiveRecord::IrreversibleMigration) do assert_raises(ActiveRecord::IrreversibleMigration) do
@recorder.revert do @recorder.revert do