ようへいの日々精進XP

よかろうもん

メタプログラミング Ruby 写経メモ - 1 章 頭文字 M -

この記事は

メタプログラミング Ruby 第 2 版を写経したり, 感想を書いたりしています.

www.oreilly.co.jp

そして, この記事は

既に 2018 年だけど...

qiita.com

初老丸 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 を継承するだけで, 実行時にアクセサメソッドが定義されるようなコードを書いた.

以上

メタプログラミング Ruby 第 2 版の第 1 章について, ざっくりと纏めてみました.