From 3836d79a761bf133f0b31a75d00a68a02fd5d924 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Tue, 17 Jan 2023 06:04:01 +0000 Subject: [PATCH 1/7] allow_to_return_raw_or_keyword_list --- lib/ayesql/runner.ex | 24 ++++++++----- test/ayesql/runner_test.exs | 69 ++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 9 deletions(-) diff --git a/lib/ayesql/runner.ex b/lib/ayesql/runner.ex index d9889fb..7e4edcb 100644 --- a/lib/ayesql/runner.ex +++ b/lib/ayesql/runner.ex @@ -29,14 +29,22 @@ defmodule AyeSQL.Runner do [] end - def handle_result(%{columns: columns, rows: rows}, options) do - struct = options[:into] - columns = Enum.map(columns, &String.to_atom/1) + def handle_result(%{columns: columns, rows: rows} = raw_data, options) do + if options[:into] == :raw do + raw_data + else + atom_columns = Stream.map(columns, &String.to_atom/1) - rows - |> Stream.map(&Stream.zip(columns, &1)) - |> Enum.map(fn row -> - if struct, do: struct(struct, row), else: Map.new(row) - end) + rows + |> Stream.map(&Stream.zip(atom_columns, &1)) + |> Enum.map(fn row -> + case options[:into] do + nil -> Enum.into(row, %{}) + Map -> Enum.into(row, %{}) + Keyword -> Enum.into(row, []) + struct -> struct(struct, row) + end + end) + end end end diff --git a/test/ayesql/runner_test.exs b/test/ayesql/runner_test.exs index 810686a..823e384 100644 --- a/test/ayesql/runner_test.exs +++ b/test/ayesql/runner_test.exs @@ -3,6 +3,10 @@ defmodule AyeSQL.RunnerTest do alias AyeSQL.Runner + defmodule User do + defstruct [:username, :email] + end + describe "handle_result/1" do test "when columns are nil, returns empty list" do data = %{columns: nil} @@ -10,7 +14,7 @@ defmodule AyeSQL.RunnerTest do assert [] = Runner.handle_result(data) end - test "when rows are not empty, returns a list of rows with columns" do + test "when rows are not empty, returns a list of map rows by default" do data = %{ columns: ["username", "email"], rows: [ @@ -26,5 +30,68 @@ defmodule AyeSQL.RunnerTest do assert ^expected = Runner.handle_result(data) end + + test "when rows are not empty, returns a list of map rows" do + data = %{ + columns: ["username", "email"], + rows: [ + ["bob", "bob@example.com"], + ["alice", "alice@example.com"] + ] + } + + expected = [ + %{username: "bob", email: "bob@example.com"}, + %{username: "alice", email: "alice@example.com"} + ] + + assert ^expected = Runner.handle_result(data, into: Map) + end + + test "when rows are not empty, returns a list of keyword rows" do + data = %{ + columns: ["username", "email"], + rows: [ + ["bob", "bob@example.com"], + ["alice", "alice@example.com"] + ] + } + + expected = [ + [username: "bob", email: "bob@example.com"], + [username: "alice", email: "alice@example.com"] + ] + + assert expected == Runner.handle_result(data, into: Keyword) + end + + test "when rows are not empty, returns a list of structs rows" do + data = %{ + columns: ["username", "email"], + rows: [ + ["bob", "bob@example.com"], + ["alice", "alice@example.com"] + ] + } + + expected = [ + %User{username: "bob", email: "bob@example.com"}, + %User{username: "alice", email: "alice@example.com"} + ] + + assert expected == Runner.handle_result(data, into: User) + end + + test "when rows are not empty, returns a raw result with columns and rows" do + data = %{ + columns: ["username", "email"], + rows: [ + ["bob", "bob@example.com"], + ["alice", "alice@example.com"] + ] + } + + assert data == Runner.handle_result(data, into: :raw) + end end end From 528a8d0b65432d60ae6214f938d096ddda42d31b Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Tue, 17 Jan 2023 06:21:39 +0000 Subject: [PATCH 2/7] allow to pass empty list or map to into --- lib/ayesql/runner.ex | 1 + test/ayesql/runner_test.exs | 19 ++----------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/lib/ayesql/runner.ex b/lib/ayesql/runner.ex index 7e4edcb..a202e5b 100644 --- a/lib/ayesql/runner.ex +++ b/lib/ayesql/runner.ex @@ -42,6 +42,7 @@ defmodule AyeSQL.Runner do nil -> Enum.into(row, %{}) Map -> Enum.into(row, %{}) Keyword -> Enum.into(row, []) + enum when enum == [] or enum == %{} -> Enum.into(row, enum) struct -> struct(struct, row) end end) diff --git a/test/ayesql/runner_test.exs b/test/ayesql/runner_test.exs index 823e384..d5c70d3 100644 --- a/test/ayesql/runner_test.exs +++ b/test/ayesql/runner_test.exs @@ -28,24 +28,8 @@ defmodule AyeSQL.RunnerTest do %{username: "alice", email: "alice@example.com"} ] - assert ^expected = Runner.handle_result(data) - end - - test "when rows are not empty, returns a list of map rows" do - data = %{ - columns: ["username", "email"], - rows: [ - ["bob", "bob@example.com"], - ["alice", "alice@example.com"] - ] - } - - expected = [ - %{username: "bob", email: "bob@example.com"}, - %{username: "alice", email: "alice@example.com"} - ] - assert ^expected = Runner.handle_result(data, into: Map) + assert ^expected = Runner.handle_result(data, into: %{}) end test "when rows are not empty, returns a list of keyword rows" do @@ -63,6 +47,7 @@ defmodule AyeSQL.RunnerTest do ] assert expected == Runner.handle_result(data, into: Keyword) + assert expected == Runner.handle_result(data, into: []) end test "when rows are not empty, returns a list of structs rows" do From c12b7cf73ef49aee886af1ef13e57f198e4b2746 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 9 Jul 2023 09:24:36 +0100 Subject: [PATCH 3/7] mention in docs about new option --- lib/ayesql.ex | 32 ++++++++++++++++++++++++++++++++ test/support/script.sql | 5 +++++ 2 files changed, 37 insertions(+) create mode 100644 test/support/script.sql diff --git a/lib/ayesql.ex b/lib/ayesql.ex index 2f76415..cbab2a0 100644 --- a/lib/ayesql.ex +++ b/lib/ayesql.ex @@ -131,6 +131,38 @@ defmodule AyeSQL do ] } ``` + AyeSQL also allows you to choose the type of returned data structures. + Instead of the default map you can also pass an into option to your query + possible values are: + - Map or %{} + - Keyword or [] + - A struct + - :raw - will return unmodified Postgrex result + + ```elixir + iex> Queries.get_avg_clicks(params, into: Keyword) + {:ok, + [ + [day: ..., count: ...], + [day: ..., count: ...], + [day: ..., count: ...], + ... + ] + } + ``` + + ```elixir + iex> defmodule AvgClicks do defstruct [:day, :count] end + iex> Queries.get_avg_clicks(params, into: AvgClicks) + {:ok, + [ + %AvgClicks{day: ..., count: ...}, + %AvgClicks{day: ..., count: ...}, + %AvgClicks{day: ..., count: ...}, + ... + ] + } + ``` """ alias AyeSQL.Compiler alias AyeSQL.Query diff --git a/test/support/script.sql b/test/support/script.sql new file mode 100644 index 0000000..9f10b2b --- /dev/null +++ b/test/support/script.sql @@ -0,0 +1,5 @@ +-- Runs multiple sql statements +-- name: set_schema +-- type: script +CREATE SCHEMA schema1; +CREATE SCHEMA schema2; From 59069f4007b058a36ce585e198c3b6a8b524df53 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Thu, 16 Nov 2023 17:48:44 +0000 Subject: [PATCH 4/7] cr suggestions --- lib/ayesql/runner.ex | 42 +++++++++++++++++++------------------ test/ayesql/runner_test.exs | 4 +--- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/ayesql/runner.ex b/lib/ayesql/runner.ex index a202e5b..a14561e 100644 --- a/lib/ayesql/runner.ex +++ b/lib/ayesql/runner.ex @@ -21,31 +21,33 @@ defmodule AyeSQL.Runner do # Handles the result. @doc false - @spec handle_result(map()) :: [map() | struct()] - @spec handle_result(map(), keyword()) :: [map() | struct()] + @spec handle_result(map()) :: [map() | struct() | keyword()] + @spec handle_result(map(), keyword()) :: [map() | struct() | keyword()] def handle_result(result, options \\ []) + def handle_result(data, options) when is_list(options) do + handle_result(data, Map.new(options)) + end + + def handle_result(raw_data, %{into: :raw}) do + raw_data + end + def handle_result(%{columns: nil}, _options) do [] end - def handle_result(%{columns: columns, rows: rows} = raw_data, options) do - if options[:into] == :raw do - raw_data - else - atom_columns = Stream.map(columns, &String.to_atom/1) - - rows - |> Stream.map(&Stream.zip(atom_columns, &1)) - |> Enum.map(fn row -> - case options[:into] do - nil -> Enum.into(row, %{}) - Map -> Enum.into(row, %{}) - Keyword -> Enum.into(row, []) - enum when enum == [] or enum == %{} -> Enum.into(row, enum) - struct -> struct(struct, row) - end - end) - end + def handle_result(%{columns: columns, rows: rows}, options) do + atom_columns = Stream.map(columns, &String.to_atom/1) + + rows + |> Stream.map(&Stream.zip(atom_columns, &1)) + |> Enum.map(fn row -> + case options[:into] do + struct when is_struct(struct) -> struct(struct, row) + [] -> Enum.into(row, []) + _ -> Enum.into(row, %{}) + end + end) end end diff --git a/test/ayesql/runner_test.exs b/test/ayesql/runner_test.exs index d5c70d3..3fca0b5 100644 --- a/test/ayesql/runner_test.exs +++ b/test/ayesql/runner_test.exs @@ -28,7 +28,6 @@ defmodule AyeSQL.RunnerTest do %{username: "alice", email: "alice@example.com"} ] - assert ^expected = Runner.handle_result(data, into: Map) assert ^expected = Runner.handle_result(data, into: %{}) end @@ -46,7 +45,6 @@ defmodule AyeSQL.RunnerTest do [username: "alice", email: "alice@example.com"] ] - assert expected == Runner.handle_result(data, into: Keyword) assert expected == Runner.handle_result(data, into: []) end @@ -64,7 +62,7 @@ defmodule AyeSQL.RunnerTest do %User{username: "alice", email: "alice@example.com"} ] - assert expected == Runner.handle_result(data, into: User) + assert expected == Runner.handle_result(data, into: %User{}) end test "when rows are not empty, returns a raw result with columns and rows" do From a4d65919e7d9957c4d504d59fde29b83f5242cb3 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Wed, 29 Nov 2023 18:03:33 +0000 Subject: [PATCH 5/7] unused file --- test/support/script.sql | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 test/support/script.sql diff --git a/test/support/script.sql b/test/support/script.sql deleted file mode 100644 index 9f10b2b..0000000 --- a/test/support/script.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Runs multiple sql statements --- name: set_schema --- type: script -CREATE SCHEMA schema1; -CREATE SCHEMA schema2; From b95d1c4e3122de2b56d39b782796ef74eb7c3b02 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Thu, 30 Nov 2023 06:51:31 +0000 Subject: [PATCH 6/7] cr suggestions --- lib/ayesql.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ayesql.ex b/lib/ayesql.ex index cbab2a0..cc79e23 100644 --- a/lib/ayesql.ex +++ b/lib/ayesql.ex @@ -132,15 +132,15 @@ defmodule AyeSQL do } ``` AyeSQL also allows you to choose the type of returned data structures. - Instead of the default map you can also pass an into option to your query + Instead of the default map you can also pass an `into` option to your query possible values are: - - Map or %{} - - Keyword or [] + - an empty map: `Map.new()` or `%{}` + - an empty list: Keyword.new()` or `[]` - A struct - - :raw - will return unmodified Postgrex result + - `:raw` - will return unmodified Postgrex result ```elixir - iex> Queries.get_avg_clicks(params, into: Keyword) + iex> Queries.get_avg_clicks(params, into: []) {:ok, [ [day: ..., count: ...], From 5725c755a7a2576899b6769a60ed5875944c4e2f Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Thu, 30 Nov 2023 06:56:13 +0000 Subject: [PATCH 7/7] lowercase a --- lib/ayesql.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ayesql.ex b/lib/ayesql.ex index cc79e23..0b48b2b 100644 --- a/lib/ayesql.ex +++ b/lib/ayesql.ex @@ -136,7 +136,7 @@ defmodule AyeSQL do possible values are: - an empty map: `Map.new()` or `%{}` - an empty list: Keyword.new()` or `[]` - - A struct + - a struct - `:raw` - will return unmodified Postgrex result ```elixir