Rails の黒魔術: 自己再定義メソッド

Ruby on Rails の意味不明な黒魔術を問題視するシリーズ。

問題: 次のメソッド定義において、(*1) や (*2) や (*3) は何をしているんでしょう?

module ActiveRecord
  module ConnectionAdapters
    class PostgreSQLColumn < Column
      private
        ...
        
        # Escapes binary strings for bytea input to the database.
        def self.string_to_binary(value)
          if PGconn.respond_to?(:escape_bytea)
            self.class.module_eval do     # (*1)
              define_method(:string_to_binary) do |value|
                PGconn.escape_bytea(value) if value
              end
            end
          else
            self.class.module_eval do     # (*2)
              define_method(:string_to_binary) do |value|
                if value
                  result = ''
                  value.each_byte { |c| result << sprintf('\\\\%03o', c) }
                  result
                end
              end
            end
          end
          self.class.string_to_binary(value)   # (*3)
        end

        ...
    end
  end
end

(activerecord-2.1.0/lib/active_record/connection_adapters/postgresql_adapter.rb より抜粋)


答えは、自己再定義メソッドの定義。
ActiveRecord::ConnectionAdapters::PostgreSQLColumn.string_to_binary() メソッドは、最初に実行されたとき、自分自身を再定義する *1

class Foo
  def hello(name)
    puts "*** 1回目の呼び出し..."
    self.class.module_eval do
      # 自分自身を再定義
      define_method(:hello) do |name|
        puts "Hello #{name}!"
      end
    end
    self.hello(name)  # 再定義したメソッドを呼び出す
    puts "*** 終了"
  end
end

foo = Foo.new
foo.hello('world')    # 1回目の呼び出し
foo.hello('world')    # 2回目の呼び出し
foo.hello('world')    # 3回目の呼び出し


実行結果:

*** 1回目の呼び出し...
Hello world!
*** 終了
Hello world!
Hello world!


よくこんなこと思いつくよな。最初に考えたやつは頭いい。


でも、これが本当に必要なのかは疑問だ。こう書けば済む話だと思う。

module ActiveRecord
  module ConnectionAdapters
    class PostgreSQLColumn < Column
      private
        ...
        
        if PGconn.respond_to?(:escape_bytea)
          def self.string_to_binary(value)
            PGconn.escape_bytea(value) if value
          end
        else
          # Escapes binary strings for bytea input to the database.
          def self.string_to_binary(value)
            if value
              result = ''
              value.each_byte { |c| result << sprintf('\\\\%03o', c) }
              result
            end
          end
        end

        ...
    end
  end
end


どう考えてもこっちのほうがわかりやすい。わざわざ黒魔術を使う意味がわからない。


なんか妥当な理由を思いついた人がいたら教えてください。

*1:*1) や (*2) の部分)。 そのあと、再定義されたメソッドを呼び出している ((*3) の部分)。 2回目以降は、再定義されたメソッドが実行されるので、(*3) の部分は実行されない。 わかりやすく書くと、こんな感じ((インスタンスメソッドの定義に変更してある