Packages
kino
0.12.3
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", "node_secret"]
@secret_attrs ["cookie_secret", "node_secret"]
@impl true
def init(attrs, ctx) do
{shared_cookie, shared_cookie_secret} =
AttributeStore.get_attribute({@global_key, :cookie}, {nil, nil})
{shared_node, shared_node_secret} =
AttributeStore.get_attribute({@global_key, :node}, {nil, nil})
node_secret = attrs["node_secret"] || shared_node_secret
node_secret_value = node_secret && System.get_env("LB_#{node_secret}")
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" => attrs["node"] || shared_node || "",
"node_secret" => node_secret || "",
"cookie" => attrs["cookie"] || shared_cookie || "",
"cookie_secret" => cookie_secret || "",
"use_node_secret" =>
if(shared_node_secret, do: true, else: Map.get(attrs, "use_node_secret", false)),
"use_cookie_secret" =>
if(shared_cookie, do: false, else: Map.get(attrs, "use_cookie_secret", true)),
"cookie_secret_value" => cookie_secret_value,
"node_secret_value" => node_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 in @secret_attrs,
do: send_event(ctx, ctx.origin, "update_node_info", %{field => fields["#{field}_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(%{"use_node_secret" => false, "node" => ""}), do: ""
def to_source(%{"use_node_secret" => true, "node_secret" => ""}), 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, "assign_to" => var} = attrs) do
var = if Kino.SmartCell.valid_variable_name?(var), do: var
call = build_call(code) |> build_var(var)
cookie = build_set_cookie(attrs)
node = build_node(attrs)
quote do
require Kino.RPC
node = unquote(node)
Node.set_cookie(node, unquote(cookie))
unquote(call)
end
|> Kino.SmartCell.quoted_to_string()
end
defp build_call(code) do
quote do
Kino.RPC.eval_string(node, unquote(quoted_code(code)), file: __ENV__.file)
end
end
defp quoted_code(code) do
{delimiter, code} =
if String.contains?(code, "\n") do
{~s["""], code <> "\n"}
else
{~s["], code}
end
{:sigil_S, [delimiter: delimiter], [{:<<>>, [], [code]}, []]}
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 build_node(%{"use_node_secret" => true, "node_secret" => secret}) do
quote do
String.to_atom(System.fetch_env!(unquote("LB_#{secret}")))
end
end
defp build_node(%{"node" => node}), do: String.to_atom(node)
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("node", value) do
AttributeStore.put_attribute({@global_key, :node}, {value, nil})
end
defp put_shared_attr("node_secret", value) do
AttributeStore.put_attribute({@global_key, :node}, {nil, 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("node_secret", node_secret) do
%{
"node_secret" => node_secret,
"node_secret_value" => System.get_env("LB_#{node_secret}")
}
end
defp update_fields(field, value), do: %{field => value}
end