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
enddef 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() が呼ばれたとき、
- まず CreateSchedules を探すが、create_table は存在しない。
- 次に基本クラスの ActiveRecord::Migration を探すが、create_table は存在しない。
- 最後にさらに基本クラスの Object を探すが、create_table は存在しない。
- ActiveRecord::Migration::method_missing が呼び出される。
- 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 はやはり只者ではない。悔しいけど、私とはまったく別次元のプログラマであるな。(当たり前だが・・・)