Current section
Files
Jump to
Current section
Files
lib/telemetry_handler.ex
defmodule TimeTravel.TelemetryHandler do
def live_view_event_handler(name, measurement, metadata, context) do
event_name =
name
|> Enum.map(&to_string/1)
|> Enum.map(&String.replace(&1, "_", " "))
|> Enum.map_join(" ", &String.capitalize/1)
{:ok, clean_assigns} =
metadata.socket.assigns
|> safe_assigns()
|> Jason.encode()
# Positive: no negative number keys
# monotonic: always increasing so the list can be sorted
time = System.unique_integer([:positive, :monotonic])
# Time key is cast to a string separately so that "time" stays an integer in the JSON response
# and makes the retrieval of indices easier
time_key = to_string(time)
# Store original assigns in our server
GenServer.cast(
TimeTravel.Jumper,
{:set, metadata.socket.id, time_key, Map.delete(metadata.socket.assigns, :flash)}
)
endpoint = Application.get_env(:time_travel, :endpoint)
# Send clean assigns to be inspected through the socket
endpoint.broadcast("lvdbg:#{metadata.socket.id}", "lv_event", %{
payload: clean_assigns,
time: time,
name: event_name
})
end
# Make assigns JSON serializable
def safe_assigns(assigns) do
Enum.reduce(assigns, %{}, fn
{_key, %Ecto.Association.NotLoaded{}}, acc ->
acc
{key, %DateTime{} = v}, acc ->
Map.put(acc, key, DateTime.to_iso8601(v))
{key, %Date{} = v}, acc ->
Map.put(acc, key, Date.to_iso8601(v))
{key, value}, acc when is_struct(value) ->
clean_struct =
value
|> Map.delete(:__meta__)
|> Map.from_struct()
Map.put(acc, key, safe_assigns(clean_struct))
{key, value}, acc when is_function(value) ->
Map.put(acc, key, inspect(value))
{key, value}, acc when is_map(value) ->
Map.put(acc, key, safe_assigns(value))
{key, value}, acc when is_list(value) ->
Map.put(acc, key, Enum.map(value, &safe_assign/1))
{key, value}, acc when is_tuple(value) ->
Map.put(acc, key, safe_assigns(Tuple.to_list(value)))
{_key, value}, acc when is_pid(value) ->
acc
{key, value}, acc ->
Map.put(acc, key, value)
%DateTime{} = v, _acc ->
DateTime.to_iso8601(v)
%Date{} = v, _acc ->
Date.to_iso8601(v)
v, _acc when is_struct(v) ->
v |> Map.delete(:__meta__) |> Map.from_struct() |> safe_assigns()
v, _acc when is_function(v) ->
inspect(v)
v, _acc when is_map(v) ->
safe_assigns(v)
v, _acc when is_tuple(v) ->
v |> Tuple.to_list() |> safe_assigns()
v, acc when is_pid(v) ->
acc
v, _acc ->
IO.inspect v, label: "Got the blank handler"
v
end)
end
# This is for when we have a list of something
def safe_assign(%DateTime{} = v), do: DateTime.to_iso8601(v)
def safe_assign(%Date{} = v), do: Date.to_iso8601(v)
def safe_assign(v) when is_function(v), do: inspect(v)
def safe_assign(v) when is_map(v), do: safe_assigns(v)
def safe_assign(v) when is_tuple(v), do: v |> Tuple.to_list() |> safe_assigns()
def safe_assign(v) when is_pid(v), do: inspect(v)
def safe_assign(v) when is_struct(v) do
v
|> Map.delete(:__meta__)
|> Map.from_struct()
end
def safe_assign(v), do: v
end