Packages

A model management framework supporting multi-layered abstraction structures, pluggable data layers, and arbitrary command-line transform tools.

Current section

Files

Jump to
paradigm lib universe.ex
Raw

lib/universe.ex

defmodule Paradigm.Universe do
@moduledoc """
Helpers for working with content-addressed Universe graphs.
"""
alias Paradigm.Graph
alias Paradigm.Graph.Node
alias Paradigm.Graph.Node.Ref
def insert_graph_with_paradigm_by_name(universe, graph, name, paradigm_name) do
paradigm_id = find_by_name(universe, paradigm_name)
insert_graph_with_paradigm(universe, graph, name, paradigm_id)
end
def insert_graph_with_paradigm(universe, graph, name, paradigm_id) do
id = Paradigm.Graph.get_content_hash(graph)
registered_graph_node = %Node{
id: id,
class: "registered_graph",
data: %{
graph: graph,
name: name,
paradigm: %Ref{id: paradigm_id},
conformance_result: nil
}
}
universe
|> Paradigm.Graph.insert_node(registered_graph_node)
end
def register_transform(universe, transform, from, to) do
id = "transform_#{:erlang.unique_integer([:positive])}"
transform_node = %Node{
id: id,
class: "transform",
data: %{
name: id,
transform: transform,
source: %Ref{id: from},
target: %Ref{id: to}
}
}
Paradigm.Graph.insert_node(universe, transform_node)
end
def register_transform_by_name(universe, module, from_name, to_name) do
register_transform(
universe,
module,
find_by_name(universe, from_name),
find_by_name(universe, to_name)
)
end
def bootstrap(name \\ "universe_model", description \\ "Test universe") do
Paradigm.Graph.MapGraph.new(name: name, description: description)
|> add_metamodel()
end
def add_metamodel(universe) do
metamodel = Paradigm.Builtin.Metamodel.definition()
metamodel_graph = metamodel |> Paradigm.Abstraction.embed()
metamodel_id = Paradigm.Graph.get_content_hash(metamodel_graph)
universe
|> Paradigm.Universe.insert_graph_with_paradigm(metamodel_graph, "Metamodel", metamodel_id)
|> apply_propagate()
end
def get_instantiation_node_for(universe, node_id) do
Graph.get_node(universe, node_id)
end
def get_paradigm_for(universe, node_id) do
registered_graph = Graph.get_node(universe, node_id)
if registered_graph && registered_graph.data["paradigm"] do
paradigm_id = registered_graph.data["paradigm"].id
paradigm_graph = Graph.get_node(universe, paradigm_id).data["graph"]
Paradigm.Abstraction.extract(paradigm_graph)
end
end
def insert_paradigm(universe, paradigm) do
paradigm_graph = Paradigm.Abstraction.embed(paradigm)
metamodel_id = find_by_name(universe, "Metamodel")
universe
|> Paradigm.Universe.insert_graph_with_paradigm(paradigm_graph, paradigm.name, metamodel_id)
end
def find_by_name(universe, name) do
Paradigm.nodes_of_type(universe, "registered_graph")
|> Enum.find(fn node ->
node.data["name"] == name
end)
|> case do
nil -> nil
node -> node.id
end
end
def apply_propagate(universe) do
{:ok, transformed_universe} = Paradigm.Transform.transform(propagate(), universe, universe)
transformed_universe
end
def all_instantiations_conformant?(universe) do
universe
|> Paradigm.nodes_of_type("registered_graph")
|> Enum.all?(fn node ->
case node.data["conformance_result"] do
nil -> false
result -> result.issues == []
end
end)
end
def get_transform_pairs(universe) do
# Returns tuples of source_id, target_id for all transforms that have occured.
Paradigm.Graph.get_all_nodes_of_class(universe, "transform_instance")
|> Enum.map(&Paradigm.Graph.get_node(universe, &1))
|> Enum.map(fn node -> {node.data["source"].id, node.data["target"].id} end)
end
def propagate() do
Paradigm.Transform.ClassBasedTransform.new()
|> Paradigm.Transform.ClassBasedTransform.with_default(fn node -> node end)
|> Paradigm.Transform.ClassBasedTransform.for_class(
"registered_graph",
&update_conformance_check_if_needed/2
)
|> Paradigm.Transform.ClassBasedTransform.for_class("transform", &apply_missing_transforms/2)
end
defp update_conformance_check_if_needed(registered_graph_node, %{graph: universe}) do
case registered_graph_node.data["conformance_result"] do
nil ->
conformance_result = do_conformance_check(universe, registered_graph_node)
updated_data =
Map.put(registered_graph_node.data, "conformance_result", conformance_result)
%Node{registered_graph_node | data: updated_data}
_ ->
registered_graph_node
end
end
defp do_conformance_check(universe, registered_graph_node) do
if registered_graph_node.data["paradigm"] do
paradigm_graph_node =
Paradigm.Graph.get_node(universe, registered_graph_node.data["paradigm"].id)
paradigm = paradigm_graph_node.data["graph"]
instance_graph = registered_graph_node.data["graph"]
Paradigm.Conformance.check_graph(instance_graph, paradigm)
else
%Paradigm.Conformance.Result{issues: []}
end
end
defp apply_missing_transforms(transform_node, %{graph: universe}) do
[transform_node] ++
(universe
|> find_valid_instantiations_of(transform_node.data["source"].id)
|> Enum.flat_map(fn instance_id ->
case find_existing_transform_instance(universe, transform_node.id, instance_id) do
nil ->
create_transform_instance(universe, transform_node.id, instance_id)
_existing ->
[]
end
end))
end
defp find_valid_instantiations_of(universe, paradigm_id) do
registered_graph_node_ids =
Paradigm.Graph.get_all_nodes_of_class(universe, "registered_graph")
registered_graph_nodes =
Enum.map(registered_graph_node_ids, &Paradigm.Graph.get_node(universe, &1))
Enum.filter(registered_graph_nodes, fn registered_graph_node ->
(registered_graph_node.data["paradigm"] &&
paradigm_id == registered_graph_node.data["paradigm"].id) and
registered_graph_node.data["conformance_result"] == %Paradigm.Conformance.Result{
issues: []
}
end)
|> Enum.map(fn registered_graph_node -> registered_graph_node.id end)
end
defp find_existing_transform_instance(universe, transform_node_id, source_instance_id) do
transform_instances = Paradigm.Graph.get_all_nodes_of_class(universe, "transform_instance")
Enum.find(transform_instances, fn transform_instance_id ->
transform_instance_node = Paradigm.Graph.get_node(universe, transform_instance_id)
%Ref{id: transform_id} = transform_instance_node.data["transform"]
%Ref{id: source_id} = transform_instance_node.data["source"]
transform_id == transform_node_id && source_id == source_instance_id
end)
end
defp create_transform_instance(universe, transform_node_id, source_graph_id) do
transform_node = Paradigm.Graph.get_node(universe, transform_node_id)
registered_graph = Paradigm.Graph.get_node(universe, source_graph_id)
source_graph = registered_graph.data["graph"]
%{"transform" => transform, "name" => transform_name, "target" => transform_target} =
transform_node.data
target_graph = Paradigm.Graph.MapGraph.new()
{:ok, result_graph} = Paradigm.Transform.transform(transform, source_graph, target_graph)
target_id = Paradigm.Graph.get_content_hash(result_graph)
[
transform_node,
%Node{
id: "#{transform_name}_from_#{source_graph_id}_to_#{target_id}",
class: "transform_instance",
data: %{
"transform" => %Ref{id: transform_node_id},
"source" => %Ref{id: source_graph_id},
"target" => %Ref{id: target_id},
"errors" => [],
"warnings" => []
}
},
%Node{
id: target_id,
class: "registered_graph",
data: %{
graph: result_graph,
name: registered_graph.data["name"],
paradigm: %Ref{id: transform_target.id},
conformance_result: nil
}
}
]
end
end