Add RDF.PrefixMap.merge/3 and RDF.PrefixMap.merge!/3
This commit is contained in:
parent
b75024baf5
commit
bee98f3e75
2 changed files with 96 additions and 5 deletions
|
@ -96,6 +96,8 @@ defmodule RDF.PrefixMap do
|
||||||
prefixes mapped to different namespaces an `:ok` tuple is returned.
|
prefixes mapped to different namespaces an `:ok` tuple is returned.
|
||||||
Otherwise an `:error` tuple with the list of prefixes with conflicting
|
Otherwise an `:error` tuple with the list of prefixes with conflicting
|
||||||
namespaces is returned.
|
namespaces is returned.
|
||||||
|
|
||||||
|
See also `merge/3` which allows you to resolve conflicts with a function.
|
||||||
"""
|
"""
|
||||||
def merge(prefix_map1, prefix_map2)
|
def merge(prefix_map1, prefix_map2)
|
||||||
|
|
||||||
|
@ -114,6 +116,53 @@ defmodule RDF.PrefixMap do
|
||||||
raise ArgumentError, "#{inspect(other_prefixes)} is not convertible to a RDF.PrefixMap"
|
raise ArgumentError, "#{inspect(other_prefixes)} is not convertible to a RDF.PrefixMap"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Merges two `RDF.PrefixMap`s, resolving conflicts through the given `fun`.
|
||||||
|
|
||||||
|
The second prefix map can also be given as any structure which can converted
|
||||||
|
to a `RDF.PrefixMap` via `new/1`.
|
||||||
|
|
||||||
|
The given function will be invoked when there are conflicting mappings of
|
||||||
|
prefixes to different namespaces; its arguments are `prefix`, `namespace1`
|
||||||
|
(the namespace for the prefix in the first prefix map),
|
||||||
|
and `namespace2` (the namespace for the prefix in the second prefix map).
|
||||||
|
The value returned by `fun` is used as the namespace for the prefix in the
|
||||||
|
resulting prefix map.
|
||||||
|
Non-`RDF.IRI` values will be tried to be converted to converted to `RDF.IRI`
|
||||||
|
via `RDF.IRI.new` implicitly.
|
||||||
|
|
||||||
|
If a conflict can't be resolved, the provided function can return `nil`.
|
||||||
|
This will result in an overall return of an `:error` tuple with the list of
|
||||||
|
prefixes for which the conflict couldn't be resolved.
|
||||||
|
|
||||||
|
If everything could be merged, an `:ok` tuple is returned.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def merge(%__MODULE__{map: map1}, %__MODULE__{map: map2}, fun) when is_function(fun) do
|
||||||
|
conflict_resolution = fn prefix, namespace1, namespace2 ->
|
||||||
|
case fun.(prefix, namespace1, namespace2) do
|
||||||
|
nil -> :conflict
|
||||||
|
result -> IRI.new(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
with resolved_merge = Map.merge(map1, map2, conflict_resolution),
|
||||||
|
[] <- resolved_merge_rest_conflicts(resolved_merge) do
|
||||||
|
{:ok, %__MODULE__{map: resolved_merge}}
|
||||||
|
else
|
||||||
|
conflicts -> {:error, conflicts}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge(prefix_map1, prefix_map2, nil), do: merge(prefix_map1, prefix_map2)
|
||||||
|
|
||||||
|
defp resolved_merge_rest_conflicts(map) do
|
||||||
|
Enum.reduce(map, [], fn
|
||||||
|
{prefix, :conflict}, conflicts -> [prefix | conflicts]
|
||||||
|
_, conflicts -> conflicts
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
defp merge_conflicts(map1, map2) do
|
defp merge_conflicts(map1, map2) do
|
||||||
Enum.reduce(map1, [], fn {prefix, namespace}, conflicts ->
|
Enum.reduce(map1, [], fn {prefix, namespace}, conflicts ->
|
||||||
if conflicts?(map2, prefix, namespace) do
|
if conflicts?(map2, prefix, namespace) do
|
||||||
|
@ -130,9 +179,11 @@ defmodule RDF.PrefixMap do
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Merges two `RDF.PrefixMap`s and raises an exception in error cases.
|
Merges two `RDF.PrefixMap`s and raises an exception in error cases.
|
||||||
|
|
||||||
|
See `merge/2` and `merge/3` for more information on merging prefix maps.
|
||||||
"""
|
"""
|
||||||
def merge!(prefix_map1, prefix_map2) do
|
def merge!(prefix_map1, prefix_map2, fun \\ nil) do
|
||||||
with {:ok, new_prefix_map} <- merge(prefix_map1, prefix_map2) do
|
with {:ok, new_prefix_map} <- merge(prefix_map1, prefix_map2, fun) do
|
||||||
new_prefix_map
|
new_prefix_map
|
||||||
else
|
else
|
||||||
{:error, conflicts} ->
|
{:error, conflicts} ->
|
||||||
|
@ -143,7 +194,7 @@ defmodule RDF.PrefixMap do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Deletes a prefix mapping from the given `RDF.PrefixMap`..
|
Deletes a prefix mapping from the given `RDF.PrefixMap`.
|
||||||
"""
|
"""
|
||||||
def delete(prefix_map, prefix)
|
def delete(prefix_map, prefix)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule RDF.PrefixMapTest do
|
||||||
@ex_ns1 ~I<http://example.com/foo/>
|
@ex_ns1 ~I<http://example.com/foo/>
|
||||||
@ex_ns2 ~I<http://example.com/bar#>
|
@ex_ns2 ~I<http://example.com/bar#>
|
||||||
@ex_ns3 ~I<http://example.com/baz#>
|
@ex_ns3 ~I<http://example.com/baz#>
|
||||||
|
@ex_ns4 ~I<http://other.com/foo>
|
||||||
|
|
||||||
@example1 %PrefixMap{map: %{ex1: @ex_ns1}}
|
@example1 %PrefixMap{map: %{ex1: @ex_ns1}}
|
||||||
|
|
||||||
|
@ -98,8 +99,6 @@ defmodule RDF.PrefixMapTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "merge/2" do
|
describe "merge/2" do
|
||||||
@ex_ns4 ~I<http://other.com/foo>
|
|
||||||
|
|
||||||
test "when the prefix maps are disjunctive" do
|
test "when the prefix maps are disjunctive" do
|
||||||
other_prefix_map = PrefixMap.new(ex3: @ex_ns3)
|
other_prefix_map = PrefixMap.new(ex3: @ex_ns3)
|
||||||
assert PrefixMap.merge(@example2, other_prefix_map) == {:ok, @example3}
|
assert PrefixMap.merge(@example2, other_prefix_map) == {:ok, @example3}
|
||||||
|
@ -128,6 +127,32 @@ defmodule RDF.PrefixMapTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "merge/3" do
|
||||||
|
test "with a function resolving conflicts by choosing one of the inputs" do
|
||||||
|
other_prefix_map = PrefixMap.new(ex3: @ex_ns4)
|
||||||
|
assert PrefixMap.merge(@example3, other_prefix_map,
|
||||||
|
fn _prefix, ns1, _ns2 -> ns1 end) == {:ok, @example3}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with a function which does not resolve by returning nil" do
|
||||||
|
other_prefix_map = PrefixMap.new(ex3: @ex_ns4)
|
||||||
|
assert PrefixMap.merge(@example3, other_prefix_map, fn _, _, _ -> nil end) ==
|
||||||
|
PrefixMap.merge(@example3, other_prefix_map)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with a function just partially resolving handling conflicts" do
|
||||||
|
assert PrefixMap.merge(@example3, @example3,
|
||||||
|
fn prefix, ns1, _ -> if prefix == :ex2, do: ns1 end) ==
|
||||||
|
{:error, [:ex3, :ex1]}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when the function returns a non-IRI value which is convertible" do
|
||||||
|
assert PrefixMap.merge(@example1, @example1,
|
||||||
|
fn _, _, _ -> "http://example.com/" end) ==
|
||||||
|
{:ok, PrefixMap.new(ex1: "http://example.com/")}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "merge!/2" do
|
describe "merge!/2" do
|
||||||
test "when the prefix maps can be merged" do
|
test "when the prefix maps can be merged" do
|
||||||
other_prefix_map = PrefixMap.new(ex3: @ex_ns3)
|
other_prefix_map = PrefixMap.new(ex3: @ex_ns3)
|
||||||
|
@ -141,6 +166,21 @@ defmodule RDF.PrefixMapTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "merge!/3" do
|
||||||
|
test "when all conflicts can be resolved" do
|
||||||
|
other_prefix_map = PrefixMap.new(ex3: @ex_ns4)
|
||||||
|
assert PrefixMap.merge!(@example3, other_prefix_map,
|
||||||
|
fn _prefix, ns1, _ns2 -> ns1 end) == @example3
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when not all conflicts can be resolved" do
|
||||||
|
assert_raise RuntimeError, "conflicting prefix mappings: :ex1", fn ->
|
||||||
|
PrefixMap.merge!(@example2, @example2,
|
||||||
|
fn prefix, ns1, _ -> if prefix == :ex2, do: ns1 end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "delete/2" do
|
describe "delete/2" do
|
||||||
test "when a mapping of the given prefix exists" do
|
test "when a mapping of the given prefix exists" do
|
||||||
assert PrefixMap.delete(@example2, :ex2) == @example1
|
assert PrefixMap.delete(@example2, :ex2) == @example1
|
||||||
|
|
Loading…
Reference in a new issue