この記事は
メタプログラミング Ruby 第 2 版を写経したり, 感想を書いたりしています.
そして, この記事は
既に 2018 年だけど...
初老丸 Advent Calendar 2017 22 日目の記事です.
メタプログラミングとは
- メタプログラミングとは, コードを記述するコードを記述することである
イントロスペクション
イントロスペクションとは...
対象物について、その素性、カバーする範囲、そして可能な事を判断するために、調査できる機能
以下のサンプルコードでイントロスペクションしてみる.
class Greeting def initialize(text) @text = text end def welcome @text end end my_object = Greeting.new("Hello")
my_object のクラスを確認.
irb(main):011:0> my_object.class => Greeting
インスタンスメソッドを確認.
irb(main):012:0> my_object.class.instance_methods(false) => [:welcome]
引数の false
は「自分に定義したインスタンスメソッドの一覧だけを出力し, 継承したものは必要ない」場合に指定する.
インスタンス変数を確認.
irb(main):013:0> my_object.instance_variables => [:@text]
メタプログラマのボブ
ボブの最初の試み
メタボプログラマのボブと読み違いしてしまう場面.
class Entity attr_reader :table, :ident def initialize(table, ident) @table = table @ident = ident Database.sql "INSERT INTO #{@table} (id) VALUES (#{@ident})" end def set(col, val) Database.sql "UPDATE #{@table} SET #{col}= '#{val}' WHERE id =#{@ident}" end ... 略 .. end
メタボプログラマ、ボブが最初に実装したオレオレ OR マッパー.
以下のように利用する.
class Movie < Entity def initialize(ident) super "movies", ident end def title get "title" end def title=(value) set "title", value end ... 略 ... end
同僚のビルに以下のようにダメ出しされるボブ.
- コードが重複しすぎ
- データベースの movies テーブルにも title カラムがあるし, コードの Movie クラスにも title というアトリビュートがあるよね...
- メタプログラミングを使ってコードを短くすれば, うまく解決出来るよ
メタプログラミングに突入
Active Record を使えば一発.
class Movie < ActiveRecord::Base end
え, ActiveRecord::Base のサブクラスを作るだけ...
movie = Movie.create movie.title = "博士の異常な愛情"
上記のコードは, movies テーブルのレコードをラップした Movie オブジェクトを生成し, title フィールドにアクセスする Movie#title や Movie#title= を呼び出している...らしいんだが, これらはどのように存在しているのか??
テーブル名は...
Active Record がイントロスペクションを利用してクラス名を調べ, 簡単な規約を適用している. 例えば, クラス名が Movie であれば, 名前が movies テーブルにマッピングしてくれる!!
アクセサメソッドは...
オブジェクトのアトリビュートにアクセスする title= や title のようなアクセサメソッドは, メタプログラミングが面倒を見てくれている. Active Record がデータベースのスキーマを読み取り, movies テーブルに title と director の 2 つのカラムがあることを見つけ, この 2 つのアトリビュートのアクセサメソッドを定義している.
メタプログラミング, イントロスペクションってすげえ.
頭文字 M セカンドステージ
- メタプログラミングとは, 言語要素を実行時に操作するコードを記述することである
Active Record の作者は ActiveRecord::Base を継承するだけで, 実行時にアクセサメソッドが定義されるようなコードを書いた.