From b841e1628d5e65011c7d4d553a5297a9b07ced8a Mon Sep 17 00:00:00 2001 From: fusillicode Date: Sun, 9 Apr 2017 15:51:41 +0200 Subject: [PATCH] Add support for keyword lists (Issue #12) --- lib/tabula.ex | 44 ++++++++++++++++++++++++++------------------ test/tabula_test.exs | 15 +++++++++++++++ 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/lib/tabula.ex b/lib/tabula.ex index 856dc90..a4da12e 100644 --- a/lib/tabula.ex +++ b/lib/tabula.ex @@ -26,6 +26,10 @@ defmodule Tabula do ] @default_sheet :org_mode + @access_functions [ + default: [access_functions: [get: &Map.get/3, keys: &Map.keys/1]], + keywords: [access_functions: [get: &Keyword.get/3, keys: &Keyword.keys/1]] + ] defmacro __using__(opts) do quote do @@ -51,27 +55,33 @@ defmodule Tabula do |> IO.puts end - def render_table(rows, opts \\ []) do + def render_table([h|_] = rows, opts \\ []) do + opts = set_access_functions(opts, h) cols = extract_cols(rows, opts) _render_table(rows, cols, opts) |> :erlang.list_to_binary end - def max_widths(cols, rows) do - max_index = rows - |> length - |> strlen - cols - |> map(fn k -> + defp set_access_functions(opts, a_row) do + opts + |> Keyword.merge(@access_functions[:default]) + |> (fn (opts) -> + is_list(a_row) && Keyword.merge(opts, @access_functions[:keywords]) || opts + end).() + end + + def max_widths(cols, rows, opts \\ @access_functions[:default]) do + extraction_function = opts[:access_functions][:get] + max_index = rows |> length |> strlen + cols |> map(fn k -> max([ - strlen(k), max_index - | map(rows, &(Map.get(&1, k) |> strlen)) + strlen(k), max_index | map(rows, &(extraction_function.(&1, k, nil) |> strlen)) ]) end) end defp _render_table(rows, [_|_]=cols, opts) do - widths = max_widths(cols, rows) + widths = max_widths(cols, rows, opts) formatters = widths |> formatters(opts) spacers = widths |> spacers(opts) @@ -87,12 +97,10 @@ defmodule Tabula do end - defp extract_cols([first|_]=_rows, opts) do + defp extract_cols([first|_] = _rows, opts) do case opts[:only] do - nil -> - first |> Map.keys - cols when is_list(cols) -> - cols + nil -> opts[:access_functions][:keys].(first) + cols when is_list(cols) -> cols end end @@ -142,11 +150,11 @@ defmodule Tabula do defp strlen(x), do: render_cell(x) |> String.length() - defp values(cols, {row, index}, _opts) do + defp values(cols, {row, index}, opts) do cols |> map(fn (@index) -> index+1 - (col) -> Map.get(row, col) - end) + (col) -> opts[:access_functions][:get].(row, col, nil) + end) end defp style(style, opts) do diff --git a/test/tabula_test.exs b/test/tabula_test.exs index 0d4fc7c..0a23ee0 100644 --- a/test/tabula_test.exs +++ b/test/tabula_test.exs @@ -145,4 +145,19 @@ defmodule TabulaTest do assert table == expect end + test "Support keyword lists and preserve ordering of columns" do + rows = [ + [name: "ecto", point: %Point{x: 0, y: 0}, version: Version.parse!("2.0.4")], + [name: "phoenix", point: %Point{x: 1, y: 0}, version: Version.parse!("1.2.0")] + ] + table = Tabula.render_table(rows) + + expect = """ + :name | :point | :version + --------+-------------------------------+--------- + ecto | %TabulaTest.Point{x: 0, y: 0} | 2.0.4 + phoenix | %TabulaTest.Point{x: 1, y: 0} | 1.2.0 + """ + assert table == expect + end end