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
enduse 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
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.
@spec registry() :: registry()
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.