Make RDF.Data.merge/3 implementations commutative

This commit is contained in:
Marcel Otto 2022-05-16 22:03:03 +02:00
parent 768bc9ae09
commit af5cc26d7f
4 changed files with 77 additions and 2 deletions

View file

@ -9,6 +9,10 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
### Changed
- `RDF.Data.merge/3` is 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
had to be given as the first argument)
- several performance improvements

View file

@ -3,8 +3,6 @@ defprotocol RDF.Data do
An abstraction over the different data structures for collections of RDF statements.
"""
@type t :: RDF.Description.t() | RDF.Graph.t() | RDF.Dataset.t()
@doc """
Adds statements to a RDF data structure.
@ -166,6 +164,14 @@ defimpl RDF.Data, for: RDF.Description do
def merge(description, %Dataset{} = dataset, opts),
do: RDF.Data.merge(dataset, description, opts)
def merge(description, %_{} = other, opts) do
if RDF.Data.impl_for(other) do
RDF.Data.merge(other, description, opts)
else
raise ArgumentError, "no RDF.Data implementation found for #{inspect(other)}"
end
end
def delete(description, input, opts \\ [])
def delete(
@ -272,6 +278,14 @@ defimpl RDF.Data, for: RDF.Graph do
def merge(graph, %Dataset{} = dataset, opts),
do: RDF.Data.merge(dataset, graph, opts)
def merge(graph, %_{} = other, opts) do
if RDF.Data.impl_for(other) do
RDF.Data.merge(other, graph, opts)
else
raise ArgumentError, "no RDF.Data implementation found for #{inspect(other)}"
end
end
def delete(graph, input, opts \\ [])
def delete(%Graph{name: name} = graph, %Graph{name: other_name}, _opts)
@ -340,6 +354,14 @@ defimpl RDF.Data, for: RDF.Dataset do
def merge(dataset, %Dataset{} = other_dataset, opts),
do: Dataset.add(dataset, other_dataset, opts)
def merge(dataset, %_{} = other, opts) do
if RDF.Data.impl_for(other) do
RDF.Data.merge(other, dataset, opts)
else
raise ArgumentError, "no RDF.Data implementation found for #{inspect(other)}"
end
end
def delete(dataset, input, opts \\ [])
def delete(%Dataset{name: name} = dataset, %Dataset{name: other_name}, _opts)

View file

@ -0,0 +1,32 @@
defmodule External do
defstruct []
import RDF.Sigils
def data, do: RDF.triple(~I<http://example.com/S>, ~I<http://example.com/p>, 42)
defimpl RDF.Data do
def merge(%External{}, data, opts) do
RDF.Data.merge(data, External.data(), opts)
end
def delete(_external, _, _opts), do: nil
def pop(_external), do: nil
def empty?(_external), do: false
def include?(_external, _, _opts \\ []), do: false
def describes?(_external, _), do: false
def description(_external, _), do: nil
def descriptions(_external), do: []
def statements(_external), do: []
def subjects(_external), do: []
def predicates(_external), do: []
def objects(_external), do: []
def resources(_external), do: []
def subject_count(_external), do: 0
def statement_count(_external), do: 0
def values(_external, _opts \\ []), do: nil
def map(_external, _fun), do: nil
def equal?(_, _), do: false
end
end

View file

@ -625,4 +625,21 @@ defmodule RDF.DataTest do
refute RDF.Data.equal?(dataset, graph)
end
end
test "external RDF.Data protocol implementations" do
description = EX.S |> EX.p(EX.O2)
assert RDF.Data.merge(description, %External{}) ==
Description.add(description, External.data())
graph = RDF.graph({EX.S, EX.p(), EX.O2})
assert RDF.Data.merge(graph, %External{}) ==
Graph.add(graph, External.data())
dataset = RDF.dataset({EX.S, EX.p(), EX.O2})
assert RDF.Data.merge(dataset, %External{}) ==
Dataset.add(dataset, External.data())
end
end