Skip to content

Commit 5704327

Browse files
Extend validations for tabular list to enforce the same columns (#19)
1 parent d902c8a commit 5704327

2 files changed

Lines changed: 60 additions & 4 deletions

File tree

lib/table/reader/enumerable.ex

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ defmodule Table.Reader.Enumerable do
3737
case columns_for(head) do
3838
{:ok, columns} ->
3939
meta = %{columns: columns}
40-
enum = Table.Mapper.map(enum, &record_values(&1, columns))
40+
enum = Table.Mapper.map(enum, &record_values(&1, columns, head))
4141
{:rows, meta, enum}
4242

4343
:error ->
@@ -70,11 +70,17 @@ defmodule Table.Reader.Enumerable do
7070
defp keyval_columns([{key, _} | rest], columns), do: keyval_columns(rest, [key | columns])
7171
defp keyval_columns(_list, _columns), do: :error
7272

73-
defp record_values(record, columns) when is_list(record) do
73+
defp record_values(record, columns, _head_record) when is_list(record) do
7474
keyval_values(record, columns)
7575
end
7676

77-
defp record_values(record, columns) when is_map(record) do
77+
defp record_values(record, columns, head_record) when is_map(record) do
78+
if map_size(record) > map_size(head_record) do
79+
missing_columns = Map.keys(record) -- columns
80+
81+
raise "map records must have the same columns, missing column(s) #{inspect(missing_columns)} in #{inspect(head_record)}"
82+
end
83+
7884
Enum.map(columns, fn column ->
7985
case record do
8086
%{^column => value} ->
@@ -86,7 +92,7 @@ defmodule Table.Reader.Enumerable do
8692
end)
8793
end
8894

89-
defp record_values(record, _columns) do
95+
defp record_values(record, _columns, _head_record) do
9096
raise "invalid table record: #{inspect(record)}"
9197
end
9298

@@ -96,6 +102,14 @@ defmodule Table.Reader.Enumerable do
96102
[value | keyval_values(rest, columns)]
97103
end
98104

105+
defp keyval_values([], [column | _columns]) do
106+
raise "key-value records must have the same columns, missing #{inspect(column)}"
107+
end
108+
109+
defp keyval_values([{column, _value}], []) do
110+
raise "key-value records must have the same columns, missing #{inspect(column)}"
111+
end
112+
99113
defp keyval_values([{actual, _value} | _rest], [column | _columns]) do
100114
raise "key-value records must have columns in the same order, expected #{inspect(column)}, but got #{inspect(actual)}"
101115
end

test/table/reader_test.exs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ defmodule Table.ReaderTest do
7474
end
7575

7676
test "enumerating rows raises on missing map element" do
77+
# First row has more columns than later rows
7778
assert {:rows, %{}, enum} =
7879
Table.Reader.init([
7980
%{"x" => 1, "y" => 1},
@@ -85,6 +86,19 @@ defmodule Table.ReaderTest do
8586
fn ->
8687
Enum.to_list(enum)
8788
end
89+
90+
# Later row has more columns than first row
91+
assert {:rows, %{}, enum} =
92+
Table.Reader.init([
93+
%{"x" => 2},
94+
%{"x" => 1, "y" => 1}
95+
])
96+
97+
assert_raise RuntimeError,
98+
~s/map records must have the same columns, missing column(s) ["y"] in %{"x" => 2}/,
99+
fn ->
100+
Enum.to_list(enum)
101+
end
88102
end
89103

90104
test "enumerating rows raises on keyval order mismatch" do
@@ -112,6 +126,34 @@ defmodule Table.ReaderTest do
112126
Enum.to_list(enum)
113127
end
114128
end
129+
130+
test "enumerating rows raises on missing keyval element" do
131+
# First row has more columns than later rows
132+
assert {:rows, %{}, enum} =
133+
Table.Reader.init([
134+
[{"x", 1}, {"y", 1}],
135+
[{"x", 2}]
136+
])
137+
138+
assert_raise RuntimeError,
139+
~s/key-value records must have the same columns, missing "y"/,
140+
fn ->
141+
Enum.to_list(enum)
142+
end
143+
144+
# Later row has more columns than first row
145+
assert {:rows, %{}, enum} =
146+
Table.Reader.init([
147+
[{"x", 2}],
148+
[{"x", 1}, {"y", 1}]
149+
])
150+
151+
assert_raise RuntimeError,
152+
~s/key-value records must have the same columns, missing "y"/,
153+
fn ->
154+
Enum.to_list(enum)
155+
end
156+
end
115157
end
116158

117159
describe "map init/1" do

0 commit comments

Comments
 (0)