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
|
||||
alias RDF.{Description, Graph, Dataset, PrefixMap}
|
||||
alias RDF.{Description, Graph, Dataset, PrefixMap, IRI}
|
||||
|
||||
defmodule Error do
|
||||
defexception [:message]
|
||||
|
@ -16,8 +16,11 @@ defmodule RDF.Graph.Builder do
|
|||
|
||||
def build({:__block__, _, block}, opts) do
|
||||
{declarations, data} = Enum.split_with(block, &declaration?/1)
|
||||
{prefixes, declarations} = extract_prefixes(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
|
||||
alias RDF.XSD
|
||||
|
@ -28,7 +31,12 @@ defmodule RDF.Graph.Builder do
|
|||
|
||||
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
|
||||
|
||||
|
@ -60,6 +68,31 @@ defmodule RDF.Graph.Builder do
|
|||
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
|
||||
{base, declarations} =
|
||||
Enum.reduce(declarations, {nil, []}, fn
|
||||
|
|
|
@ -12,7 +12,7 @@ defmodule RDF.Graph.BuilderTest do
|
|||
defmodule TestNS do
|
||||
use RDF.Vocabulary.Namespace
|
||||
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]
|
||||
end
|
||||
|
||||
|
@ -410,6 +410,8 @@ defmodule RDF.Graph.BuilderTest do
|
|||
|
||||
describe "@base" do
|
||||
test "with vocabulary namespace" do
|
||||
import RDF.Sigils
|
||||
|
||||
# we're wrapping this in a function to isolate the alias
|
||||
graph =
|
||||
(fn ->
|
||||
|
@ -418,12 +420,17 @@ defmodule RDF.Graph.BuilderTest do
|
|||
# @base 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).()
|
||||
|
||||
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
|
||||
)
|
||||
end
|
||||
|
@ -433,10 +440,19 @@ defmodule RDF.Graph.BuilderTest do
|
|||
RDF.Graph.build do
|
||||
@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
|
||||
|
||||
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
|
||||
|
||||
test "with URI as string" do
|
||||
|
@ -444,22 +460,19 @@ defmodule RDF.Graph.BuilderTest do
|
|||
RDF.Graph.build do
|
||||
@base "http://example.com/base"
|
||||
|
||||
EX.S |> EX.p(EX.O)
|
||||
~I<#S> |> EX.p(~I<#O>)
|
||||
{~I<#foo>, ~I<#bar>, ~I<#baz>}
|
||||
end
|
||||
|
||||
assert graph == RDF.graph(EX.S |> EX.p(EX.O), base_iri: "http://example.com/base")
|
||||
end
|
||||
|
||||
test "with URI from variable" do
|
||||
graph =
|
||||
RDF.Graph.build do
|
||||
foo = "http://example.com/base"
|
||||
@base foo
|
||||
|
||||
EX.S |> EX.p(EX.O)
|
||||
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
|
||||
|
||||
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
|
||||
@base "http://example.com/base"
|
||||
|
||||
EX.S |> EX.p(EX.O)
|
||||
~I<#S> |> EX.p(~I<#O>)
|
||||
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
|
||||
|
||||
|
|
Loading…
Reference in a new issue