Allow use of aliases in @prefix declarations in graph builder blocks

This commit is contained in:
Marcel Otto 2022-06-07 00:18:11 +02:00
parent 1a51aea606
commit e9fd42430b
4 changed files with 61 additions and 31 deletions

View file

@ -37,6 +37,11 @@ The generated namespaces are much more flexible now and compile faster.
had to be given as the first argument). had to be given as the first argument).
- several performance improvements - several performance improvements
### Fixed
- The RDF vocabulary namespaces used in `@prefix` and `@base` declarations in a
`RDF.Graph.build` block no longer have to be written out which had to be done
previously even when parts of the module were available as an alias.
[Compare v0.12.0...HEAD](https://github.com/rdf-elixir/rdf-ex/compare/v0.12.0...HEAD) [Compare v0.12.0...HEAD](https://github.com/rdf-elixir/rdf-ex/compare/v0.12.0...HEAD)

View file

@ -145,7 +145,7 @@ defmodule RDF.Graph do
For a description of the DSL see [this guide](https://rdf-elixir.dev/rdf-ex/description-and-graph-dsl.html). For a description of the DSL see [this guide](https://rdf-elixir.dev/rdf-ex/description-and-graph-dsl.html).
""" """
defmacro build(opts \\ [], do: block) do defmacro build(opts \\ [], do: block) do
Builder.build(block, opts) Builder.build(block, __CALLER__, opts)
end end
@doc """ @doc """

View file

@ -16,13 +16,14 @@ defmodule RDF.Graph.Builder do
def exclude(_), do: nil def exclude(_), do: nil
end end
def build({:__block__, _, block}, opts) do def build({:__block__, _, block}, env, opts) do
env_aliases = env_aliases(env)
{declarations, data} = Enum.split_with(block, &declaration?/1) {declarations, data} = Enum.split_with(block, &declaration?/1)
{base, declarations} = extract_base(declarations) {base, declarations} = extract_base(declarations, env_aliases)
base_string = base_string(base) base_string = base_string(base)
data = resolve_relative_iris(data, base_string) data = resolve_relative_iris(data, base_string)
declarations = resolve_relative_iris(declarations, base_string) declarations = resolve_relative_iris(declarations, base_string)
{prefixes, declarations} = extract_prefixes(declarations) {prefixes, declarations} = extract_prefixes(declarations, env_aliases)
quote do quote do
alias RDF.XSD alias RDF.XSD
@ -42,8 +43,8 @@ defmodule RDF.Graph.Builder do
end end
end end
def build(single, opts) do def build(single, env, opts) do
build({:__block__, [], List.wrap(single)}, opts) build({:__block__, [], List.wrap(single)}, env, opts)
end end
@doc false @doc false
@ -95,11 +96,14 @@ defmodule RDF.Graph.Builder do
end) end)
end end
defp extract_base(declarations) do defp extract_base(declarations, env_aliases) do
{base, declarations} = {base, declarations} =
Enum.reduce(declarations, {nil, []}, fn Enum.reduce(declarations, {nil, []}, fn
{:@, line, [{:base, _, [{:__aliases__, _, ns}] = aliases}]}, {_, declarations} -> {:@, line, [{:base, _, [{:__aliases__, _, ns}] = aliases}]}, {_, declarations} ->
{Module.concat(ns), [{:alias, line, aliases} | declarations]} {
ns |> expand_module(env_aliases) |> Module.concat(),
[{:alias, line, aliases} | declarations]
}
{:@, _, [{:base, _, [base]}]}, {_, declarations} -> {:@, _, [{:base, _, [base]}]}, {_, declarations} ->
{base, declarations} {base, declarations}
@ -111,15 +115,16 @@ defmodule RDF.Graph.Builder do
{base, Enum.reverse(declarations)} {base, Enum.reverse(declarations)}
end end
defp extract_prefixes(declarations) do defp extract_prefixes(declarations, env_aliases) do
{prefixes, declarations} = {prefixes, declarations} =
Enum.reduce(declarations, {[], []}, fn Enum.reduce(declarations, {[], []}, fn
{:@, line, [{:prefix, _, [[{prefix, {:__aliases__, _, ns} = aliases}]]}]}, {:@, line, [{:prefix, _, [[{prefix, {:__aliases__, _, ns} = aliases}]]}]},
{prefixes, declarations} -> {prefixes, declarations} ->
{[prefix(prefix, ns) | prefixes], [{:alias, line, [aliases]} | declarations]} {[prefix(prefix, ns, env_aliases) | prefixes],
[{:alias, line, [aliases]} | declarations]}
{:@, line, [{:prefix, _, [{:__aliases__, _, ns}] = aliases}]}, {prefixes, declarations} -> {:@, line, [{:prefix, _, [{:__aliases__, _, ns}] = aliases}]}, {prefixes, declarations} ->
{[prefix(ns) | prefixes], [{:alias, line, aliases} | declarations]} {[prefix(ns, env_aliases) | prefixes], [{:alias, line, aliases} | declarations]}
declaration, {prefixes, declarations} -> declaration, {prefixes, declarations} ->
{prefixes, [declaration | declarations]} {prefixes, [declaration | declarations]}
@ -128,18 +133,25 @@ defmodule RDF.Graph.Builder do
{prefixes, Enum.reverse(declarations)} {prefixes, Enum.reverse(declarations)}
end end
defp prefix(namespace) do defp prefix(namespace, env_aliases) do
namespace
|> determine_prefix()
|> prefix(namespace, env_aliases)
end
defp prefix(prefix, namespace, env_aliases) do
{prefix, namespace |> expand_module(env_aliases) |> Module.concat()}
end
defp determine_prefix(namespace) do
namespace namespace
|> Enum.reverse() |> Enum.reverse()
|> hd() |> hd()
|> to_string() |> to_string()
|> Macro.underscore() |> Macro.underscore()
|> String.to_atom() |> String.to_atom()
|> prefix(namespace)
end end
defp prefix(prefix, namespace), do: {prefix, Module.concat(namespace)}
defp declaration?({:=, _, _}), do: true defp declaration?({:=, _, _}), do: true
defp declaration?({:@, _, [{:prefix, _, _}]}), do: true defp declaration?({:@, _, [{:prefix, _, _}]}), do: true
defp declaration?({:@, _, [{:base, _, _}]}), do: true defp declaration?({:@, _, [{:base, _, _}]}), do: true
@ -161,4 +173,26 @@ defmodule RDF.Graph.Builder do
defp rdf?(invalid) do defp rdf?(invalid) do
raise Error, message: "invalid RDF data: #{inspect(invalid)}" raise Error, message: "invalid RDF data: #{inspect(invalid)}"
end end
defp expand_module([first | rest] = module, env_aliases) do
if full = env_aliases[first] do
full ++ rest
else
module
end
end
defp env_aliases(env) do
Map.new(env.aliases, fn {short, full} ->
{
module_to_atom_without_elixir_prefix(short),
full |> Module.split() |> Enum.map(&String.to_atom/1)
}
end)
end
defp module_to_atom_without_elixir_prefix(module) do
[short] = Module.split(module)
String.to_atom(short)
end
end end

View file

@ -19,7 +19,7 @@ defmodule RDF.Graph.BuilderTest do
@compile {:no_warn_undefined, __MODULE__.TestNS.EX} @compile {:no_warn_undefined, __MODULE__.TestNS.EX}
@compile {:no_warn_undefined, __MODULE__.TestNS.Custom} @compile {:no_warn_undefined, __MODULE__.TestNS.Custom}
alias __MODULE__.TestNS.EX alias TestNS.EX
alias RDF.NS alias RDF.NS
defmodule UseTest do defmodule UseTest do
@ -346,7 +346,6 @@ defmodule RDF.Graph.BuilderTest do
(fn -> (fn ->
RDF.Graph.build do RDF.Graph.build do
alias TestNS.Custom alias TestNS.Custom
# alias RDF.Graph.BuilderTest.TestNS.Custom
Custom.S |> Custom.p(Custom.O) Custom.S |> Custom.p(Custom.O)
end end
end).() end).()
@ -359,8 +358,8 @@ defmodule RDF.Graph.BuilderTest do
graph = graph =
(fn -> (fn ->
RDF.Graph.build do RDF.Graph.build do
import RDF.Graph.BuilderTest.TestNS.ImportTest import TestNS.ImportTest
EX.S |> foo(RDF.Graph.BuilderTest.TestNS.ImportTest.Bar) EX.S |> foo(TestNS.ImportTest.Bar)
end end
end).() end).()
@ -399,9 +398,7 @@ defmodule RDF.Graph.BuilderTest do
graph = graph =
(fn -> (fn ->
RDF.Graph.build do RDF.Graph.build do
# TODO: the following leads to a (RDF.Namespace.UndefinedTermError) Elixir.TestNS is not a RDF.Namespace @prefix cust: TestNS.Custom
# @prefix cust: TestNS.Custom
@prefix cust: RDF.Graph.BuilderTest.TestNS.Custom
Custom.S |> Custom.p(Custom.O) Custom.S |> Custom.p(Custom.O)
end end
@ -418,9 +415,7 @@ defmodule RDF.Graph.BuilderTest do
graph = graph =
(fn -> (fn ->
RDF.Graph.build do RDF.Graph.build do
# TODO: the following leads to a (RDF.Namespace.UndefinedTermError) Elixir.TestNS is not a RDF.Namespace @prefix TestNS.Custom
# @prefix TestNS.Custom
@prefix RDF.Graph.BuilderTest.TestNS.Custom
Custom.S |> Custom.p(Custom.O) Custom.S |> Custom.p(Custom.O)
end end
@ -437,9 +432,7 @@ defmodule RDF.Graph.BuilderTest do
graph = graph =
(fn -> (fn ->
RDF.Graph.build prefixes: [custom: EX] do RDF.Graph.build prefixes: [custom: EX] do
# TODO: the following leads to a (RDF.Namespace.UndefinedTermError) Elixir.TestNS is not a RDF.Namespace @prefix custom: TestNS.Custom
# @prefix custom: TestNS.Custom
@prefix custom: RDF.Graph.BuilderTest.TestNS.Custom
Custom.S |> Custom.p(Custom.O) Custom.S |> Custom.p(Custom.O)
end end
@ -460,9 +453,7 @@ defmodule RDF.Graph.BuilderTest do
graph = graph =
(fn -> (fn ->
RDF.Graph.build do RDF.Graph.build do
# TODO: the following leads to a (RDF.Namespace.UndefinedTermError) Elixir.TestNS is not a RDF.Namespace @base TestNS.Custom
# @base TestNS.Custom
@base RDF.Graph.BuilderTest.TestNS.Custom
~I<S> |> Custom.p(~I<O>) ~I<S> |> Custom.p(~I<O>)
{~I<foo>, ~I<bar>, ~I<baz>} {~I<foo>, ~I<bar>, ~I<baz>}