mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-11-12 10:49:29 +08:00
8dea99e600
* Render basic input * Make each cell type a separate struct * Add operation to set input value * Prototype communication * Make input name editable * Refactoring * Unify cell attribute setters * IO tests * Document input communication protocol in Runtime * Add more tests * Add persistence * Fix status update on cell attributes change * Rework persistence * Integrate input cell with insert mode * Add missing users keybinding * Mimic autofocus for dynamically inserted elements * Support input type selection * Support URL input and set up validation * Convert input error into a more meaningful io error * Add missing client checks * Consume every input only once per evaluation * Fixes
118 lines
3.1 KiB
Elixir
118 lines
3.1 KiB
Elixir
defmodule Livebook.Utils do
|
|
@moduledoc false
|
|
|
|
@type id :: binary()
|
|
|
|
@doc """
|
|
Generates a random binary id.
|
|
"""
|
|
@spec random_id() :: binary()
|
|
def random_id() do
|
|
:crypto.strong_rand_bytes(20) |> Base.encode32(case: :lower)
|
|
end
|
|
|
|
@doc """
|
|
Generates a random short binary id.
|
|
"""
|
|
@spec random_short_id() :: binary()
|
|
def random_short_id() do
|
|
:crypto.strong_rand_bytes(5) |> Base.encode32(case: :lower)
|
|
end
|
|
|
|
@doc """
|
|
Generates a random cookie for a distributed node.
|
|
"""
|
|
@spec random_cookie() :: atom()
|
|
def random_cookie() do
|
|
:crypto.strong_rand_bytes(42) |> Base.url_encode64() |> String.to_atom()
|
|
end
|
|
|
|
@doc """
|
|
Converts the given name to node identifier.
|
|
"""
|
|
@spec node_from_name(String.t()) :: atom()
|
|
def node_from_name(name) do
|
|
if name =~ "@" do
|
|
String.to_atom(name)
|
|
else
|
|
# Default to the same host as the current node
|
|
:"#{name}@#{node_host()}"
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Returns the host part of a node.
|
|
"""
|
|
@spec node_host() :: binary()
|
|
def node_host do
|
|
[_, host] = node() |> Atom.to_string() |> :binary.split("@")
|
|
host
|
|
end
|
|
|
|
@doc """
|
|
Registers the given process under `name` for the time of `fun` evaluation.
|
|
"""
|
|
@spec temporarily_register(pid(), atom(), (... -> any())) :: any()
|
|
def temporarily_register(pid, name, fun) do
|
|
Process.register(pid, name)
|
|
fun.()
|
|
after
|
|
Process.unregister(name)
|
|
end
|
|
|
|
@doc """
|
|
Returns a function that accesses list items by the given id.
|
|
|
|
## Examples
|
|
|
|
iex> list = [%{id: 1, name: "Jake"}, %{id: 2, name: "Amy"}]
|
|
iex> get_in(list, [Livebook.Utils.access_by_id(2), Access.key(:name)])
|
|
"Amy"
|
|
|
|
iex> list = [%{id: 1, name: "Jake"}, %{id: 2, name: "Amy"}]
|
|
iex> put_in(list, [Livebook.Utils.access_by_id(2), Access.key(:name)], "Amy Santiago")
|
|
[%{id: 1, name: "Jake"}, %{id: 2, name: "Amy Santiago"}]
|
|
|
|
An error is raised if the accessed structure is not a list:
|
|
|
|
iex> get_in(%{}, [Livebook.Utils.access_by_id(1)])
|
|
** (RuntimeError) Livebook.Utils.access_by_id/1 expected a list, got: %{}
|
|
"""
|
|
@spec access_by_id(term()) ::
|
|
Access.access_fun(data :: struct() | map(), current_value :: term())
|
|
def access_by_id(id) do
|
|
fn
|
|
:get, data, next when is_list(data) ->
|
|
data
|
|
|> Enum.find(fn item -> item.id == id end)
|
|
|> next.()
|
|
|
|
:get_and_update, data, next when is_list(data) ->
|
|
case Enum.split_while(data, fn item -> item.id != id end) do
|
|
{prev, [item | cons]} ->
|
|
case next.(item) do
|
|
{get, update} ->
|
|
{get, prev ++ [update | cons]}
|
|
|
|
:pop ->
|
|
{item, prev ++ cons}
|
|
end
|
|
|
|
_ ->
|
|
{nil, data}
|
|
end
|
|
|
|
_op, data, _next ->
|
|
raise "Livebook.Utils.access_by_id/1 expected a list, got: #{inspect(data)}"
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Validates if the given URL is syntactically valid.
|
|
"""
|
|
@spec valid_url?(String.t()) :: boolean()
|
|
def valid_url?(url) do
|
|
uri = URI.parse(url)
|
|
uri.scheme != nil and uri.host != nil and uri.host =~ "."
|
|
end
|
|
end
|