DataMapper 0.10.2 Reading (その 4: dm-core/core_ext/)

このディレクトリに配置されたコードは,ruby の標準クラス (モジュール) を拡張するためのもの.具体的には以下の 3 つが拡張されている.

  • Enumerable
  • Kernel
  • Symbol

それぞれは小さなコードだが,影響範囲は広い.

Enumerable

empty?/one?/first/size メソッドを定義している.標準メソッドについては こちら を参照されたし.
Enumerable の規約に従い,全て each により定義されている.これらの内,ここでは one? メソッドを取り上げようと思う.one? メソッドは文字通り「1 つだけ?」かどうかを判定するわけだが,この判定基準にブロックを用いる.

module Enumerable
  def one?
    return one? { |entry| entry } unless block_given?

    matches = 0
    each do |entry|
      matches += 1 if yield(entry)
      return false if matches > 1
    end
    matches == 1
  end

与えられたブロックで各要素を評価し,true を返した要素の個数をカウントしていく.カウントが 1 であれば true を返すし,そうでなければ false を返す.

Kernel

Kernel では,DataMapper モジュールの repository メソッドを呼び出すためのメソッドが定義されている.

module Kernel
  private
  def repository(*args, &block)
    DataMapper.repository(*args, &block)
  end
end

ここで定義されているということは,self.repository の形であれば (self 以外に対して .repository としなければ) どこでも呼び出せる.
簡単な例を示すと,以下のような感じ.

module Kernel
  private
  def hoge
    puts "hogeっていうな"
  end
end

hoge
# => hogeっていうな

class A; end
a = A.new
a.hoge
# => NoMethodError

class A
  def call_hoge
    hoge
  end
end
a.call_hoge
# => hogeっていうな

Symbol

コードは短いが,検索に広く影響する条件用の Symbol を定義している.

class Symbol
  (DataMapper::Query::Conditions::Comparison.slugs | [ :not, :asc, :desc ]).each do |sym|
    class_eval <<-RUBY, __FILE__, __LINE__ + 1
      def #{sym}
        #{"warn \"explicit use of '#{sym}' operator is deprecated (#{caller[0]})\"" if sym == :eql || sym == :in}
        DataMapper::Query::Operator.new(self, #{sym.inspect})
      end
    RUBY
  end
end

例えば,

class MyModel
  include DataMapper::Resource
  property :id, Serial
  property :name, String
end

というモデルを定義したとき,

m = MyModel.all(:name.like => 'Krd%')

という呼び出しの ".like" にあたる部分を定義していることになる.
この定義により,".like" と呼び出すことで,条件演算を表す Operator インスタンスが得られる.

なお,"Comparison.slugs" については後ほど.ここでは「条件記述用に定義された Symbol を配列で返す」ぐらいの認識でよいかと.