defmodule ExDump do
@moduledoc """
Helper utility for dumping variables in EEx templates for easy inspection.
"""
use Phoenix.HTML
@doc """
Dumps a term to html in a easy to read format.
"""
@spec dump(any(), keyword()) :: {:safe, String.t()}
def dump(term, opts \\ []) do
theme = Keyword.get(opts, :theme, :default)
{:safe, dump_html} = to_html(term)
[
~s(
),
dump_html,
~s(
),
css(theme)
]
|> Enum.join("\n")
|> raw()
end
defp to_html(nil), do: dump_simple_term(:empty, "nil")
defp to_html(value) when is_binary(value), do: dump_simple_term(:binary, value)
defp to_html(value) when is_boolean(value), do: dump_simple_term(:boolean, value)
defp to_html(value) when is_integer(value), do: dump_simple_term(:integer, value)
defp to_html(value) when is_tuple(value), do: dump_simple_term(:tuple, inspect(value))
defp to_html(%Decimal{} = value), do: dump_simple_term(:decimal, value)
defp to_html(%Date{} = value), do: dump_simple_term("Date", value)
defp to_html(%DateTime{} = value), do: dump_simple_term("Datetime", value)
defp to_html(value) when is_atom(value), do: dump_simple_term(:atom, inspect(value))
defp to_html(%NaiveDateTime{} = value), do: dump_simple_term("Naive Datetime", value)
defp to_html(%{__struct__: struct} = value) do
struct_name = inspect(struct)
value
|> Map.from_struct()
|> dump_complex_term("map", ~s[Struct: #{struct_name}])
end
defp to_html(value) when is_map(value), do: dump_complex_term(value, "map", "Map")
defp to_html(values) when is_list(values) do
values
|> Enum.with_index()
|> Enum.map(fn {value, index} -> {index + 1, value} end)
|> dump_complex_term("list", "List")
end
# catch all
defp to_html(values), do: dump_simple_term("uknown", inspect(values))
defp dump_simple_term(type, value)
when type in [:atom, :binary, :boolean, :decimal, :integer, :tuple] do
"""
"""
|> raw()
end
defp dump_simple_term(type, value) do
"""
"""
|> raw()
end
defp dump_complex_term(values, name, title) do
fingerprint = random_string()
rows =
values
|> Enum.map(fn {key, value} ->
{:safe, value_html} = to_html(value)
"""
|
#{value_html}
|
"""
end)
|> Enum.join("\n")
toggle_javascript =
"var elements = document.getElementsByClassName('#{fingerprint}'); " <>
"for(var i = 0; i < elements.length; i++) { " <>
"elements[i].style.display = elements[i].style.display == 'none' ? '' : 'none'; }"
"""
"""
|> raw()
end
defp css(:default) do
"""
"""
end
defp random_string(length \\ 6) do
length |> :crypto.strong_rand_bytes() |> Base.url_encode64() |> binary_part(0, length)
end
end