Visualize.VirtualSources (Visualize v0.8.3)

Copy Markdown View Source

A host's virtual-source functions (spec/08 §10.5.8, D-194), written as Elixir in the host's own module and tested with ExUnit, as generators are (spec/14 §3.13, D-177):

defmodule MyApp.Sources do
  use Visualize.VirtualSources

  defsource health(rows) do
    cpu = Enum.map(rows, & &1.cpu)
    %{state: if(List.last(cpu) > 90, do: :error, else: :ok)}
  end

  defsource bins(inputs, context) do
    [lo, hi] = context["vars"]["bin_domain"] || [0, 100]
    ...
  end
end

use Visualize.VirtualSources imports defsource/2 and compiles __virtual_sources__/0, each function's name, its arity and its source text, [{name, arity, source}] in definition order. A function of two arguments is called with its inputs and its context (spec/08 §10.5.6, D-228), one of one argument with its inputs alone. The text is captured when the module compiles, since a release strips the debug information a later reading would need; the builder shows it.

A host's function is trusted — the sandbox's checker does not read it — but it runs through the same bounded runner as a stored one (Visualize.VirtualSource.Sandbox.run/3), so a runaway ends as a fault, not as the board's crash. A virtual source names it with function: :health; the host registers the module with config :visualize, virtual_sources: [MyApp.Sources] or the board's virtual_sources: option.

Examples

iex> defmodule Doc.Sources do
...>   use Visualize.VirtualSources
...>   defsource doubled(rows), do: Enum.map(rows, &(&1 * 2))
...> end
iex> Doc.Sources.doubled([1, 2])
[2, 4]
iex> Doc.Sources.__virtual_sources__()
[{:doubled, 1, "fn rows -> Enum.map(rows, &(&1 * 2)) end"}]
iex> Visualize.VirtualSources.registry([Doc.Sources])
%{doubled: Doc.Sources}

Summary

Types

Each host function's name by the module that defines it.

Functions

A host's virtual-source function: defsource name(inputs) do … end is a public def of one argument, and defsource name(inputs, context) do … end one of two, which is called with its context (spec/08 §10.5.6, D-228); either is recorded with its arity and source text under the module's __virtual_sources__/0. A function of several clauses is defined clause by clause, as any function is, and listed once with every clause.

As registry/1 with no modules: the configuration's alone.

The registry of modules then config :visualize, virtual_sources: (spec/08 §10.5.8): each function's name by the module that defines it. Raises ArgumentError for a module without __virtual_sources__/0 and for a name two modules define.

Types

registry()

@type registry() :: %{required(atom()) => module()}

Each host function's name by the module that defines it.

Functions

defsource(head, body)

(macro)

A host's virtual-source function: defsource name(inputs) do … end is a public def of one argument, and defsource name(inputs, context) do … end one of two, which is called with its context (spec/08 §10.5.6, D-228); either is recorded with its arity and source text under the module's __virtual_sources__/0. A function of several clauses is defined clause by clause, as any function is, and listed once with every clause.

registry()

@spec registry() :: registry()

As registry/1 with no modules: the configuration's alone.

registry(modules)

@spec registry([module()]) :: registry()

The registry of modules then config :visualize, virtual_sources: (spec/08 §10.5.8): each function's name by the module that defines it. Raises ArgumentError for a module without __virtual_sources__/0 and for a name two modules define.