マイグレーションファイルのdef upとdef self.upの違いについて調べてみた

## 背景
社内のマイグレーションファイルにて、`def up`と`def self.up`が混在していたのでどちらかに統一すべく(そもそも統一すべきかどうかも含めて)、違いを調べてみた。
（正確には、調べてみたけどまだ途中です）


## 環境
ruby 2.6.3
rails 5.2.3


## やり方
適当なマイグレーションファイル内に`binding.pry`を埋め込んで挙動を確認する。


## def up
```
class CreateUsers < ActiveRecord::Migration[5.2]
  def up
binding.pry    # ここに埋め込む
    create_table :users do |t|
      t.string :name

      t.timestamps
    end
  end
end
```
実行

```
$ rails db:migrate
== 20191020160206 CreateUsers: migrating ======================================

    2:   def self.up
    3: binding.pry
 => 4:     create_table :users do |t|
    5:       t.string :name
    6: 
    7:       t.timestamps
    8:     end
    9:   end

pry(CreateUsers)> step  # nextだとcreate_tableの実行後に移動してしまうのでstepでcreate_tableの中に入る

    603: def method_missing(name, *args, &block) # :nodoc:
 => 604:   nearest_delegate.send(name, *args, &block)
    605: end

pry(CreateUsers)> self
=> CreateUser

pry(CreateUsers)> nearest_delegate   # CreateUsersクラスのインスタンスが格納されている
=> <##<CreateUsers:0x00007fbc300761f8
 @connection= #<ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x00007f9f74064350 .....>
 @name="CreateUsers"
 @version=2019102016020
 
pry(CreateUsers)> name
=> :create_table

pry(CreateUsers)> args
=> [:users]

pry(CreateUsers)> block
=> #<Proc:0x00007fbc30176c10@/home/ec2-user/environment/sample/db/migrate/20191020160206_create_users.rb:4>
```
`CreateUsers`クラスのインスタンス(`nearest_delegate`)が`create_table`を実行。

```
pry(CreateUsers)> step

    858: def method_missing(method, *arguments, &block)
 => 859:   arg_list = arguments.map(&:inspect) * ", "
    860: 
    861:   say_with_time "#{method}(#{arg_list})" do
    862:     unless connection.respond_to? :revert
    863:       unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
    864:         arguments[0] = proper_table_name(arguments.first, table_name_options)
    865:         if [:rename_table, :add_foreign_key].include?(method) ||
    866:           (method == :remove_foreign_key && !arguments.second.is_a?(Hash))
    867:           arguments[1] = proper_table_name(arguments.second, table_name_options)
    868:         end
    869:       end
    870:     end
    871:     return super unless connection.respond_to?(method)
    872:     connection.send(method, *arguments, &block)
    873:   end
    874: end

pry(#<CreateUsers>)> self   # nearest_delegateと同じ
=> #<CreateUsers:0x00007fbc300761f8
 @connection= #<ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x00007f9f74064350 .....>
 @name="CreateUsers"
 @version=2019102016020

pry(#<CreateUsers>)> method
=> :create_table

pry(#<CreateUsers>)> arguments
=> [:users]
```
`create_table`のレシーバは`CreateUsers`であり、`CreateUsers`の親クラス(ActiveRecord::Migrationなど)や読み込んでいるモジュールのクラスメソッドにもないっぽいので、クラスメソッドの`method_missing`が呼ばれる。


## def up
とりあえず実行

```
$ rails db:migrate
== 20191020160206 CreateUsers: migrating ======================================

From: /home/ec2-user/environment/sample/db/migrate/20191020160206_create_users.rb @ line 4 CreateUsers#up:

    2:   def up
    3: binding.pry
 => 4:     create_table :users do |t|
    5:       t.string :name
    6: 
    7:       t.timestamps
    8:     end
    9:   end

pry(#<CreateUsers>)> self
=> #<CreateUsers:0x00007f8f04070c70
 @connection= #<ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x00007f9f74064350 .....>
 @name="CreateUsers",
 @version=20191020160206>

pry(#<CreateUsers>)> step

From: /home/ec2-user/environment/rails/activerecord/lib/active_record/migration.rb @ line 859 ActiveRecord::Migration#method_missing:

    858: def method_missing(method, *arguments, &block)
 => 859:   arg_list = arguments.map(&:inspect) * ", "
    860: 
    861:   say_with_time "#{method}(#{arg_list})" do
    862:     unless connection.respond_to? :revert
    863:       unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
    864:         arguments[0] = proper_table_name(arguments.first, table_name_options)
    865:         if [:rename_table, :add_foreign_key].include?(method) ||
    866:           (method == :remove_foreign_key && !arguments.second.is_a?(Hash))
    867:           arguments[1] = proper_table_name(arguments.second, table_name_options)
    868:         end
    869:       end
    870:     end
    871:     return super unless connection.respond_to?(method)
    872:     connection.send(method, *arguments, &block)
    873:   end
    874: end
```

`create_table`のレシーバがインスタンスになったので、インスタンスメソッドの`method_missing`が呼ばれる。

これ以降は`up`も`self.up`も同じ(だと思う)。


## 結論

* `self.up`の場合は**クラスメソッドのmethod_missing**が呼ばれ、その後にレシーバにインスタンスを指定した後、再度**インスタンスメソッドのmethod_missing**が呼ばれる。
* `up`の場合は**インスタンスメソッドのmethod_missing**だけが呼ばれる。

<br>

いやいや

<br>

`nearest_delegate`ってなんなの


## nearest_delegate

```
module ActiveRecord



 class Migration



   class << self
     attr_accessor :delegate # :nodoc:
     attr_accessor :disable_ddl_transaction # :nodoc:

->  def nearest_delegate # :nodoc:
       delegate || superclass.nearest_delegate
     end
```
`accessor :delegate `がある。あと下の方に、

```
   def initialize(name = self.class.name, version = nil)
      @name       = name
      @version    = version
      @connection = nil
    end

    # instantiate the delegate object after initialize is defined
    self.delegate = new
```
とかあったので、今回の場合、

```
CreateUsers.delegate = CreateUsers.new(name : "CreateUsers", version: 2019102016020, connection: #<ActiveRecord::ConnectionAdapters::SQLite3Adapter:0x00007f9f74064350 .....>)
```
ということ？


## 結び
引き続き調査します。



## 追記(2019/12/25)

`self.up`と書いた場合には、`method_missing`の前に、マイグレーションクラスのインスタンスが下記を実行します。

```
ActiveRecord::Migrator#up

def up
  self.class.delegate = self
  return unless self.class.respond_to?(:up)
  self.class.up
end
```

`self.class.delegate = self`によって、マイグレーションクラスが`nearest_delagate`を実行したときに、マイグレーションクラスのインスタンスが返るようになります。