Rails の内部構造(続)


前回の続き。

db/migrate/001_create_schedule.rb (修正後)


class CreateSchedules < ActiveRecord::Migration
def self.up
create_table(:schedules) do |t|
t.column(:datatime, :timestamp)
t.column(:title, :string)
t.column(:content, :text) end
end

def self.down
drop_table(:schedules)
end
end

上のように、generate コマンドで作られたテーブル定義用の Ruby クラスで何気なく使われている create_table() はActiveRecord::Migration クラスのメソッドではなく、ActiveRecord::ConnectionAdapters::SchemaStatements モジュールに属するメソッドなのはどうしてなんだろう、という話だった。


いろいろ調べてみたところ、次のようなことがわかった。


class ActiveRecord::Migration に method_missing() メソッドが定義されているのである。method_missing()
はもともと Object クラスのメソッドで、定義されていないメソッドが呼び出されたときに、呼び出される。
この method_missing() で今度は create_table() に転送される、というわけだ。


このことをもう少し詳しく説明しよう。
上のコードで、create_table() が呼ばれたとき、

  1. まず CreateSchedules を探すが、create_table は存在しない。
  2. 次に基本クラスの ActiveRecord::Migration を探すが、create_table は存在しない。
  3. 最後にさらに基本クラスの Object を探すが、create_table は存在しない。
  4. ActiveRecord::Migration::method_missing が呼び出される。
  5. method_missing で他のオブジェクトに対して create_table() が呼び出される。

という実行の流れになる。実際 method_missing() の中身はこうなっている。

active_record/migration.rb


def method_missing(method, *arguments, &block)
      ...
ActiveRecord::Base.connection.send(method, *arguments, &block)
end
end

ここで、ActiveRecord::Base.connection() が返すのは、ActiveRecord::ConnectionAdapters::AbstractAdapter インスタンスである。
(active_record/connection_adapters/配下のabstract/connection_specification.rb, abstract_adapter.rb等を参照のこと)


次の疑問は、この AbstractAdapter と SchemaStatements の関係である。
これは、active_record/connection_adapters/abstract_adapter.rb を見るとわかる。


...
require 'active_record/connection_adapters/abstract/schema_statements'
...

module ActiveRecord
module ConnectionAdapters
class AbstractAdapter
include ..., SchemaStatements

という風にして、AbstractAdapter に SchemaStatements が include されているのだ。


こういう仕組みで、ActiveRecord::Migration のサブクラスのメソッドで使われている create_table() が SchemaStatements モジュールのメソッドを呼び出すことができるようになっている。


Rails のソースは極めて整然と書かれている。もともと簡潔な Ruby の文法とあいまって、とても読みやすいコードになっている。1メソッドあたりの行数が少ないために、コードにリズム感があり、音楽を楽しむように読み進めることができる。コードを簡潔化するテクニックが至るところにちりばめられていて、非常に勉強になる。


こんな美しいコードが書ける DHH はやはり只者ではない。悔しいけど、私とはまったく別次元のプログラマであるな。(当たり前だが・・・)