byebug と prepend で gem の中を探る

## モチベーション

`rake db:migrate` を実行するときに 
`ActiveRecord::ConnectionAdapters::Mysql2Adapter.initialize_schema_migrations_table` がいったいどのようなタイミングで呼び出されるのか、知りたくなりました。

Rails 4.2.4 でのソースコードは https://github.com/rails/rails/blob/v4.2.4/activerecord/lib/active_record/migration.rb になるのですが、ちょっと追いづらい。

そこで `prepend` と byebug を使ってスタックトレースを表示してみることにしました。

## 実際のコード

```rb:config/initializers/ar_test.rb
ActiveSupport.on_load :active_record do
  module ActiveRecord::ConnectionAdapters
    module Mysql2AdapterExt
      def initialize_schema_migrations_table
        debugger
        super
      end
    end

    class Mysql2Adapter
      prepend Mysql2AdapterExt
    end
  end
end
```

`prepend` を使わずに、オープンクラスを使っても良かったのかもしれませんが、その後の実行もできるように今回は `prepend` にしてみました。

## 実行結果

`rake db:migrate` を実行してみます。

```shell-session
[1, 10] in /Users/../config/initializers/ar_test.rb
    1: ActiveSupport.on_load :active_record do
    2:   module ActiveRecord::ConnectionAdapters
    3:     module Mysql2AdapterExt
    4:       def initialize_schema_migrations_table
    5:         debugger
=>  6:         super
    7:       end
    8:     end
    9: 
   10:     class Mysql2Adapter
(byebug) 
```

無事に `debugger` を入れたところで止まります。ここで byebug の `bt` コマンドを使って、スタックトレースを表示してみます。

```shell-session
(byebug) bt
--> #0  ActiveRecord::ConnectionAdapters::Mysql2AdapterExt.initialize_schema_migrations_table at /Users/.../config/initializers/ar_test.rb:6
    #1  ActiveRecord::Migrator.initialize(direction#Symbol, migrations#Array, target_version#NilClass) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/activerecord-4.2.4/lib/active_record/migration.rb:922
    ͱ-- #2  Class.new(*args) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/activerecord-4.2.4/lib/active_record/migration.rb:820
    #3  #<Class:ActiveRecord::Migrator>.up(migrations_paths#Array, target_version#NilClass) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/activerecord-4.2.4/lib/active_record/migration.rb:820
    #4  #<Class:ActiveRecord::Migrator>.migrate(migrations_paths#Array, target_version#NilClass, &block#Proc) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/activerecord-4.2.4/lib/active_record/migration.rb:798
    #5  ActiveRecord::Tasks::DatabaseTasks.migrate at /Users/.../.rvm/gems/ruby-2.1.7@/gems/activerecord-4.2.4/lib/active_record/tasks/database_tasks.rb:137
    #6  block (2 levels) in <top (required)> at /Users/.../.rvm/gems/ruby-2.1.7@/gems/activerecord-4.2.4/lib/active_record/railties/databases.rake:44
    ͱ-- #7  Proc.call(*args) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/task.rb:240
    #8  block in Rake::Task.execute(args#Rake::TaskArguments) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/task.rb:240
    ͱ-- #9  Array.each at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/task.rb:235
    #10 Rake::Task.execute(args#Rake::TaskArguments) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/task.rb:235
    #11 block in Rake::Task.invoke_with_call_chain(task_args#Rake::TaskArguments, invocation_chain#Rake::InvocationChain::EmptyInvocationChain) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/task.rb:179
    #12 MonitorMixin.mon_synchronize at /Users/.../.rvm/rubies/ruby-2.1.7@/lib/ruby/2.1.0/monitor.rb:211
    #13 Rake::Task.invoke_with_call_chain(task_args#Rake::TaskArguments, invocation_chain#Rake::InvocationChain::EmptyInvocationChain) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/task.rb:172
    #14 Rake::Task.invoke(*args#Array) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/task.rb:165
    #15 Rake::Application.invoke_task(task_string#String) at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/application.rb:150
    #16 block (2 levels) in Rake::Application.top_level at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/application.rb:106
    ͱ-- #17 Array.each at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/application.rb:106
    #18 block in Rake::Application.top_level at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/application.rb:106
    #19 Rake::Application.run_with_threads at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/application.rb:115
    #20 Rake::Application.top_level at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/application.rb:100
    #21 block in Rake::Application.run at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/application.rb:78
    #22 Rake::Application.standard_exception_handling at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/application.rb:176
    #23 Rake::Application.run at /Users/.../.rvm/gems/ruby-2.1.7@/gems/rake-10.4.2/lib/rake/application.rb:75
    #24 <main> at /Users/.../bin/rake:4
(byebug)
```

よく理解できました。あとは byebug の `c` コマンドを使えば、そのまま実行を継続できます。