Allow query results to be read as named tuples directly (#56)

This commit is contained in:
Arthur Poulet 2017-12-29 23:32:25 +01:00 committed by Brian J. Cardiff
parent 28b17b7dba
commit d55a34e851
3 changed files with 82 additions and 0 deletions

View File

@ -138,6 +138,12 @@ describe DummyDriver do
end
end
it "with a named tuple" do
with_dummy do |db|
db.query_one("3,4", as: {a: Int64, b: Int64}).should eq({a: 3i64, b: 4i64})
end
end
it "with as, just one" do
with_dummy do |db|
db.query_one("3", as: Int64).should eq(3i64)
@ -176,6 +182,14 @@ describe DummyDriver do
end
end
it "with as" do
with_dummy do |db|
value = db.query_one?("3,4", as: {a: Int64, b: Int64})
value.should be_a(NamedTuple(a: Int64, b: Int64)?)
value.should eq({a: 3i64, b: 4i64})
end
end
it "with as, just one" do
with_dummy do |db|
value = db.query_one?("3", as: Int64)
@ -200,6 +214,13 @@ describe DummyDriver do
end
end
it "queries with a named tuple" do
with_dummy do |db|
ary = db.query_all "3,4 1,2", as: {a: Int64, b: Int64}
ary.should eq([{a: 3, b: 4}, {a: 1, b: 2}])
end
end
it "queries with as, just one" do
with_dummy do |db|
ary = db.query_all "3 1", as: Int64

View File

@ -88,6 +88,21 @@ module DB
end
end
# Executes a *query* that expects a single row and returns it
# as a named tuple of the given *types* (the keys of the named tuple
# are not necessarily the column names).
#
# Raises `DB::Error` if there were no rows, or if there were more than one row.
#
# ```
# db.query_one "select name, age from contacts where id = ?", 1, as: {name: String, age: Int32}
# ```
def query_one(query, *args, as types : NamedTuple)
query_one(query, *args) do |rs|
rs.read(**types)
end
end
# Executes a *query* that expects a single row
# and returns the first column's value as the given *type*.
#
@ -141,6 +156,24 @@ module DB
end
end
# Executes a *query* that expects a single row and returns it
# as a named tuple of the given *types* (the keys of the named tuple
# are not necessarily the column names).
#
# Returns `nil` if there were no rows.
#
# Raises `DB::Error` if there were more than one row.
#
# ```
# result = db.query_one? "select name, age from contacts where id = ?", 1, as: {age: String, name: Int32}
# typeof(result) # => NamedTuple(age: String, name: Int32) | Nil
# ```
def query_one?(query, *args, as types : NamedTuple)
query_one(query, *args) do |rs|
rs.read(**types)
end
end
# Executes a *query* that expects a single row
# and returns the first column's value as the given *type*.
#
@ -184,6 +217,19 @@ module DB
end
end
# Executes a *query* and returns an array where each row is
# read as a named tuple of the given *types* (the keys of the named tuple
# are not necessarily the column names).
#
# ```
# contacts = db.query_all "select name, age from contacts", as: {name: String, age: Int32}
# ```
def query_all(query, *args, as types : NamedTuple)
query_all(query, *args) do |rs|
rs.read(**types)
end
end
# Executes a *query* and returns an array where the
# value of each row is read as the given *type*.
#

View File

@ -89,6 +89,11 @@ module DB
internal_read(*types)
end
# Reads the next columns and returns a named tuple of the values.
def read(**types : Class)
internal_read(**types)
end
private def internal_read(*types : *T) forall T
{% begin %}
Tuple.new(
@ -99,6 +104,16 @@ module DB
{% end %}
end
private def internal_read(**types : **T) forall T
{% begin %}
NamedTuple.new(
{% for name, type in T %}
{{ name }}: read({{type.instance}}),
{% end %}
)
{% end %}
end
# def read_blob
# yield ... io ....
# end