diff --git a/CHANGELOG.md b/CHANGELOG.md index bca6aad..db7b61a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,11 @@ The generated namespaces are much more flexible now and compile faster. had to be given as the first argument). - 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) diff --git a/lib/rdf/graph.ex b/lib/rdf/graph.ex index 6922382..ec317b7 100644 --- a/lib/rdf/graph.ex +++ b/lib/rdf/graph.ex @@ -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). """ defmacro build(opts \\ [], do: block) do - Builder.build(block, opts) + Builder.build(block, __CALLER__, opts) end @doc """ diff --git a/lib/rdf/graph_builder.ex b/lib/rdf/graph_builder.ex index 1c78b1b..ebcb9b9 100644 --- a/lib/rdf/graph_builder.ex +++ b/lib/rdf/graph_builder.ex @@ -16,13 +16,14 @@ defmodule RDF.Graph.Builder do def exclude(_), do: nil 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) - {base, declarations} = extract_base(declarations) + {base, declarations} = extract_base(declarations, env_aliases) base_string = base_string(base) data = resolve_relative_iris(data, base_string) declarations = resolve_relative_iris(declarations, base_string) - {prefixes, declarations} = extract_prefixes(declarations) + {prefixes, declarations} = extract_prefixes(declarations, env_aliases) quote do alias RDF.XSD @@ -42,8 +43,8 @@ defmodule RDF.Graph.Builder do end end - def build(single, opts) do - build({:__block__, [], List.wrap(single)}, opts) + def build(single, env, opts) do + build({:__block__, [], List.wrap(single)}, env, opts) end @doc false @@ -95,11 +96,14 @@ defmodule RDF.Graph.Builder do end) end - defp extract_base(declarations) do + defp extract_base(declarations, env_aliases) do {base, declarations} = Enum.reduce(declarations, {nil, []}, fn {:@, 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, declarations} @@ -111,15 +115,16 @@ defmodule RDF.Graph.Builder do {base, Enum.reverse(declarations)} end - defp extract_prefixes(declarations) do + defp extract_prefixes(declarations, env_aliases) do {prefixes, declarations} = Enum.reduce(declarations, {[], []}, fn {:@, line, [{:prefix, _, [[{prefix, {:__aliases__, _, ns} = aliases}]]}]}, {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} -> - {[prefix(ns) | prefixes], [{:alias, line, aliases} | declarations]} + {[prefix(ns, env_aliases) | prefixes], [{:alias, line, aliases} | declarations]} declaration, {prefixes, declarations} -> {prefixes, [declaration | declarations]} @@ -128,18 +133,25 @@ defmodule RDF.Graph.Builder do {prefixes, Enum.reverse(declarations)} 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 |> Enum.reverse() |> hd() |> to_string() |> Macro.underscore() |> String.to_atom() - |> prefix(namespace) end - defp prefix(prefix, namespace), do: {prefix, Module.concat(namespace)} - defp declaration?({:=, _, _}), do: true defp declaration?({:@, _, [{:prefix, _, _}]}), do: true defp declaration?({:@, _, [{:base, _, _}]}), do: true @@ -161,4 +173,26 @@ defmodule RDF.Graph.Builder do defp rdf?(invalid) do raise Error, message: "invalid RDF data: #{inspect(invalid)}" 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 diff --git a/test/unit/graph_builder_test.exs b/test/unit/graph_builder_test.exs index 041a2ed..15f6622 100644 --- a/test/unit/graph_builder_test.exs +++ b/test/unit/graph_builder_test.exs @@ -19,7 +19,7 @@ defmodule RDF.Graph.BuilderTest do @compile {:no_warn_undefined, __MODULE__.TestNS.EX} @compile {:no_warn_undefined, __MODULE__.TestNS.Custom} - alias __MODULE__.TestNS.EX + alias TestNS.EX alias RDF.NS defmodule UseTest do @@ -346,7 +346,6 @@ defmodule RDF.Graph.BuilderTest do (fn -> RDF.Graph.build do alias TestNS.Custom - # alias RDF.Graph.BuilderTest.TestNS.Custom Custom.S |> Custom.p(Custom.O) end end).() @@ -359,8 +358,8 @@ defmodule RDF.Graph.BuilderTest do graph = (fn -> RDF.Graph.build do - import RDF.Graph.BuilderTest.TestNS.ImportTest - EX.S |> foo(RDF.Graph.BuilderTest.TestNS.ImportTest.Bar) + import TestNS.ImportTest + EX.S |> foo(TestNS.ImportTest.Bar) end end).() @@ -399,9 +398,7 @@ defmodule RDF.Graph.BuilderTest do graph = (fn -> 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: RDF.Graph.BuilderTest.TestNS.Custom + @prefix cust: TestNS.Custom Custom.S |> Custom.p(Custom.O) end @@ -418,9 +415,7 @@ defmodule RDF.Graph.BuilderTest do graph = (fn -> RDF.Graph.build do - # TODO: the following leads to a (RDF.Namespace.UndefinedTermError) Elixir.TestNS is not a RDF.Namespace - # @prefix TestNS.Custom - @prefix RDF.Graph.BuilderTest.TestNS.Custom + @prefix TestNS.Custom Custom.S |> Custom.p(Custom.O) end @@ -437,9 +432,7 @@ defmodule RDF.Graph.BuilderTest do graph = (fn -> 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: RDF.Graph.BuilderTest.TestNS.Custom + @prefix custom: TestNS.Custom Custom.S |> Custom.p(Custom.O) end @@ -460,9 +453,7 @@ defmodule RDF.Graph.BuilderTest do graph = (fn -> RDF.Graph.build do - # TODO: the following leads to a (RDF.Namespace.UndefinedTermError) Elixir.TestNS is not a RDF.Namespace - # @base TestNS.Custom - @base RDF.Graph.BuilderTest.TestNS.Custom + @base TestNS.Custom ~I |> Custom.p(~I) {~I, ~I, ~I}