Packages
kino
0.11.0
0.19.0
0.18.0
0.17.0
0.16.1
0.16.0
0.15.3
0.15.2
0.15.1
0.15.0
0.14.2
0.14.1
0.14.0
0.13.2
0.13.1
0.13.0
0.12.3
0.12.2
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.2
0.6.1
0.6.0
retired
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Interactive widgets for Livebook
Current section
Files
Jump to
Current section
Files
lib/kino/remote_execution_cell.ex
defmodule Kino.RemoteExecutionCell do
@moduledoc false
use Kino.JS, assets_path: "lib/assets/remote_execution_cell"
use Kino.JS.Live
use Kino.SmartCell, name: "Remote execution"
alias Kino.AttributeStore
@default_code ":ok"
@global_key __MODULE__
@global_attrs ["node", "cookie", "cookie_secret"]
@impl true
def init(attrs, ctx) do
{shared_cookie, shared_cookie_secret} =
AttributeStore.get_attribute({@global_key, :cookie}, {nil, nil})
node = attrs["node"] || AttributeStore.get_attribute({@global_key, :node}) || ""
cookie_secret = attrs["cookie_secret"] || shared_cookie_secret
cookie_secret_value = cookie_secret && System.get_env("LB_#{cookie_secret}")
fields = %{
"assign_to" => attrs["assign_to"] || "",
"node" => node,
"cookie" => attrs["cookie"] || shared_cookie || "",
"cookie_secret" => cookie_secret || "",
"use_cookie_secret" =>
if(shared_cookie, do: false, else: Map.get(attrs, "use_cookie_secret", true)),
"cookie_secret_value" => cookie_secret_value
}
ctx = assign(ctx, fields: fields)
{:ok, ctx, editor: [attribute: "code", language: "elixir", default_source: @default_code]}
end
@impl true
def handle_connect(ctx) do
payload = %{fields: ctx.assigns.fields}
{:ok, payload, ctx}
end
@impl true
def handle_event("update_field", %{"field" => field, "value" => value}, ctx) do
ctx = update(ctx, :fields, &Map.put(&1, field, value))
if field in @global_attrs, do: put_shared_attr(field, value)
fields = update_fields(field, value)
if field == "cookie_secret",
do: send_event(ctx, ctx.origin, "update_node_info", fields["cookie_secret_value"])
broadcast_event(ctx, "update_field", %{"fields" => fields})
{:noreply, ctx}
end
@impl true
def to_attrs(ctx) do
Map.delete(ctx.assigns.fields, "cookie_secret_value")
end
@impl true
def to_source(%{"code" => ""}), do: ""
def to_source(%{"node" => ""}), do: ""
def to_source(%{"use_cookie_secret" => false, "cookie" => ""}), do: ""
def to_source(%{"use_cookie_secret" => true, "cookie_secret" => ""}), do: ""
def to_source(%{"code" => code} = attrs) do
code = Code.string_to_quoted(code)
to_source(attrs, code)
end
defp to_source(%{"node" => node, "assign_to" => var} = attrs, {:ok, code}) do
var = if Kino.SmartCell.valid_variable_name?(var), do: var
call = build_call(code) |> build_var(var)
cookie = build_set_cookie(attrs)
quote do
node = unquote(String.to_atom(node))
Node.set_cookie(node, unquote(cookie))
unquote(call)
end
|> Kino.SmartCell.quoted_to_string()
end
defp to_source(%{"code" => code}, {:error, _reason}) do
"# Invalid code for RPC, reproducing the error below\n" <>
Kino.SmartCell.quoted_to_string(
quote do
Code.string_to_quoted!(unquote(code))
end
)
end
defp build_call(code) do
quote do
:erpc.call(node, fn -> unquote(code) end)
end
end
defp build_var(call, nil), do: call
defp build_var(call, var) do
quote do
unquote({String.to_atom(var), [], nil}) = unquote(call)
end
end
defp build_set_cookie(%{"use_cookie_secret" => true, "cookie_secret" => secret}) do
quote do
String.to_atom(System.fetch_env!(unquote("LB_#{secret}")))
end
end
defp build_set_cookie(%{"cookie" => cookie}), do: String.to_atom(cookie)
defp put_shared_attr("cookie", value) do
AttributeStore.put_attribute({@global_key, :cookie}, {value, nil})
end
defp put_shared_attr("cookie_secret", value) do
AttributeStore.put_attribute({@global_key, :cookie}, {nil, value})
end
defp put_shared_attr(field, value) do
AttributeStore.put_attribute({@global_key, String.to_atom(field)}, value)
end
defp update_fields("cookie_secret", cookie_secret) do
%{
"cookie_secret" => cookie_secret,
"cookie_secret_value" => System.get_env("LB_#{cookie_secret}")
}
end
defp update_fields(field, value), do: %{field => value}
end