DataMapper Reading (その 4: Adapter 補足)

前回積み残した TODO の Adapter について.

実際に DB へ SQL 発行を行うのが Adapter なので,こいつを実装すればいろんなストレージにアクセスできるようになる.なお,自分で Adapter を実装する場合は,in_memory_adapter.rb が参考になる.

AbstractAdapter#read_many

module DataMapper
  module Adapters
    class AbstractAdapter
      def read_many(query)
        raise NotImplementedError
      end

何もないっす.

DataObjectsAdapter#read_many

ここがメイン.個々のストレージに依存しないコードになっている.

module DataMapper
  module Adapters
    class DataObjectsAdapter < AbstractAdapter
       def read_many(query)
        Collection.new(query) do |collection|
          with_connection do |connection|
            command = connection.create_command(read_statement(query))
            command.set_types(query.fields.map { |p| p.primitive })

            begin
              bind_values = query.bind_values.map do |v|
                v == [] ? [nil] : v
              end
              reader = command.execute_reader(*bind_values)

              while(reader.next!)
                collection.load(reader.values)
              end
            ensure
              reader.close if reader
            end
          end
        end
      end

メインの処理は with_connection に渡しているブロックの部分.ちなみに with_connection メソッドは,コネクションの生成→利用→クローズといった一連の流れと,エラー時のロギング処理が入ったベーシックなメソッドである.

module DataMapper
  module Adapters
    class DataObjectsAdapter < AbstractAdapter
      def with_connection
        connection = nil
        begin
          connection = create_connection
          return yield(connection)
        rescue => e
          DataMapper.logger.error(e.to_s)
          raise e
        ensure
          close_connection(connection) if connection
        end
      end

さてさて,上記 yield で実行されるコードについて.

Connection から Command 作って,Command 実行して Reader で受け取り,Reader で読み取ったデータをCollection に load していくという流れ.Collection#load メソッドで Model#load メソッドが呼び出される.SQL は,DataObjectsAdapter の内部に定義された SQL#read_statement メソッドで作成される.他の各種 statement も SQL モジュールにメソッドとして定義されている.

DataObjects module

Connection とか Command とか Reader は,DataObjects モジュールに定義されている.DataObjects は github における do に定義されている.中身についてはアクセス先のストレージによりまちまちで,MySQL/PostgreSQL/SQLite の場合は ruby 拡張ライブラリとして実装されている.

終わりに

0.9.x 系は中断して,今後は 0.10.x 系のコードをメインに追っかけていきます.
なるべく図を入れるようにしよう.