Dmn.Extension behaviour (dmn v0.1.0)

Copy Markdown View Source

A set of additions to DMN 1.6 that a caller switches on when it compiles a model (DD-28), as Feel.Extension is for FEEL (D-81 of feel).

DMN 1.6 defines what a model means, and the models that exist were written for other versions and other engines. An extension is a module that says what it adds, and the compiler asks the extensions that are on at the few places where it decides. Nothing in the compiler names an extension.

Dmn.compile(model, extensions: [:dmn_1_1_types])

The option takes names (registry/0) and modules that implement this behaviour. It is [] by default, and then the engine is DMN 1.6 and nothing else. The extensions are those of the compile: they are not a property of a run, and the compiled model does not hold them. A name that is not one, a module that is not an extension, and two extensions that give one name two meanings are {:error, %Dmn.Error{kind: :compile, code: :invalid_options}}, as the options of a run are.

What an extension can do

An extension adds: it never changes what a model that is valid without it means. What it adds was an error before (a typeRef that names no type), and a name that the model defines itself is the model's, whatever an extension says of it. The DMN TCK is run with the extensions of Dmn.TCK on, and a node that is exact without them must stay exact.

CallbackAdds
type_aliases/0names of types that stand for another type, such as the names that DMN 1.1 gave the date and time types
feel_extensions/0the Feel.Extensions that the expressions of the model are prepared with
external_functions/0the kinds of external function that the extension runs (:java), each with the kind of FEEL ("java") that it is run as

External functions

A functionDefinition of a kind other than FEEL (kind="Java") describes a function that something outside the model runs, and DMN 1.6 does not say how. The engine does not run one: the function is null and an :unsupported_function_kind error. An extension that names the kind in external_functions/0 has it read as the external function of FEEL of that kind, with the body of the definition for the description, and the function of FEEL extension that runs that kind (Feel.Analysis.External, D-211 of feel) is the one that feel_extensions/0 brings. dmn does not know any kind: dmn_java is the one that runs Java.

Summary

Callbacks

The kinds of external function that the extension runs: the kind of a functionDefinition (:java, :pmml, :onnx) to the kind that the function is in FEEL, the key of function(...) external {kind: ...} ("java"). A function of such a kind is compiled to the external function of FEEL, which the extensions of FEEL that the extension brings have to run (feel_extensions/0).

The extensions of FEEL that the expressions of the model are prepared with (Feel.Extension: names or modules). Those of all the extensions that are on are given together.

The name of the extension, for the messages about it.

Type names that stand for a type of FEEL: the name that is written in a typeRef, to the name of the type it stands for (Feel.Type.builtin/1). A model that defines an item definition of a name is not asked: its definition is the type.

Functions

The external function kinds that the extensions that are on run: the kind of the functionDefinition to the kind in FEEL. A later extension wins for a kind that two give.

The extensions of FEEL that the extensions that are on bring, each once, in the order given.

The extensions that extensions: knows by name, each with the module it stands for.

The modules for a list of names and modules, each once, in the order given. The names are those of registry/0, or of the map given as the second argument.

The type aliases of the extensions that are on, from the name to the type it stands for. resolve/1 has seen that no two give one name two types.

Callbacks

external_functions()

(optional)
@callback external_functions() :: %{optional(:java | :pmml | :onnx) => String.t()}

The kinds of external function that the extension runs: the kind of a functionDefinition (:java, :pmml, :onnx) to the kind that the function is in FEEL, the key of function(...) external {kind: ...} ("java"). A function of such a kind is compiled to the external function of FEEL, which the extensions of FEEL that the extension brings have to run (feel_extensions/0).

feel_extensions()

(optional)
@callback feel_extensions() :: [atom()]

The extensions of FEEL that the expressions of the model are prepared with (Feel.Extension: names or modules). Those of all the extensions that are on are given together.

name()

@callback name() :: atom()

The name of the extension, for the messages about it.

type_aliases()

(optional)
@callback type_aliases() :: %{required(String.t()) => String.t()}

Type names that stand for a type of FEEL: the name that is written in a typeRef, to the name of the type it stands for (Feel.Type.builtin/1). A model that defines an item definition of a name is not asked: its definition is the type.

Functions

external_functions(modules)

@spec external_functions([module()]) :: %{optional(atom()) => String.t()}

The external function kinds that the extensions that are on run: the kind of the functionDefinition to the kind in FEEL. A later extension wins for a kind that two give.

feel_extensions(modules)

@spec feel_extensions([module()]) :: [atom()]

The extensions of FEEL that the extensions that are on bring, each once, in the order given.

registry()

@spec registry() :: %{optional(atom()) => module()}

The extensions that extensions: knows by name, each with the module it stands for.

iex> Dmn.Extension.registry()
%{dmn_1_1_types: Dmn.Extension.Dmn11Types}

resolve(extensions, registry \\ registry())

@spec resolve(term(), %{optional(atom()) => module()}) ::
  {:ok, [module()]} | {:error, String.t()}

The modules for a list of names and modules, each once, in the order given. The names are those of registry/0, or of the map given as the second argument.

iex> Dmn.Extension.resolve([:dmn_1_1_types, Dmn.Extension.Dmn11Types])
{:ok, [Dmn.Extension.Dmn11Types]}
iex> {:error, message} = Dmn.Extension.resolve([:nope])
iex> message =~ ":nope"
true

type_aliases(modules)

@spec type_aliases([module()]) :: %{optional(String.t()) => String.t()}

The type aliases of the extensions that are on, from the name to the type it stands for. resolve/1 has seen that no two give one name two types.