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 系のコードをメインに追っかけていきます.
なるべく図を入れるようにしよう.