Add auto-resolution of relative IRIs in Graph build blocks
This commit is contained in:
parent
3ff1186336
commit
178e8315ab
2 changed files with 73 additions and 24 deletions
|
@ -1,5 +1,5 @@
|
||||||
defmodule RDF.Graph.Builder do
|
defmodule RDF.Graph.Builder do
|
||||||
alias RDF.{Description, Graph, Dataset, PrefixMap}
|
alias RDF.{Description, Graph, Dataset, PrefixMap, IRI}
|
||||||
|
|
||||||
defmodule Error do
|
defmodule Error do
|
||||||
defexception [:message]
|
defexception [:message]
|
||||||
|
@ -16,8 +16,11 @@ defmodule RDF.Graph.Builder do
|
||||||
|
|
||||||
def build({:__block__, _, block}, opts) do
|
def build({:__block__, _, block}, opts) do
|
||||||
{declarations, data} = Enum.split_with(block, &declaration?/1)
|
{declarations, data} = Enum.split_with(block, &declaration?/1)
|
||||||
{prefixes, declarations} = extract_prefixes(declarations)
|
|
||||||
{base, declarations} = extract_base(declarations)
|
{base, declarations} = extract_base(declarations)
|
||||||
|
base_string = base_string(base)
|
||||||
|
data = resolve_relative_iris(data, base_string)
|
||||||
|
declarations = resolve_relative_iris(declarations, base_string)
|
||||||
|
{prefixes, declarations} = extract_prefixes(declarations)
|
||||||
|
|
||||||
quote do
|
quote do
|
||||||
alias RDF.XSD
|
alias RDF.XSD
|
||||||
|
@ -28,7 +31,12 @@ defmodule RDF.Graph.Builder do
|
||||||
|
|
||||||
unquote(declarations)
|
unquote(declarations)
|
||||||
|
|
||||||
RDF.Graph.Builder.do_build(unquote(data), unquote(opts), unquote(prefixes), unquote(base))
|
RDF.Graph.Builder.do_build(
|
||||||
|
unquote(data),
|
||||||
|
unquote(opts),
|
||||||
|
unquote(prefixes),
|
||||||
|
unquote(base_string)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -60,6 +68,31 @@ defmodule RDF.Graph.Builder do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp base_string(nil), do: nil
|
||||||
|
defp base_string(base) when is_binary(base), do: base
|
||||||
|
defp base_string(base) when is_atom(base), do: apply(base, :__base_iri__, [])
|
||||||
|
defp base_string({:sigil_I, _, [{_, _, [base]}, _]}), do: base
|
||||||
|
|
||||||
|
defp base_string(_) do
|
||||||
|
raise Error,
|
||||||
|
message: "invalid @base expression; only literal values are allowed as @base value"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp resolve_relative_iris(ast, base) do
|
||||||
|
Macro.prewalk(ast, fn
|
||||||
|
{:sigil_I, meta_outer, [{:<<>>, meta_inner, [iri]}, list]} = sigil ->
|
||||||
|
if IRI.absolute?(iri) do
|
||||||
|
sigil
|
||||||
|
else
|
||||||
|
absolute = iri |> IRI.absolute(base) |> IRI.to_string()
|
||||||
|
{:sigil_I, meta_outer, [{:<<>>, meta_inner, [absolute]}, list]}
|
||||||
|
end
|
||||||
|
|
||||||
|
other ->
|
||||||
|
other
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
defp extract_base(declarations) do
|
defp extract_base(declarations) do
|
||||||
{base, declarations} =
|
{base, declarations} =
|
||||||
Enum.reduce(declarations, {nil, []}, fn
|
Enum.reduce(declarations, {nil, []}, fn
|
||||||
|
|
|
@ -12,7 +12,7 @@ defmodule RDF.Graph.BuilderTest do
|
||||||
defmodule TestNS do
|
defmodule TestNS do
|
||||||
use RDF.Vocabulary.Namespace
|
use RDF.Vocabulary.Namespace
|
||||||
defvocab EX, base_iri: "http://example.com/", terms: [], strict: false
|
defvocab EX, base_iri: "http://example.com/", terms: [], strict: false
|
||||||
defvocab Custom, base_iri: "http://custom.com/foo#", terms: [], strict: false
|
defvocab Custom, base_iri: "http://custom.com/foo/", terms: [], strict: false
|
||||||
defvocab ImportTest, base_iri: "http://import.com/bar#", terms: [:foo, :Bar]
|
defvocab ImportTest, base_iri: "http://import.com/bar#", terms: [:foo, :Bar]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -410,6 +410,8 @@ defmodule RDF.Graph.BuilderTest do
|
||||||
|
|
||||||
describe "@base" do
|
describe "@base" do
|
||||||
test "with vocabulary namespace" do
|
test "with vocabulary namespace" do
|
||||||
|
import RDF.Sigils
|
||||||
|
|
||||||
# we're wrapping this in a function to isolate the alias
|
# we're wrapping this in a function to isolate the alias
|
||||||
graph =
|
graph =
|
||||||
(fn ->
|
(fn ->
|
||||||
|
@ -418,12 +420,17 @@ defmodule RDF.Graph.BuilderTest do
|
||||||
# @base TestNS.Custom
|
# @base TestNS.Custom
|
||||||
@base RDF.Graph.BuilderTest.TestNS.Custom
|
@base RDF.Graph.BuilderTest.TestNS.Custom
|
||||||
|
|
||||||
Custom.S |> Custom.p(Custom.O)
|
~I<S> |> Custom.p(~I<O>)
|
||||||
|
{~I<foo>, ~I<bar>, ~I<baz>}
|
||||||
end
|
end
|
||||||
end).()
|
end).()
|
||||||
|
|
||||||
assert graph ==
|
assert graph ==
|
||||||
RDF.graph(TestNS.Custom.S |> TestNS.Custom.p(TestNS.Custom.O),
|
RDF.graph(
|
||||||
|
[
|
||||||
|
TestNS.Custom.S |> TestNS.Custom.p(TestNS.Custom.O),
|
||||||
|
TestNS.Custom.foo() |> TestNS.Custom.bar(TestNS.Custom.baz())
|
||||||
|
],
|
||||||
base_iri: TestNS.Custom
|
base_iri: TestNS.Custom
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -433,10 +440,19 @@ defmodule RDF.Graph.BuilderTest do
|
||||||
RDF.Graph.build do
|
RDF.Graph.build do
|
||||||
@base ~I<http://example.com/base>
|
@base ~I<http://example.com/base>
|
||||||
|
|
||||||
EX.S |> EX.p(EX.O)
|
~I<#S> |> EX.p(~I<#O>)
|
||||||
|
{~I<#foo>, ~I<#bar>, ~I<#baz>}
|
||||||
end
|
end
|
||||||
|
|
||||||
assert graph == RDF.graph(EX.S |> EX.p(EX.O), base_iri: "http://example.com/base")
|
assert graph ==
|
||||||
|
RDF.graph(
|
||||||
|
[
|
||||||
|
~I<http://example.com/base#S> |> EX.p(~I<http://example.com/base#O>),
|
||||||
|
{~I<http://example.com/base#foo>, ~I<http://example.com/base#bar>,
|
||||||
|
~I<http://example.com/base#baz>}
|
||||||
|
],
|
||||||
|
base_iri: "http://example.com/base"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with URI as string" do
|
test "with URI as string" do
|
||||||
|
@ -444,22 +460,19 @@ defmodule RDF.Graph.BuilderTest do
|
||||||
RDF.Graph.build do
|
RDF.Graph.build do
|
||||||
@base "http://example.com/base"
|
@base "http://example.com/base"
|
||||||
|
|
||||||
EX.S |> EX.p(EX.O)
|
~I<#S> |> EX.p(~I<#O>)
|
||||||
|
{~I<#foo>, ~I<#bar>, ~I<#baz>}
|
||||||
end
|
end
|
||||||
|
|
||||||
assert graph == RDF.graph(EX.S |> EX.p(EX.O), base_iri: "http://example.com/base")
|
assert graph ==
|
||||||
end
|
RDF.graph(
|
||||||
|
[
|
||||||
test "with URI from variable" do
|
~I<http://example.com/base#S> |> EX.p(~I<http://example.com/base#O>),
|
||||||
graph =
|
{~I<http://example.com/base#foo>, ~I<http://example.com/base#bar>,
|
||||||
RDF.Graph.build do
|
~I<http://example.com/base#baz>}
|
||||||
foo = "http://example.com/base"
|
],
|
||||||
@base foo
|
base_iri: "http://example.com/base"
|
||||||
|
)
|
||||||
EX.S |> EX.p(EX.O)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert graph == RDF.graph(EX.S |> EX.p(EX.O), base_iri: "http://example.com/base")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "conflict with base_iri opt" do
|
test "conflict with base_iri opt" do
|
||||||
|
@ -467,10 +480,13 @@ defmodule RDF.Graph.BuilderTest do
|
||||||
RDF.Graph.build base_iri: "http://example.com/old" do
|
RDF.Graph.build base_iri: "http://example.com/old" do
|
||||||
@base "http://example.com/base"
|
@base "http://example.com/base"
|
||||||
|
|
||||||
EX.S |> EX.p(EX.O)
|
~I<#S> |> EX.p(~I<#O>)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert graph == RDF.graph(EX.S |> EX.p(EX.O), base_iri: "http://example.com/base")
|
assert graph ==
|
||||||
|
RDF.graph(~I<http://example.com/base#S> |> EX.p(~I<http://example.com/base#O>),
|
||||||
|
base_iri: "http://example.com/base"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue