Tablet
View SourceTablet renders tabular data as text for output to the console or any where else. Give it data in either of the following common tabular data shapes:
# List of matching maps (atom or string keys)
data = [
%{"id" => 1, "name" => "Puck"},
%{"id" => 2, "name" => "Nick Bottom"}
]
# List of matching key-value lists
data = [
[{"id", 1}, {"name", "Puck"}],
[{"id", 2}, {"name", "Nick Bottom"}]
]
Then call Tablet.puts/2
:
Tablet.puts(data)
#=> id name
#=> 1 Puck
#=> 2 Nick Bottom
While this shows a table with minimal styling, it's possible to create fancier tables with colors, borders and more.
Here are some of Tablet's features:
Kino.DataTable
-inspired API for ease of switching between Livebook and console output- Automatic column sizing
- Multi-column wrapping for tables with many rows and few columns
- Data eliding for long strings
- Customizable data formatting and styling
- Unicode support for emojis and other wide characters
IO.ANSI.ansidata/0
throughout- Small. No runtime dependencies. Intentionally minimal feature scope.
Example
Here's a more involved example:
iex> data = [
...> %{planet: "Mercury", orbital_period: 88},
...> %{planet: "Venus", orbital_period: 224.701},
...> %{planet: "Earth", orbital_period: 365.256},
...> %{planet: "Mars", orbital_period: 686.971}
...> ]
iex> formatter = fn
...> :__header__, :planet -> {:ok, "Planet"}
...> :__header__, :orbital_period -> {:ok, "Orbital Period"}
...> :orbital_period, value -> {:ok, "\#{value} days"}
...> _, _ -> :default
...> end
iex> Tablet.render(data, keys: [:planet, :orbital_period], formatter: formatter)
...> |> IO.ANSI.format(false)
...> |> IO.chardata_to_string()
"Planet Orbital Period \n" <>
"Mercury 88 days \n" <>
"Venus 224.701 days \n" <>
"Earth 365.256 days \n" <>
"Mars 686.971 days \n"
Note that normally you'd call IO.ANSI.format/2
without passing false
to
get colorized output and also call IO.puts/2
to write to a terminal.
Data formatting and column headers
Tablet naively converts data values and constructs column headers to
IO.ANSI.ansidata/0
. This may not be what you want. To customize this,
pass a 2-arity function using the :formatter
option. That function takes
the key and value as arguments and should return {:ok, ansidata}
. The special key
:__header__
is passed when constructing header row. Return :default
to use the default conversion.
Styling
Various table output styles are supported by passing an atom or 3-arity
function to the :style
parameter.
See the Style Gallery for the built-in styles.
If the built-in styles don't suffice, it is possible for you to add your own by
creating a function of the type Tablet.style_function/0
. Due to the desire
to minimize the main Tablet
code as much as possible, only a few helper
functions are available. See the built-in styles for code examples.
Ansidata
Tablet takes advantage of IO.ANSI.ansidata/0
everywhere. This makes it
easy to apply styling, colorization, and other transformations. However,
it can be hard to read. It's highly recommended to either call simplify/1
to
simplify the output for review or to call IO.ANSI.format/2
and then
IO.puts/2
to print it.
In a nutshell, IO.ANSI.ansidata/0
lets you create lists of strings to
print and intermix atoms like :red
or :blue
to indicate where ANSI escape
sequences should be inserted if supported. Tablet actually doesn't know what
any of the atoms means and passes them through. Elixir's IO.ANSI
module
does all of the work. If fact, if you find IO.ANSI
too limited, then you
could use an alternative like bunt and
include atoms like :chartreuse
which its formatter will understand.
Acknowledgements
Thanks to the Rust tabled project for showing what's possible.