Allow to pass bindings to RDF.Graph.build blocks
This commit is contained in:
parent
65bb0831b8
commit
3f0c6635c1
4 changed files with 125 additions and 12 deletions
|
@ -37,7 +37,8 @@ The generated namespaces are much more flexible now and compile faster.
|
||||||
import no longer affect the caller context. `alias`es in the caller context are
|
import no longer affect the caller context. `alias`es in the caller context are
|
||||||
still available in the build block, but `import`s not and must be reimported in
|
still available in the build block, but `import`s not and must be reimported in
|
||||||
the build block. Variables in the caller context are also no longer available
|
the build block. Variables in the caller context are also no longer available
|
||||||
build block.
|
build block, but must be passed explicitly with the new optional `bindings`
|
||||||
|
argument of `RDF.Graph.build/3`.
|
||||||
- `RDF.Data.merge/2` and `RDF.Data.equal?/2` are now commutative, i.e. structs
|
- `RDF.Data.merge/2` and `RDF.Data.equal?/2` are now commutative, i.e. structs
|
||||||
which implement the `RDF.Data` protocol can be given also as the second argument
|
which implement the `RDF.Data` protocol can be given also as the second argument
|
||||||
(previously custom structs with `RDF.Data` protocol implementations always
|
(previously custom structs with `RDF.Data` protocol implementations always
|
||||||
|
|
|
@ -144,8 +144,8 @@ 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(bindings \\ [], opts \\ [], do: block) do
|
||||||
Builder.build(block, __CALLER__, Builder.builder_mod(__CALLER__), opts)
|
Builder.build(block, __CALLER__, Builder.builder_mod(__CALLER__), bindings, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -17,13 +17,15 @@ defmodule RDF.Graph.Builder do
|
||||||
def exclude(_), do: nil
|
def exclude(_), do: nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def build(do_block, env, opts) do
|
def build(do_block, env, bindings, opts) do
|
||||||
build(do_block, env, builder_mod(env), opts)
|
build(do_block, env, builder_mod(env), bindings, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def build({:__block__, _, block}, env, builder_mod, opts) do
|
def build({:__block__, _, block}, env, builder_mod, bindings, opts) do
|
||||||
env_aliases = env_aliases(env)
|
env_aliases = env_aliases(env)
|
||||||
block = expand_aliased_modules(block, env_aliases)
|
block = expand_aliased_modules(block, env_aliases)
|
||||||
|
{bindings_vars, bindings_pattern} = bindings_vars(bindings, builder_mod)
|
||||||
|
block = bind_vars(block, bindings_vars, builder_mod)
|
||||||
non_strict_ns = extract_non_strict_ns(block)
|
non_strict_ns = extract_non_strict_ns(block)
|
||||||
{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)
|
||||||
|
@ -39,7 +41,7 @@ defmodule RDF.Graph.Builder do
|
||||||
@compile {:no_warn_undefined, mod}
|
@compile {:no_warn_undefined, mod}
|
||||||
end
|
end
|
||||||
|
|
||||||
def build(opts) do
|
def build(unquote(bindings_pattern), opts) do
|
||||||
alias RDF.XSD
|
alias RDF.XSD
|
||||||
alias RDF.NS.{RDFS, OWL}
|
alias RDF.NS.{RDFS, OWL}
|
||||||
|
|
||||||
|
@ -60,12 +62,12 @@ defmodule RDF.Graph.Builder do
|
||||||
Module.create(builder_mod, mod_body, Macro.Env.location(env))
|
Module.create(builder_mod, mod_body, Macro.Env.location(env))
|
||||||
|
|
||||||
quote do
|
quote do
|
||||||
apply(unquote(builder_mod), :build, [unquote(opts)])
|
apply(unquote(builder_mod), :build, [Map.new(unquote(bindings)), unquote(opts)])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def build(single, env, builder_mod, opts) do
|
def build(single, env, builder_mod, bindings, opts) do
|
||||||
build({:__block__, [], List.wrap(single)}, env, builder_mod, opts)
|
build({:__block__, [], List.wrap(single)}, env, builder_mod, bindings, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
|
@ -247,6 +249,19 @@ defmodule RDF.Graph.Builder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp bindings_vars(bindings, mod) do
|
||||||
|
vars = Enum.map(bindings, fn {key, _} -> key end)
|
||||||
|
|
||||||
|
{vars, {:%{}, [], Enum.map(vars, &{&1, Macro.var(&1, mod)})}}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp bind_vars(block, bindings_vars, mod) do
|
||||||
|
Macro.prewalk(block, fn
|
||||||
|
{var, line, nil} = expr -> if var in bindings_vars, do: {var, line, mod}, else: expr
|
||||||
|
other -> other
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
defp env_aliases(env) do
|
defp env_aliases(env) do
|
||||||
Map.new(env.aliases, fn {short, full} ->
|
Map.new(env.aliases, fn {short, full} ->
|
||||||
{
|
{
|
||||||
|
|
|
@ -495,7 +495,7 @@ defmodule RDF.Graph.BuilderTest do
|
||||||
|
|
||||||
test "merge with prefixes opt" do
|
test "merge with prefixes opt" do
|
||||||
graph =
|
graph =
|
||||||
RDF.Graph.build prefixes: [custom: EX] do
|
RDF.Graph.build [], prefixes: [custom: EX] do
|
||||||
@prefix custom: TestNS.Custom
|
@prefix custom: TestNS.Custom
|
||||||
|
|
||||||
Custom.S |> Custom.p(Custom.O)
|
Custom.S |> Custom.p(Custom.O)
|
||||||
|
@ -604,6 +604,103 @@ defmodule RDF.Graph.BuilderTest do
|
||||||
assert graph == RDF.graph(EX.S |> EX.p(EX.O))
|
assert graph == RDF.graph(EX.S |> EX.p(EX.O))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "bindings" do
|
||||||
|
test "passing a variable from the outer context" do
|
||||||
|
literal = "foo"
|
||||||
|
|
||||||
|
graph =
|
||||||
|
RDF.Graph.build literal: literal do
|
||||||
|
EX.S1 |> EX.p1(EX.O1)
|
||||||
|
EX.S2 |> EX.p2(literal)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert graph ==
|
||||||
|
RDF.graph([
|
||||||
|
EX.S1 |> EX.p1(EX.O1),
|
||||||
|
EX.S2 |> EX.p2("foo")
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "passing a complex expression from the outer context" do
|
||||||
|
literal = "foo"
|
||||||
|
|
||||||
|
graph =
|
||||||
|
RDF.Graph.build literal: String.upcase(literal <> "bar") do
|
||||||
|
EX.S1 |> EX.p1(EX.O1)
|
||||||
|
EX.S2 |> EX.p2(literal)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert graph ==
|
||||||
|
RDF.graph([
|
||||||
|
EX.S1 |> EX.p1(EX.O1),
|
||||||
|
EX.S2 |> EX.p2("FOOBAR")
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "passing a graph from a previous build step" do
|
||||||
|
graph =
|
||||||
|
(fn ->
|
||||||
|
graph1 =
|
||||||
|
RDF.Graph.build do
|
||||||
|
@prefix ad: "http://example.com/ad-hoc/"
|
||||||
|
@prefix ex1: "http://example.com/ad-hoc/ex1#"
|
||||||
|
|
||||||
|
Ad.S |> Ad.p(Ex1.O)
|
||||||
|
end
|
||||||
|
|
||||||
|
RDF.Graph.build graph: graph1 do
|
||||||
|
@prefix ad: "http://example.com/ad-hoc/"
|
||||||
|
@prefix ex2: "http://example.com/ad-hoc/ex2#"
|
||||||
|
|
||||||
|
graph
|
||||||
|
|
||||||
|
Ad.S |> Ad.p(Ex2.O)
|
||||||
|
end
|
||||||
|
end).()
|
||||||
|
|
||||||
|
assert graph ==
|
||||||
|
RDF.graph(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
RDF.iri("http://example.com/ad-hoc/S"),
|
||||||
|
RDF.iri("http://example.com/ad-hoc/p"),
|
||||||
|
RDF.iri("http://example.com/ad-hoc/ex1#O")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RDF.iri("http://example.com/ad-hoc/S"),
|
||||||
|
RDF.iri("http://example.com/ad-hoc/p"),
|
||||||
|
RDF.iri("http://example.com/ad-hoc/ex2#O")
|
||||||
|
}
|
||||||
|
],
|
||||||
|
prefixes:
|
||||||
|
RDF.default_prefixes(
|
||||||
|
ad: "http://example.com/ad-hoc/",
|
||||||
|
ex1: "http://example.com/ad-hoc/ex1#",
|
||||||
|
ex2: "http://example.com/ad-hoc/ex2#"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with opts" do
|
||||||
|
literal = "foo"
|
||||||
|
|
||||||
|
graph =
|
||||||
|
RDF.Graph.build [literal: literal], name: EX.Graph do
|
||||||
|
EX.S1 |> EX.p1(EX.O1)
|
||||||
|
EX.S2 |> EX.p2(literal)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert graph ==
|
||||||
|
RDF.graph(
|
||||||
|
[
|
||||||
|
EX.S1 |> EX.p1(EX.O1),
|
||||||
|
EX.S2 |> EX.p2("foo")
|
||||||
|
],
|
||||||
|
name: EX.Graph
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "opts" do
|
test "opts" do
|
||||||
initial = {EX.S, EX.p(), "init"}
|
initial = {EX.S, EX.p(), "init"}
|
||||||
|
|
||||||
|
@ -615,7 +712,7 @@ defmodule RDF.Graph.BuilderTest do
|
||||||
]
|
]
|
||||||
|
|
||||||
graph =
|
graph =
|
||||||
RDF.Graph.build opts do
|
RDF.Graph.build [], opts do
|
||||||
EX.S |> EX.p(EX.O)
|
EX.S |> EX.p(EX.O)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue