# class ActiveRecord::Migration::CommandRecorder

`ActiveRecord::Migration::CommandRecorder` records commands done during a migration and knows how to reverse those commands. The CommandRecorder knows how to invert the following commands:

- add_column

- add_foreign_key

- add_index

- add_reference

- add_timestamps

- change_column

- change_column_default (must supply a :from and :to option)

- change_column_null

- create_join_table

- create_table

- disable_extension

- drop_join_table

- drop_table (must supply a block)

- enable_extension

- remove_column (must supply a type)

- remove_columns (must specify at least one column name or more)

- remove_foreign_key (must supply a second table)

- remove_index

- remove_reference

- remove_timestamps

- rename_column

- rename_index

- rename_table

### Constants

ReversibleAndIrreversibleMethods

### Attributes

commands[RW]
delegate[RW]
reverting[RW]

### Public Class Methods

new(delegate = nil) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 42
    def initialize(delegate = nil)
      @commands = []
      @delegate = delegate
      @reverting = false
    end

### Public Instance Methods

inverse_of(command, args, &block) click to toggle source

Returns the inverse of the given command. For example:

    recorder.inverse_of(:rename_table, [:old, :new])
    # => [:rename_table, [:new, :old]]

This method will raise an `IrreversibleMigration` exception if it cannot invert the `command`.

    # File lib/active_record/migration/command_recorder.rb, line 84
          def inverse_of(command, args, &block)
            method = :"invert_#{command}"
            raise IrreversibleMigration, "          This migration uses #{command}, which is not automatically reversible.
              To make the migration reversible you can either:
              1. Define #up and #down methods in place of the #change method.
              2. Use the #reversible method to define reversible behavior.
    ".strip_heredoc unless respond_to?(method, true)
            send(method, args, &block)
          end

record(*command, &block) click to toggle source

Record `command`. `command` should be a method name and arguments. For example:

    recorder.record(:method_name, [:arg1, :arg2])

    # File lib/active_record/migration/command_recorder.rb, line 69
    def record(*command, &block)
      if @reverting
        @commands << inverse_of(*command, &block)
      else
        @commands << (command << block)
      end
    end

revert() { || ... } click to toggle source

While executing the given block, the recorded will be in reverting mode. All commands recorded will end up being recorded reverted and in reverse order. For example:

    recorder.revert{ recorder.record(:rename_table, [:old, :new]) }
    # same effect as recorder.record(:rename_table, [:new, :old])

    # File lib/active_record/migration/command_recorder.rb, line 55
    def revert
      @reverting = !@reverting
      previous = @commands
      @commands = []
      yield
    ensure
      @commands = previous.concat(@commands.reverse)
      @reverting = !@reverting
    end

### Private Instance Methods

invert_add_foreign_key(args) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 203
    def invert_add_foreign_key(args)
      from_table, to_table, add_options = args
      add_options ||= {}

      if add_options[:name]
        options = { name: add_options[:name] }
      elsif add_options[:column]
        options = { column: add_options[:column] }
      else
        options = to_table
      end

      [:remove_foreign_key, [from_table, options]]
    end

invert_add_index(args) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 162
    def invert_add_index(args)
      table, columns, options = *args
      options ||= {}

      index_name = options[:name]
      options_hash = index_name ? { name: index_name } : { column: columns }

      [:remove_index, [table, options_hash]]
    end

invert_change_column_default(args) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 188
    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

invert_change_column_null(args) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 198
    def invert_change_column_null(args)
      args[2] = !args[2]
      [:change_column_null, args]
    end

invert_drop_table(args, &block) click to toggle source
Calls superclass method

    # File lib/active_record/migration/command_recorder.rb, line 138
    def invert_drop_table(args, &block)
      if args.size == 1 && block == nil
        raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
      end
      super
    end

invert_remove_column(args) click to toggle source
Calls superclass method

    # File lib/active_record/migration/command_recorder.rb, line 149
    def invert_remove_column(args)
      raise ActiveRecord::IrreversibleMigration, "remove_column is only reversible if given a type." if args.size <= 2
      super
    end

invert_remove_foreign_key(args) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 218
    def invert_remove_foreign_key(args)
      from_table, to_table, remove_options = args
      raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil? || to_table.is_a?(Hash)

      reversed_args = [from_table, to_table]
      reversed_args << remove_options if remove_options

      [:add_foreign_key, reversed_args]
    end

invert_remove_index(args) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 172
    def invert_remove_index(args)
      table, options_or_column = *args
      if (options = options_or_column).is_a?(Hash)
        unless options[:column]
          raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
        end
        options = options.dup
        [:add_index, [table, options.delete(:column), options]]
      elsif (column = options_or_column).present?
        [:add_index, [table, column]]
      end
    end

invert_rename_column(args) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 158
    def invert_rename_column(args)
      [:rename_column, [args.first] + args.last(2).reverse]
    end

invert_rename_index(args) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 154
    def invert_rename_index(args)
      [:rename_index, [args.first] + args.last(2).reverse]
    end

invert_rename_table(args) click to toggle source

    # File lib/active_record/migration/command_recorder.rb, line 145
    def invert_rename_table(args)
      [:rename_table, args.reverse]
    end

method_missing(method, *args, &block) click to toggle source

Forwards any missing method call to the target.

Calls superclass method

    # File lib/active_record/migration/command_recorder.rb, line 229
    def method_missing(method, *args, &block)
      if @delegate.respond_to?(method)
        @delegate.send(method, *args, &block)
      else
        super
      end
    end