#5 remove read_object. allow read / read? to receive any type

This commit is contained in:
Brian J. Cardiff 2016-06-21 12:10:09 -03:00
parent 0daa1c18d7
commit 9f3f0a9836
2 changed files with 74 additions and 31 deletions

View file

@ -1,10 +1,7 @@
require "./spec_helper" require "./spec_helper"
class GenericResultSet(T) < DB::ResultSet module GenericResultSet
def initialize(statement, @row : Array(T)) @index = 0
super(statement)
@index = 0
end
def move_next def move_next
@index = 0 @index = 0
@ -26,11 +23,11 @@ class GenericResultSet(T) < DB::ResultSet
{% for t in DB::TYPES %} {% for t in DB::TYPES %}
# Reads the next column as a nillable {{t}}. # Reads the next column as a nillable {{t}}.
def read?(t : {{t}}.class) : {{t}}? def read?(t : {{t}}.class) : {{t}}?
read_object as {{t}}? read_and_move_next_column as {{t}}?
end end
{% end %} {% end %}
def read_object def read_and_move_next_column
@index += 1 @index += 1
@row[@index - 1] @row[@index - 1]
end end
@ -69,13 +66,25 @@ class FooDriver < DB::Driver
class FooStatement < DB::Statement class FooStatement < DB::Statement
protected def perform_query(args : Enumerable) : DB::ResultSet protected def perform_query(args : Enumerable) : DB::ResultSet
GenericResultSet(Any).new(self, FooDriver.fake_row) FooResultSet.new(self, FooDriver.fake_row)
end end
protected def perform_exec(args : Enumerable) : DB::ExecResult protected def perform_exec(args : Enumerable) : DB::ExecResult
DB::ExecResult.new 0, 0i64 DB::ExecResult.new 0, 0i64
end end
end end
class FooResultSet < DB::ResultSet
include GenericResultSet
def initialize(statement, @row : Array(FooDriver::Any))
super(statement)
end
def read?(t : FooValue.class) : FooValue?
read_and_move_next_column.as(FooValue?)
end
end
end end
DB.register_driver "foo", FooDriver DB.register_driver "foo", FooDriver
@ -111,13 +120,25 @@ class BarDriver < DB::Driver
class BarStatement < DB::Statement class BarStatement < DB::Statement
protected def perform_query(args : Enumerable) : DB::ResultSet protected def perform_query(args : Enumerable) : DB::ResultSet
GenericResultSet(Any).new(self, BarDriver.fake_row) BarResultSet.new(self, BarDriver.fake_row)
end end
protected def perform_exec(args : Enumerable) : DB::ExecResult protected def perform_exec(args : Enumerable) : DB::ExecResult
DB::ExecResult.new 0, 0i64 DB::ExecResult.new 0, 0i64
end end
end end
class BarResultSet < DB::ResultSet
include GenericResultSet
def initialize(statement, @row : Array(BarDriver::Any))
super(statement)
end
def read?(t : BarValue.class) : BarValue?
read_and_move_next_column.as(BarValue?)
end
end
end end
DB.register_driver "bar", BarDriver DB.register_driver "bar", BarDriver
@ -138,7 +159,7 @@ describe DB do
rs.move_next rs.move_next
rs.read?(Int32).should eq(1) rs.read?(Int32).should eq(1)
rs.read?(String).should eq("string") rs.read?(String).should eq("string")
(rs.read_object.as(FooValue)).value.should eq(3) rs.read(FooValue).value.should eq(3)
end end
end end
end end
@ -150,7 +171,7 @@ describe DB do
db.query "query" do |rs| db.query "query" do |rs|
w.check w.check
rs.move_next rs.move_next
(rs.read_object.as(BarValue)).value.should eq(4) rs.read(BarValue).value.should eq(4)
rs.read?(String).should eq("lorem") rs.read?(String).should eq("lorem")
rs.read?(Float64).should eq(1.0) rs.read?(Float64).should eq(1.0)
end end
@ -158,6 +179,34 @@ describe DB do
end end
end end
it "Foo and Bar drivers should not implement each other read" do
with_witness do |w|
DB.open("foo://host") do |db|
FooDriver.fake_row = [1] of FooDriver::Any
db.query "query" do |rs|
rs.move_next
expect_raises Exception, "read?(t : BarValue) is not implemented in FooDriver::FooResultSet" do
w.check
rs.read(BarValue)
end
end
end
end
with_witness do |w|
DB.open("bar://host") do |db|
BarDriver.fake_row = [1] of BarDriver::Any
db.query "query" do |rs|
rs.move_next
expect_raises Exception, "read?(t : FooValue) is not implemented in BarDriver::BarResultSet" do
w.check
rs.read(FooValue)
end
end
end
end
end
it "allow custom types to be used as arguments for query" do it "allow custom types to be used as arguments for query" do
DB.open("foo://host") do |db| DB.open("foo://host") do |db|
FooDriver.fake_row = [1, "string"] of FooDriver::Any FooDriver.fake_row = [1, "string"] of FooDriver::Any

View file

@ -16,14 +16,10 @@ module DB
# ### Note to implementors # ### Note to implementors
# #
# 1. Override `#move_next` to move to the next row. # 1. Override `#move_next` to move to the next row.
# 2. Override `#read?(t)` for all `t` in `DB::TYPES`. # 2. Override `#read?(t)` for all `t` in `DB::TYPES` and any other types the driver should handle.
# 3. (Optional) Override `#read(t)` for all `t` in `DB::TYPES`. # 3. (Optional) Override `#read(t)` for all `t` in `DB::TYPES` and any other.
# 4. Override `#column_count`, `#column_name`. # 4. Override `#column_count`, `#column_name`.
# 5. Override `#column_type`. It must return a type in `DB::TYPES`. # 5. Override `#column_type`. It must return a type in `DB::TYPES`.
# 6. Override `#read_object` to return other data types not included in `DB::TYPES`. This
# will create a union type, so user will be forced to cast result type. Usually `#read`
# should be used to avoid unnecesary intermediate union type values. Calling `#read_object`
# should also move to the next column.
abstract class ResultSet abstract class ResultSet
include Disposable include Disposable
@ -63,28 +59,26 @@ module DB
# The result is one of `DB::TYPES`. # The result is one of `DB::TYPES`.
abstract def column_type(index : Int32) abstract def column_type(index : Int32)
# list datatypes that must be supported form the driver def read(t)
# users will call read(String) or read?(String) for nillables read?(t).not_nil!
end
{% for t in DB::TYPES %}
# Reads the next column as a nillable {{t}}.
abstract def read?(t : {{t}}.class) : {{t}}?
# Reads the next column as a {{t}}.
def read(t : {{t}}.class) : {{t}}
read?({{t}}).not_nil!
end
{% end %}
# Reads the next column as a Nil. # Reads the next column as a Nil.
def read(t : Nil.class) : Nil def read(t : Nil.class) : Nil
read?(Nil) read?(Nil)
end end
def read_object def read?(t)
raise "Not implemented" raise "read?(t : #{t}) is not implemented in #{self.class}"
end end
# list datatypes that must be supported form the driver
# users will call read(String) or read?(String) for nillables
{% for t in DB::TYPES %}
# Reads the next column as a nillable {{t}}.
abstract def read?(t : {{t}}.class) : {{t}}?
{% end %}
# def read_blob # def read_blob
# yield ... io .... # yield ... io ....
# end # end