class RuboCop::Cop::Rails::ReversibleMigration
This cop checks whether the change method of the migration file is reversible.
@example
# bad def change change_table :users do |t| t.remove :name end end # good def change create_table :users do |t| t.string :name end end # good def change reversible do |dir| change_table :users do |t| dir.up do t.column :name, :string end dir.down do t.remove :name end end end end
@example
# drop_table # bad def change drop_table :users end # good def change drop_table :users do |t| t.string :name end end
@example
# change_column_default # bad def change change_column_default(:suppliers, :qualification, 'new') end # good def change change_column_default(:posts, :state, from: nil, to: "draft") end
@example
# remove_column # bad def change remove_column(:suppliers, :qualification) end # good def change remove_column(:suppliers, :qualification, :string) end
@example
# remove_foreign_key # bad def change remove_foreign_key :accounts, column: :owner_id end # good def change remove_foreign_key :accounts, :branches end
@example
# change_table # bad def change change_table :users do |t| t.remove :name t.change_default :authorized, 1 t.change :price, :string end end # good def change change_table :users do |t| t.string :name end end # good def change reversible do |dir| change_table :users do |t| dir.up do t.change :price, :string end dir.down do t.change :price, :integer end end end end
@see api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html
Constants
- IRREVERSIBLE_CHANGE_TABLE_CALLS
- MSG
Public Instance Methods
on_block(node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 168 def on_block(node) return unless within_change_method?(node) return if within_reversible_block?(node) check_change_table_node(node.send_node, node.body) end
on_send(node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 157 def on_send(node) return unless within_change_method?(node) return if within_reversible_block?(node) check_irreversible_schema_statement_node(node) check_drop_table_node(node) check_change_column_default_node(node) check_remove_column_node(node) check_remove_foreign_key_node(node) end
Private Instance Methods
all_hash_key?(args, *keys)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 269 def all_hash_key?(args, *keys) return false unless args && args.hash_type? hash_keys = args.to_a.map do |arg| arg.to_a.first.children.first.to_sym end hash_keys & keys == keys end
check_change_column_default_node(node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 194 def check_change_column_default_node(node) change_column_default_call(node) do |args| unless all_hash_key?(args.first, :from, :to) add_offense( node, message: format( MSG, 'change_column_default(without :from and :to)' ) ) end end end
check_change_table_node(node, block)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 229 def check_change_table_node(node, block) change_table_call(node) do |arg| if target_rails_version < 4.0 add_offense( node, message: format(MSG, 'change_table') ) elsif block.send_type? check_change_table_offense(arg, block) else block.each_child_node do |child_node| check_change_table_offense(arg, child_node) end end end end
check_change_table_offense(receiver, node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 246 def check_change_table_offense(receiver, node) method_name = node.method_name return if receiver != node.receiver && !IRREVERSIBLE_CHANGE_TABLE_CALLS.include?(method_name) add_offense( node, message: format(MSG, "change_table(with #{method_name})") ) end
check_drop_table_node(node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 183 def check_drop_table_node(node) drop_table_call(node) do unless node.parent.block_type? add_offense( node, message: format(MSG, 'drop_table(without block)') ) end end end
check_irreversible_schema_statement_node(node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 177 def check_irreversible_schema_statement_node(node) irreversible_schema_statement_call(node) do |method_name| add_offense(node, message: format(MSG, method_name)) end end
check_remove_column_node(node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 207 def check_remove_column_node(node) remove_column_call(node) do |args| if args.to_a.size < 3 add_offense( node, message: format(MSG, 'remove_column(without type)') ) end end end
check_remove_foreign_key_node(node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 218 def check_remove_foreign_key_node(node) remove_foreign_key_call(node) do |arg| if arg.hash_type? add_offense( node, message: format(MSG, 'remove_foreign_key(without table)') ) end end end
within_change_method?(node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 256 def within_change_method?(node) node.each_ancestor(:def).any? do |ancestor| method_name, = *ancestor method_name == :change end end
within_reversible_block?(node)
click to toggle source
# File lib/rubocop/cop/rails/reversible_migration.rb, line 263 def within_reversible_block?(node) node.each_ancestor(:block).any? do |ancestor| ancestor.block_type? && ancestor.send_node.method?(:reversible) end end