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
|
||||
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
|
||||
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
|
||||
which implement the `RDF.Data` protocol can be given also as the second argument
|
||||
(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).
|
||||
"""
|
||||
defmacro build(opts \\ [], do: block) do
|
||||
Builder.build(block, __CALLER__, Builder.builder_mod(__CALLER__), opts)
|
||||
defmacro build(bindings \\ [], opts \\ [], do: block) do
|
||||
Builder.build(block, __CALLER__, Builder.builder_mod(__CALLER__), bindings, opts)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
|
|
@ -17,13 +17,15 @@ defmodule RDF.Graph.Builder do
|
|||
def exclude(_), do: nil
|
||||
end
|
||||
|
||||
def build(do_block, env, opts) do
|
||||
build(do_block, env, builder_mod(env), opts)
|
||||
def build(do_block, env, bindings, opts) do
|
||||
build(do_block, env, builder_mod(env), bindings, opts)
|
||||
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)
|
||||
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)
|
||||
{declarations, data} = Enum.split_with(block, &declaration?/1)
|
||||
{base, declarations} = extract_base(declarations)
|
||||
|
@ -39,7 +41,7 @@ defmodule RDF.Graph.Builder do
|
|||
@compile {:no_warn_undefined, mod}
|
||||
end
|
||||
|
||||
def build(opts) do
|
||||
def build(unquote(bindings_pattern), opts) do
|
||||
alias RDF.XSD
|
||||
alias RDF.NS.{RDFS, OWL}
|
||||
|
||||
|
@ -60,12 +62,12 @@ defmodule RDF.Graph.Builder do
|
|||
Module.create(builder_mod, mod_body, Macro.Env.location(env))
|
||||
|
||||
quote do
|
||||
apply(unquote(builder_mod), :build, [unquote(opts)])
|
||||
apply(unquote(builder_mod), :build, [Map.new(unquote(bindings)), unquote(opts)])
|
||||
end
|
||||
end
|
||||
|
||||
def build(single, env, builder_mod, opts) do
|
||||
build({:__block__, [], List.wrap(single)}, env, builder_mod, opts)
|
||||
def build(single, env, builder_mod, bindings, opts) do
|
||||
build({:__block__, [], List.wrap(single)}, env, builder_mod, bindings, opts)
|
||||
end
|
||||
|
||||
@doc false
|
||||
|
@ -247,6 +249,19 @@ defmodule RDF.Graph.Builder do
|
|||
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
|
||||
Map.new(env.aliases, fn {short, full} ->
|
||||
{
|
||||
|
|
|
@ -495,7 +495,7 @@ defmodule RDF.Graph.BuilderTest do
|
|||
|
||||
test "merge with prefixes opt" do
|
||||
graph =
|
||||
RDF.Graph.build prefixes: [custom: EX] do
|
||||
RDF.Graph.build [], prefixes: [custom: EX] do
|
||||
@prefix custom: TestNS.Custom
|
||||
|
||||
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))
|
||||
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
|
||||
initial = {EX.S, EX.p(), "init"}
|
||||
|
||||
|
@ -615,7 +712,7 @@ defmodule RDF.Graph.BuilderTest do
|
|||
]
|
||||
|
||||
graph =
|
||||
RDF.Graph.build opts do
|
||||
RDF.Graph.build [], opts do
|
||||
EX.S |> EX.p(EX.O)
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue