Limit the forms of input on RDF.Description functions

This commit is contained in:
Marcel Otto 2020-07-27 23:09:23 +02:00
parent 3f6393a191
commit e9432ef556
9 changed files with 611 additions and 573 deletions

View file

@ -233,8 +233,6 @@ defmodule RDF do
defdelegate quad(tuple), to: Quad, as: :new defdelegate quad(tuple), to: Quad, as: :new
defdelegate description(arg), to: Description, as: :new defdelegate description(arg), to: Description, as: :new
defdelegate description(arg1, arg2), to: Description, as: :new
defdelegate description(arg1, arg2, arg3), to: Description, as: :new
defdelegate graph(), to: Graph, as: :new defdelegate graph(), to: Graph, as: :new
defdelegate graph(arg), to: Graph, as: :new defdelegate graph(arg), to: Graph, as: :new

View file

@ -15,294 +15,154 @@ defmodule RDF.Description do
import RDF.Statement import RDF.Statement
alias RDF.{Statement, Triple} alias RDF.{Statement, Triple}
@type predications :: %{Statement.predicate() => %{Statement.object() => nil}} @enforce_keys [:subject]
defstruct subject: nil, predications: %{}
@type statements ::
{Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_predicate()]}
| Statement.t()
| predications
| t
@type t :: %__MODULE__{ @type t :: %__MODULE__{
subject: Statement.subject(), subject: Statement.subject(),
predications: predications predications: predications
} }
@enforce_keys [:subject] @type predications :: %{Statement.predicate() => %{Statement.object() => nil}}
defstruct subject: nil, predications: %{}
@type input ::
Triple.coercible_t()
| {Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]}
| %{
Statement.coercible_predicate() =>
Statement.coercible_object() | [Statement.coercible_object()]
}
| [
Triple.coercible_t()
| {Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]}
| t
]
| t
@doc """ @doc """
Creates a new `RDF.Description` about the given subject with optional initial statements. Creates an empty `RDF.Description` about the given subject.
When given a list of statements, the first one must contain a subject.
""" """
@spec new(Statement.coercible_subject() | statements | [statements]) :: t @spec new(Statement.coercible_subject() | Triple.coercible_t() | t) :: t
def new(subject) def new(subject)
def new(%__MODULE__{} = description), do: new(description.subject)
def new({subject, predicate, object}), def new({subject, predicate, object}), do: new(subject) |> add({predicate, object})
do: new(subject) |> add(predicate, object) def new(subject), do: %__MODULE__{subject: coerce_subject(subject)}
def new([statement | more_statements]),
do: new(statement) |> add(more_statements)
def new(%__MODULE__{} = description),
do: description
def new(subject),
do: %__MODULE__{subject: coerce_subject(subject)}
@doc """ @doc """
Creates a new `RDF.Description` about the given subject with optional initial statements. Add statements to a `RDF.Description`.
"""
@spec new(Statement.coercible_subject(), statements | [statements]) :: t
def new(subject, {predicate, objects}),
do: new(subject) |> add(predicate, objects)
def new(subject, statements) when is_list(statements),
do: new(subject) |> add(statements)
def new(subject, %__MODULE__{predications: predications}),
do: %__MODULE__{new(subject) | predications: predications}
def new(subject, predications = %{}),
do: new(subject) |> add(predications)
@doc """
Creates a new `RDF.Description` about the given subject with optional initial statements.
"""
@spec new(
Statement.coercible_subject() | statements | [statements],
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]
) :: t
def new(%__MODULE__{} = description, predicate, objects),
do: add(description, predicate, objects)
def new(subject, predicate, objects),
do: new(subject) |> add(predicate, objects)
@doc """
Add objects to a predicate of a `RDF.Description`.
## Examples
iex> RDF.Description.add(RDF.Description.new({EX.S, EX.P1, EX.O1}), EX.P2, EX.O2)
RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}])
iex> RDF.Description.add(RDF.Description.new({EX.S, EX.P, EX.O1}), EX.P, [EX.O2, EX.O3])
RDF.Description.new([{EX.S, EX.P, EX.O1}, {EX.S, EX.P, EX.O2}, {EX.S, EX.P, EX.O3}])
"""
@spec add(
t,
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]
) :: t
def add(description, predicate, objects)
def add(description, predicate, objects) when is_list(objects) do
Enum.reduce(objects, description, fn object, description ->
add(description, predicate, object)
end)
end
def add(%__MODULE__{subject: subject, predications: predications}, predicate, object) do
with triple_predicate = coerce_predicate(predicate),
triple_object = coerce_object(object),
new_predications =
Map.update(predications, triple_predicate, %{triple_object => nil}, fn objects ->
Map.put_new(objects, triple_object, nil)
end) do
%__MODULE__{subject: subject, predications: new_predications}
end
end
@doc """
Adds statements to a `RDF.Description`.
Note: When the statements to be added are given as another `RDF.Description`, Note: When the statements to be added are given as another `RDF.Description`,
the subject must not match subject of the description to which the statements the subject must not match subject of the description to which the statements
are added. As opposed to that `RDF.Data.merge/2` will produce a `RDF.Graph` are added. As opposed to that `RDF.Data.merge/2` will produce a `RDF.Graph`
containing both descriptions. containing both descriptions.
"""
@spec add(t, statements | [statements]) :: t
def add(description, statements)
def add(description, {predicate, object}),
do: add(description, predicate, object)
def add(description = %__MODULE__{}, {subject, predicate, object}) do
if coerce_subject(subject) == description.subject,
do: add(description, predicate, object),
else: description
end
def add(description, {subject, predicate, object, _}),
do: add(description, {subject, predicate, object})
def add(description, statements) when is_list(statements) do
Enum.reduce(statements, description, fn statement, description ->
add(description, statement)
end)
end
def add(
%__MODULE__{subject: subject, predications: predications},
%__MODULE__{predications: other_predications}
) do
merged_predications =
Map.merge(predications, other_predications, fn _, objects, other_objects ->
Map.merge(objects, other_objects)
end)
%__MODULE__{subject: subject, predications: merged_predications}
end
def add(description = %__MODULE__{}, predications = %{}) do
Enum.reduce(predications, description, fn {predicate, objects}, description ->
add(description, predicate, objects)
end)
end
@doc """
Puts objects to a predicate of a `RDF.Description`, overwriting all existing objects.
## Examples ## Examples
iex> RDF.Description.put(RDF.Description.new({EX.S, EX.P, EX.O1}), EX.P, EX.O2) iex> RDF.Description.new({EX.S, EX.P1, EX.O1}) |> RDF.Description.add({EX.P2, EX.O2})
RDF.Description.new([{EX.S, EX.P, EX.O2}]) RDF.Description.new(EX.S) |> RDF.Description.add([{EX.P1, EX.O1}, {EX.P2, EX.O2}])
iex> RDF.Description.put(RDF.Description.new({EX.S, EX.P1, EX.O1}), EX.P2, EX.O2) iex> RDF.Description.new({EX.S, EX.P, EX.O1}) |> RDF.Description.add({EX.P, [EX.O2, EX.O3]})
RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}]) RDF.Description.new(EX.S) |> RDF.Description.add([{EX.P, EX.O1}, {EX.P, EX.O2}, {EX.P, EX.O3}])
"""
@spec put(
t,
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]
) :: t
def put(description, predicate, objects)
def put(%__MODULE__{subject: subject, predications: predications}, predicate, objects) """
when is_list(objects) do @spec add(t, input, keyword) :: t
with triple_predicate = coerce_predicate(predicate), def add(description, input, opts \\ [])
triple_objects =
Enum.reduce(objects, %{}, fn object, acc -> # This implementation is actually unnecessary as the implementation with the is_map clause
Map.put_new(acc, coerce_object(object), nil) # would work perfectly fine with RDF.Descriptions Enumerable implementation.
end), # It exists only for performance reasons, since this version is roughly twice as fast.
do: %__MODULE__{ def add(%__MODULE__{} = description, %__MODULE__{} = input_description, _opts) do
subject: subject, %__MODULE__{
predications: Map.put(predications, triple_predicate, triple_objects) description
} | predications:
Map.merge(
description.predications,
input_description.predications,
fn _predicate, objects, new_objects ->
Map.merge(objects, new_objects)
end
)
}
end end
def put(%__MODULE__{} = description, predicate, object), def add(description, predications, opts)
do: put(description, predicate, [object]) when is_map(predications) or is_list(predications) do
Enum.reduce(predications, description, fn
predications, description -> add(description, predications, opts)
end)
end
def add(%__MODULE__{} = description, {subject, predicate, objects}, opts) do
if coerce_subject(subject) == description.subject do
add(description, {predicate, objects}, opts)
else
description
end
end
def add(%__MODULE__{} = description, {predicate, objects}, _opts) do
normalized_objects =
objects
|> List.wrap()
|> Map.new(fn object -> {coerce_object(object), nil} end)
if Enum.empty?(normalized_objects) do
description
else
%__MODULE__{
description
| predications:
Map.update(
description.predications,
coerce_predicate(predicate),
normalized_objects,
fn objects ->
Map.merge(objects, normalized_objects)
end
)
}
end
end
@doc """ @doc """
Adds statements to a `RDF.Description` and overwrites all existing statements with already used predicates. Adds statements to a `RDF.Description` and overwrites all existing statements with already used predicates.
Note: As it is a destructive function this function is more strict in its handling of
`RDF.Description`s than `add/3`. The subject of a `RDF.Description` to be put must
match.
## Examples ## Examples
iex> RDF.Description.put(RDF.Description.new({EX.S, EX.P, EX.O1}), {EX.P, EX.O2}) iex> RDF.Description.put(RDF.Description.new({EX.S, EX.P, EX.O1}), {EX.P, EX.O2})
RDF.Description.new([{EX.S, EX.P, EX.O2}]) RDF.Description.new({EX.S, EX.P, EX.O2})
iex> RDF.Description.new({EX.S, EX.P1, EX.O1}) |>
...> RDF.Description.put([{EX.P2, EX.O2}, {EX.S, EX.P2, EX.O3}, {EX.P1, EX.O4}])
RDF.Description.new([{EX.S, EX.P1, EX.O4}, {EX.S, EX.P2, EX.O2}, {EX.S, EX.P2, EX.O3}])
iex> RDF.Description.new({EX.S, EX.P, EX.O1}) |>
...> RDF.Description.put(RDF.Description.new(EX.S, EX.P, [EX.O1, EX.O2]))
RDF.Description.new([{EX.S, EX.P, EX.O1}, {EX.S, EX.P, EX.O2}])
iex> RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}]) |>
...> RDF.Description.put(%{EX.P2 => [EX.O3, EX.O4]})
RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O3}, {EX.S, EX.P2, EX.O4}])
""" """
@spec put(t, statements | [statements]) :: t @spec put(t, input, keyword) :: t
def put(description, statements) def put(description, input, opts \\ [])
def put(%__MODULE__{} = description, {predicate, object}),
do: put(description, predicate, object)
def put(%__MODULE__{} = description, {subject, predicate, object}) do
if coerce_subject(subject) == description.subject,
do: put(description, predicate, object),
else: description
end
def put(description, {subject, predicate, object, _}),
do: put(description, {subject, predicate, object})
def put(%__MODULE__{subject: subject} = description, statements) when is_list(statements) do
statements
|> Stream.map(fn
{p, o} ->
{coerce_predicate(p), o}
{^subject, p, o} ->
{coerce_predicate(p), o}
{s, p, o} ->
if coerce_subject(s) == subject,
do: {coerce_predicate(p), o}
bad ->
raise ArgumentError, "#{inspect(bad)} is not a valid statement"
end)
# filter nil values
|> Stream.filter(& &1)
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
|> Enum.reduce(description, fn {predicate, objects}, description ->
put(description, predicate, objects)
end)
end
def put( def put(
%__MODULE__{subject: subject, predications: predications}, %__MODULE__{subject: subject} = description,
%__MODULE__{predications: other_predications} %__MODULE__{subject: subject} = input,
_opts
) do ) do
merged_predications = %__MODULE__{
Map.merge(predications, other_predications, fn _, _, other_objects -> other_objects end) description
| predications:
%__MODULE__{subject: subject, predications: merged_predications} Enum.reduce(
end input.predications,
description.predications,
def put(description = %__MODULE__{}, predications = %{}) do fn {predicate, objects}, predications ->
Enum.reduce(predications, description, fn {predicate, objects}, description -> Map.put(predications, predicate, objects)
put(description, predicate, objects)
end)
end
@doc """
Deletes statements from a `RDF.Description`.
"""
@spec delete(
t,
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]
) :: t
def delete(description, predicate, objects)
def delete(description, predicate, objects) when is_list(objects) do
Enum.reduce(objects, description, fn object, description ->
delete(description, predicate, object)
end)
end
def delete(%__MODULE__{subject: subject, predications: predications} = descr, predicate, object) do
with triple_predicate = coerce_predicate(predicate),
triple_object = coerce_object(object) do
if (objects = predications[triple_predicate]) && Map.has_key?(objects, triple_object) do
%__MODULE__{
subject: subject,
predications:
if map_size(objects) == 1 do
Map.delete(predications, triple_predicate)
else
Map.update!(predications, triple_predicate, fn objects ->
Map.delete(objects, triple_object)
end)
end end
} )
else }
descr end
end
end def put(%__MODULE__{} = description, %__MODULE__{}, _opts), do: description
def put(%__MODULE__{} = description, input, opts) do
put(description, description.subject |> new() |> add(input), opts)
end end
@doc """ @doc """
@ -313,37 +173,76 @@ defmodule RDF.Description do
are deleted. If you want to delete only a matching description subject, you can are deleted. If you want to delete only a matching description subject, you can
use `RDF.Data.delete/2`. use `RDF.Data.delete/2`.
""" """
@spec delete(t, statements | [statements]) :: t @spec delete(t, input, keyword) :: t
def delete(description, statements) def delete(description, input, opts \\ [])
def delete(desc = %__MODULE__{}, {predicate, object}), # This implementation is actually unnecessary as the implementation with the is_map clause
do: delete(desc, predicate, object) # would work perfectly fine with RDF.Descriptions Enumerable implementation.
# It exists only for performance reasons.
def delete(%__MODULE__{} = description, %__MODULE__{} = input_description, _opts) do
predications = description.predications
def delete(description = %__MODULE__{}, {subject, predicate, object}) do %__MODULE__{
if coerce_subject(subject) == description.subject, description
do: delete(description, predicate, object), | predications:
else: description Enum.reduce(
input_description.predications,
predications,
fn {predicate, objects}, predications ->
if current_objects = Map.get(description.predications, predicate) do
rest = Map.drop(current_objects, Map.keys(objects))
if Enum.empty?(rest) do
Map.delete(predications, predicate)
else
Map.put(predications, predicate, rest)
end
else
predications
end
end
)
}
end end
def delete(description, {subject, predicate, object, _}), def delete(description, predications, opts)
do: delete(description, {subject, predicate, object}) when is_map(predications) or is_list(predications) do
Enum.reduce(predications, description, fn
def delete(description, statements) when is_list(statements) do predications, description -> delete(description, predications, opts)
Enum.reduce(statements, description, fn statement, description ->
delete(description, statement)
end) end)
end end
def delete(description = %__MODULE__{}, other_description = %__MODULE__{}) do def delete(%__MODULE__{} = description, {subject, predicate, objects}, opts) do
Enum.reduce(other_description, description, fn {_, predicate, object}, description -> if coerce_subject(subject) == description.subject do
delete(description, predicate, object) delete(description, {predicate, objects}, opts)
end) else
description
end
end end
def delete(description = %__MODULE__{}, predications = %{}) do def delete(%__MODULE__{} = description, {predicate, objects}, _opts) do
Enum.reduce(predications, description, fn {predicate, objects}, description -> predicate = coerce_predicate(predicate)
delete(description, predicate, objects)
end) if current_objects = Map.get(description.predications, predicate) do
normalized_objects =
objects
|> List.wrap()
|> Enum.map(&coerce_object/1)
rest = Map.drop(current_objects, normalized_objects)
%__MODULE__{
description
| predications:
if Enum.empty?(rest) do
Map.delete(description.predications, predicate)
else
Map.put(description.predications, predicate, rest)
end
}
else
description
end
end end
@doc """ @doc """
@ -360,9 +259,10 @@ defmodule RDF.Description do
end end
def delete_predicates(%__MODULE__{subject: subject, predications: predications}, property) do def delete_predicates(%__MODULE__{subject: subject, predications: predications}, property) do
with property = coerce_predicate(property) do %__MODULE__{
%__MODULE__{subject: subject, predications: Map.delete(predications, property)} subject: subject,
end predications: Map.delete(predications, coerce_predicate(property))
}
end end
@doc """ @doc """
@ -372,10 +272,10 @@ defmodule RDF.Description do
## Examples ## Examples
iex> RDF.Description.fetch(RDF.Description.new({EX.S, EX.p, EX.O}), EX.p) iex> RDF.Description.new({EX.S, EX.p, EX.O}) |> RDF.Description.fetch(EX.p)
{:ok, [RDF.iri(EX.O)]} {:ok, [RDF.iri(EX.O)]}
iex> RDF.Description.fetch(RDF.Description.new([{EX.S, EX.P, EX.O1}, iex> RDF.Description.new(EX.S) |> RDF.Description.add([{EX.P, EX.O1}, {EX.P, EX.O2}]) |>
...> {EX.S, EX.P, EX.O2}]), EX.P) ...> RDF.Description.fetch(EX.P)
{:ok, [RDF.iri(EX.O1), RDF.iri(EX.O2)]} {:ok, [RDF.iri(EX.O1), RDF.iri(EX.O2)]}
iex> RDF.Description.fetch(RDF.Description.new(EX.S), EX.foo) iex> RDF.Description.fetch(RDF.Description.new(EX.S), EX.foo)
:error :error
@ -445,7 +345,7 @@ defmodule RDF.Description do
iex> RDF.Description.new({EX.S, EX.p, EX.O}) |> iex> RDF.Description.new({EX.S, EX.p, EX.O}) |>
...> RDF.Description.update(EX.p, fn objects -> [EX.O2 | objects] end) ...> RDF.Description.update(EX.p, fn objects -> [EX.O2 | objects] end)
RDF.Description.new([{EX.S, EX.p, EX.O}, {EX.S, EX.p, EX.O2}]) RDF.Description.new(EX.S) |> RDF.Description.add([{EX.p, EX.O}, {EX.p, EX.O2}])
iex> RDF.Description.new(EX.S) |> iex> RDF.Description.new(EX.S) |>
...> RDF.Description.update(EX.p, EX.O, fn _ -> EX.O2 end) ...> RDF.Description.update(EX.p, EX.O, fn _ -> EX.O2 end)
RDF.Description.new({EX.S, EX.p, EX.O}) RDF.Description.new({EX.S, EX.p, EX.O})
@ -463,7 +363,7 @@ defmodule RDF.Description do
case get(description, predicate) do case get(description, predicate) do
nil -> nil ->
if initial do if initial do
put(description, predicate, initial) put(description, {predicate, initial})
else else
description description
end end
@ -474,7 +374,7 @@ defmodule RDF.Description do
|> List.wrap() |> List.wrap()
|> case do |> case do
[] -> delete_predicates(description, predicate) [] -> delete_predicates(description, predicate)
objects -> put(description, predicate, objects) objects -> put(description, {predicate, objects})
end end
end end
end end
@ -500,7 +400,8 @@ defmodule RDF.Description do
...> {current_objects, EX.NEW} ...> {current_objects, EX.NEW}
...> end) ...> end)
{[RDF.iri(EX.O)], RDF.Description.new({EX.S, EX.P, EX.NEW})} {[RDF.iri(EX.O)], RDF.Description.new({EX.S, EX.P, EX.NEW})}
iex> RDF.Description.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}]) |> iex> RDF.Graph.new([{EX.S, EX.P1, EX.O1}, {EX.S, EX.P2, EX.O2}]) |>
...> RDF.Graph.description(EX.S) |>
...> RDF.Description.get_and_update(EX.P1, fn _ -> :pop end) ...> RDF.Description.get_and_update(EX.P1, fn _ -> :pop end)
{[RDF.iri(EX.O1)], RDF.Description.new({EX.S, EX.P2, EX.O2})} {[RDF.iri(EX.O1)], RDF.Description.new({EX.S, EX.P2, EX.O2})}
""" """
@ -511,14 +412,14 @@ defmodule RDF.Description do
([Statement.Object] -> {[Statement.Object], t} | :pop) ([Statement.Object] -> {[Statement.Object], t} | :pop)
) :: {[Statement.Object], t} ) :: {[Statement.Object], t}
def get_and_update(description = %__MODULE__{}, predicate, fun) do def get_and_update(description = %__MODULE__{}, predicate, fun) do
with triple_predicate = coerce_predicate(predicate) do triple_predicate = coerce_predicate(predicate)
case fun.(get(description, triple_predicate)) do
{objects_to_return, new_objects} ->
{objects_to_return, put(description, triple_predicate, new_objects)}
:pop -> case fun.(get(description, triple_predicate)) do
pop(description, triple_predicate) {objects_to_return, new_objects} ->
end {objects_to_return, put(description, {triple_predicate, new_objects})}
:pop ->
pop(description, triple_predicate)
end end
end end
@ -572,10 +473,10 @@ defmodule RDF.Description do
## Examples ## Examples
iex> RDF.Description.new([ iex> RDF.Description.new(EX.S1) |> RDF.Description.add([
...> {EX.S1, EX.p1, EX.O1}, ...> {EX.p1, EX.O1},
...> {EX.p2, EX.O2}, ...> {EX.p2, EX.O2},
...> {EX.p2, EX.O3}]) |> ...> {EX.p2, EX.O3}]) |>
...> RDF.Description.predicates ...> RDF.Description.predicates
MapSet.new([EX.p1, EX.p2]) MapSet.new([EX.p1, EX.p2])
""" """
@ -590,12 +491,12 @@ defmodule RDF.Description do
## Examples ## Examples
iex> RDF.Description.new([ iex> RDF.Description.new(EX.S1) |> RDF.Description.add([
...> {EX.S1, EX.p1, EX.O1}, ...> {EX.p1, EX.O1},
...> {EX.p2, EX.O2}, ...> {EX.p2, EX.O2},
...> {EX.p3, EX.O2}, ...> {EX.p3, EX.O2},
...> {EX.p4, RDF.bnode(:bnode)}, ...> {EX.p4, RDF.bnode(:bnode)},
...> {EX.p3, "foo"} ...> {EX.p3, "foo"}
...> ]) |> RDF.Description.objects ...> ]) |> RDF.Description.objects
MapSet.new([RDF.iri(EX.O1), RDF.iri(EX.O2), RDF.bnode(:bnode)]) MapSet.new([RDF.iri(EX.O1), RDF.iri(EX.O2), RDF.bnode(:bnode)])
""" """
@ -622,12 +523,12 @@ defmodule RDF.Description do
## Examples ## Examples
iex> RDF.Description.new([ iex> RDF.Description.new(EX.S1) |> RDF.Description.add([
...> {EX.S1, EX.p1, EX.O1}, ...> {EX.p1, EX.O1},
...> {EX.p2, EX.O2}, ...> {EX.p2, EX.O2},
...> {EX.p1, EX.O2}, ...> {EX.p1, EX.O2},
...> {EX.p2, RDF.bnode(:bnode)}, ...> {EX.p2, RDF.bnode(:bnode)},
...> {EX.p3, "foo"} ...> {EX.p3, "foo"}
...> ]) |> RDF.Description.resources ...> ]) |> RDF.Description.resources
MapSet.new([RDF.iri(EX.O1), RDF.iri(EX.O2), RDF.bnode(:bnode), EX.p1, EX.p2, EX.p3]) MapSet.new([RDF.iri(EX.O1), RDF.iri(EX.O2), RDF.bnode(:bnode), EX.p1, EX.p2, EX.p3])
""" """
@ -657,46 +558,42 @@ defmodule RDF.Description do
@doc """ @doc """
Checks if the given statement exists within a `RDF.Description`. Checks if the given statement exists within a `RDF.Description`.
""" """
@spec include?(t, statements) :: boolean @spec include?(t, input) :: boolean
def include?(description, statement) def include?(description, input)
def include?( def include?(description, predications) when is_map(predications) or is_list(predications) do
%__MODULE__{predications: predications}, Enum.all?(predications, fn predication -> include?(description, predication) end)
{predicate, object} end
) do
with triple_predicate = coerce_predicate(predicate), def include?(%__MODULE__{} = description, {subject, predicate, objects}) do
triple_object = coerce_object(object) do coerce_subject(subject) == description.subject &&
predications include?(description, {predicate, objects})
|> Map.get(triple_predicate, %{}) end
|> Map.has_key?(triple_object)
def include?(%__MODULE__{} = description, {predicate, objects}) do
if existing_objects = description.predications[coerce_predicate(predicate)] do
objects
|> List.wrap()
|> Enum.map(&coerce_object/1)
|> Enum.all?(fn object -> Map.has_key?(existing_objects, object) end)
else
false
end end
end end
def include?(
desc = %__MODULE__{subject: desc_subject},
{subject, predicate, object}
) do
coerce_subject(subject) == desc_subject &&
include?(desc, {predicate, object})
end
def include?(%__MODULE__{}, _), do: false
@doc """ @doc """
Checks if a `RDF.Description` has the given resource as subject. Checks if a `RDF.Description` has the given resource as subject.
## Examples ## Examples
iex> RDF.Description.new(EX.S1, EX.p1, EX.O1) |> RDF.Description.describes?(EX.S1) iex> RDF.Description.new({EX.S1, EX.p1, EX.O1}) |> RDF.Description.describes?(EX.S1)
true true
iex> RDF.Description.new(EX.S1, EX.p1, EX.O1) |> RDF.Description.describes?(EX.S2) iex> RDF.Description.new({EX.S1, EX.p1, EX.O1}) |> RDF.Description.describes?(EX.S2)
false false
""" """
@spec describes?(t, Statement.subject()) :: boolean @spec describes?(t, Statement.subject()) :: boolean
def describes?(%__MODULE__{subject: subject}, other_subject) do def describes?(%__MODULE__{subject: subject}, other_subject) do
with other_subject = coerce_subject(other_subject) do subject == coerce_subject(other_subject)
subject == other_subject
end
end end
@doc """ @doc """

View file

@ -47,7 +47,7 @@ defmodule RDF.Diff do
## Examples ## Examples
iex> RDF.Diff.diff( iex> RDF.Diff.diff(
...> RDF.description(EX.S1, EX.p1, [EX.O1, EX.O2]), ...> RDF.description({EX.S1, EX.p1, [EX.O1, EX.O2]}),
...> RDF.graph([ ...> RDF.graph([
...> {EX.S1, EX.p1, [EX.O2, EX.O3]}, ...> {EX.S1, EX.p1, [EX.O2, EX.O3]},
...> {EX.S2, EX.p2, EX.O4} ...> {EX.S2, EX.p2, EX.O4}
@ -81,7 +81,7 @@ defmodule RDF.Diff do
nil -> nil ->
{ {
additions, additions,
Description.add(deletions, property, original_objects) Description.add(deletions, {property, original_objects})
} }
new_objects -> new_objects ->
@ -96,8 +96,8 @@ defmodule RDF.Diff do
end) end)
{ {
Description.delete(additions, property, unchanged_objects), Description.delete(additions, {property, unchanged_objects}),
Description.add(deletions, property, deleted_objects) Description.add(deletions, {property, deleted_objects})
} }
end end
end end

View file

@ -202,9 +202,14 @@ defmodule RDF.Graph do
%__MODULE__{ %__MODULE__{
graph graph
| descriptions: | descriptions:
Map.update(descriptions, subject, Description.new(statements), fn description -> Map.update(
Description.add(description, statements) descriptions,
end) subject,
Description.new(subject) |> Description.add(statements),
fn description ->
Description.add(description, statements)
end
)
} }
end end
@ -277,7 +282,7 @@ defmodule RDF.Graph do
Map.update( Map.update(
descriptions, descriptions,
subject, subject,
Description.new(subject, predications), Description.new(subject) |> Description.add(predications),
fn current -> fn current ->
Description.put(current, predications) Description.put(current, predications)
end end
@ -293,9 +298,14 @@ defmodule RDF.Graph do
%__MODULE__{ %__MODULE__{
graph graph
| descriptions: | descriptions:
Map.update(descriptions, subject, Description.new(statements), fn current -> Map.update(
Description.put(current, statements) descriptions,
end) subject,
Description.new(subject) |> Description.add(statements),
fn current ->
Description.put(current, statements)
end
)
} }
end end
@ -420,15 +430,15 @@ defmodule RDF.Graph do
iex> RDF.Graph.new({EX.S, EX.p, EX.O}) |> iex> RDF.Graph.new({EX.S, EX.p, EX.O}) |>
...> RDF.Graph.update(EX.S, ...> RDF.Graph.update(EX.S,
...> fn description -> Description.add(description, EX.p, EX.O2) end) ...> fn description -> Description.add(description, {EX.p, EX.O2}) end)
RDF.Graph.new([{EX.S, EX.p, EX.O}, {EX.S, EX.p, EX.O2}]) RDF.Graph.new([{EX.S, EX.p, EX.O}, {EX.S, EX.p, EX.O2}])
iex> RDF.Graph.new({EX.S, EX.p, EX.O}) |> iex> RDF.Graph.new({EX.S, EX.p, EX.O}) |>
...> RDF.Graph.update(EX.S, ...> RDF.Graph.update(EX.S,
...> fn _ -> Description.new(EX.S2, EX.p2, EX.O2) end) ...> fn _ -> Description.new({EX.S2, EX.p2, EX.O2}) end)
RDF.Graph.new([{EX.S, EX.p2, EX.O2}]) RDF.Graph.new([{EX.S, EX.p2, EX.O2}])
iex> RDF.Graph.new() |> iex> RDF.Graph.new() |>
...> RDF.Graph.update(EX.S, Description.new({EX.S, EX.p, EX.O}), ...> RDF.Graph.update(EX.S, Description.new({EX.S, EX.p, EX.O}),
...> fn description -> Description.add(description, EX.p, EX.O2) end) ...> fn description -> Description.add(description, {EX.p, EX.O2}) end)
RDF.Graph.new([{EX.S, EX.p, EX.O}]) RDF.Graph.new([{EX.S, EX.p, EX.O}])
""" """
@ -444,7 +454,7 @@ defmodule RDF.Graph do
case get(graph, subject) do case get(graph, subject) do
nil -> nil ->
if initial do if initial do
add(graph, Description.new(subject, initial)) add(graph, Description.new(subject) |> Description.add(initial))
else else
graph graph
end end
@ -459,7 +469,7 @@ defmodule RDF.Graph do
new_description -> new_description ->
graph graph
|> delete_subjects(subject) |> delete_subjects(subject)
|> add(Description.new(subject, new_description)) |> add(Description.new(subject) |> Description.add(new_description))
end end
end end
end end
@ -566,7 +576,7 @@ defmodule RDF.Graph do
...> RDF.Graph.get_and_update(EX.S, fn current_description -> ...> RDF.Graph.get_and_update(EX.S, fn current_description ->
...> {current_description, {EX.P, EX.NEW}} ...> {current_description, {EX.P, EX.NEW}}
...> end) ...> end)
{RDF.Description.new(EX.S, EX.P, EX.O), RDF.Graph.new(EX.S, EX.P, EX.NEW)} {RDF.Description.new({EX.S, EX.P, EX.O}), RDF.Graph.new({EX.S, EX.P, EX.NEW})}
""" """
@impl Access @impl Access

View file

@ -9,6 +9,7 @@ defmodule RDF.Vocabulary.Namespace do
the `RDF.NS` module. the `RDF.NS` module.
""" """
alias RDF.Description
alias RDF.Utils.ResourceClassifier alias RDF.Utils.ResourceClassifier
@vocabs_dir "priv/vocabs" @vocabs_dir "priv/vocabs"
@ -123,10 +124,20 @@ defmodule RDF.Vocabulary.Namespace do
end end
def unquote(:"$handle_undefined_function")(term, [subject | objects]) do def unquote(:"$handle_undefined_function")(term, [subject | objects]) do
objects =
case objects do
[objects] when is_list(objects) -> objects
_ -> objects
end
if MapSet.member?(@ignored_terms, term) do if MapSet.member?(@ignored_terms, term) do
raise UndefinedFunctionError raise UndefinedFunctionError
else else
RDF.Description.new(subject, term_to_iri(@base_iri, term), objects) case subject do
%Description{} -> subject
_ -> Description.new(subject)
end
|> Description.add({term_to_iri(@base_iri, term), objects})
end end
end end
end end
@ -153,8 +164,15 @@ defmodule RDF.Vocabulary.Namespace do
def unquote(term)(), do: unquote(Macro.escape(iri)) def unquote(term)(), do: unquote(Macro.escape(iri))
@doc "`RDF.Description` builder for `#{unquote(term)}/0`" @doc "`RDF.Description` builder for `#{unquote(term)}/0`"
def unquote(term)(subject, object)
def unquote(term)(%Description{} = subject, object) do
Description.add(subject, {unquote(Macro.escape(iri)), object})
end
def unquote(term)(subject, object) do def unquote(term)(subject, object) do
RDF.Description.new(subject, unquote(Macro.escape(iri)), object) Description.new(subject)
|> Description.add({unquote(Macro.escape(iri)), object})
end end
# Is there a better way to support multiple objects via arguments? # Is there a better way to support multiple objects via arguments?
@ -224,7 +242,7 @@ defmodule RDF.Vocabulary.Namespace do
end) end)
end end
defp raw_rdf_data(%RDF.Description{} = rdf_data), do: rdf_data defp raw_rdf_data(%Description{} = rdf_data), do: rdf_data
defp raw_rdf_data(%RDF.Graph{} = rdf_data), do: rdf_data defp raw_rdf_data(%RDF.Graph{} = rdf_data), do: rdf_data
defp raw_rdf_data(%RDF.Dataset{} = rdf_data), do: rdf_data defp raw_rdf_data(%RDF.Dataset{} = rdf_data), do: rdf_data

View file

@ -325,7 +325,8 @@ defmodule RDF.DatasetTest do
ds = ds =
Dataset.add( Dataset.add(
dataset(), dataset(),
Description.new(EX.Subject1, [ Description.new(EX.Subject1)
|> Description.add([
{EX.predicate1(), EX.Object1}, {EX.predicate1(), EX.Object1},
{EX.predicate2(), EX.Object2} {EX.predicate2(), EX.Object2}
]) ])
@ -339,7 +340,8 @@ defmodule RDF.DatasetTest do
ds = ds =
Dataset.add( Dataset.add(
dataset(), dataset(),
Description.new(EX.Subject1, [ Description.new(EX.Subject1)
|> RDF.Description.add([
{EX.predicate1(), EX.Object1}, {EX.predicate1(), EX.Object1},
{EX.predicate2(), EX.Object2} {EX.predicate2(), EX.Object2}
]), ]),
@ -707,7 +709,10 @@ defmodule RDF.DatasetTest do
test "a Description" do test "a Description" do
ds = ds =
Dataset.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}, {EX.S1, EX.P3, EX.O3}]) Dataset.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}, {EX.S1, EX.P3, EX.O3}])
|> RDF.Dataset.put(Description.new(EX.S1, [{EX.P3, EX.O4}, {EX.P2, bnode(:foo)}])) |> RDF.Dataset.put(
Description.new(EX.S1)
|> Description.add([{EX.P3, EX.O4}, {EX.P2, bnode(:foo)}])
)
assert Dataset.statement_count(ds) == 4 assert Dataset.statement_count(ds) == 4
assert dataset_includes_statement?(ds, {EX.S1, EX.P1, EX.O1}) assert dataset_includes_statement?(ds, {EX.S1, EX.P1, EX.O1})
@ -823,15 +828,15 @@ defmodule RDF.DatasetTest do
test "multiple statements with a Description", test "multiple statements with a Description",
%{dataset1: dataset1, dataset2: dataset2} do %{dataset1: dataset1, dataset2: dataset2} do
assert Dataset.delete(dataset1, Description.new(EX.S1, EX.p1(), EX.O1)) == Dataset.new() assert Dataset.delete(dataset1, Description.new({EX.S1, EX.p1(), EX.O1})) == Dataset.new()
assert Dataset.delete(dataset1, Description.new(EX.S1, EX.p1(), EX.O1), EX.Graph) == assert Dataset.delete(dataset1, Description.new({EX.S1, EX.p1(), EX.O1}), EX.Graph) ==
dataset1 dataset1
assert Dataset.delete(dataset2, Description.new(EX.S2, EX.p2(), EX.O2), EX.Graph) == assert Dataset.delete(dataset2, Description.new({EX.S2, EX.p2(), EX.O2}), EX.Graph) ==
dataset1 dataset1
assert Dataset.delete(dataset2, Description.new(EX.S1, EX.p1(), EX.O1)) == assert Dataset.delete(dataset2, Description.new({EX.S1, EX.p1(), EX.O1})) ==
Dataset.new({EX.S2, EX.p2(), EX.O2, EX.Graph}) Dataset.new({EX.S2, EX.p2(), EX.O2, EX.Graph})
end end

View file

@ -26,128 +26,61 @@ defmodule RDF.DescriptionTest do
assert description_of_subject(Description.new(bnode(:foo)), bnode(:foo)) assert description_of_subject(Description.new(bnode(:foo)), bnode(:foo))
end end
test "with a single initial triple" do test "with another description" do
desc = Description.new({EX.Subject, EX.predicate(), EX.Object}) existing_description = description({EX.Subject, EX.predicate(), EX.Object})
assert description_of_subject(desc, iri(EX.Subject)) new_description = Description.new(existing_description)
assert description_includes_predication(desc, {EX.predicate(), iri(EX.Object)}) assert description_of_subject(new_description, iri(EX.Subject))
refute description_includes_predication(new_description, {EX.predicate(), iri(EX.Object)})
desc = Description.new(EX.Subject, EX.predicate(), 42)
assert description_of_subject(desc, iri(EX.Subject))
assert description_includes_predication(desc, {EX.predicate(), literal(42)})
end
test "with a list of initial triples" do
desc =
Description.new([
{EX.Subject, EX.predicate1(), EX.Object1},
{EX.Subject, EX.predicate2(), EX.Object2}
])
assert description_of_subject(desc, iri(EX.Subject))
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
desc = Description.new(EX.Subject, EX.predicate(), [EX.Object, bnode(:foo), "bar"])
assert description_of_subject(desc, iri(EX.Subject))
assert description_includes_predication(desc, {EX.predicate(), iri(EX.Object)})
assert description_includes_predication(desc, {EX.predicate(), bnode(:foo)})
assert description_includes_predication(desc, {EX.predicate(), literal("bar")})
end
test "from another description" do
desc1 = Description.new({EX.Other, EX.predicate(), EX.Object})
desc2 = Description.new(EX.Subject, desc1)
assert description_of_subject(desc2, iri(EX.Subject))
assert description_includes_predication(desc2, {EX.predicate(), iri(EX.Object)})
end
test "from a map with coercible RDF term" do
desc = Description.new(EX.Subject, %{EX.Predicate => EX.Object})
assert description_of_subject(desc, iri(EX.Subject))
assert description_includes_predication(desc, {iri(EX.Predicate), iri(EX.Object)})
end
test "with another description as subject, it performs and add " do
desc = Description.new({EX.S, EX.p(), EX.O})
assert Description.new(desc, EX.p2(), EX.O2) ==
Description.add(desc, EX.p2(), EX.O2)
assert Description.new(desc, EX.p(), [EX.O1, EX.O2]) ==
Description.add(desc, EX.p(), [EX.O1, EX.O2])
end end
end end
describe "add" do describe "add/3" do
test "a predicate-object-pair of proper RDF terms" do test "with a triple" do
assert Description.add(description(), EX.predicate(), iri(EX.Object)) assert Description.add(description(), {iri(EX.Subject), EX.predicate(), iri(EX.Object)})
|> description_includes_predication({EX.predicate(), iri(EX.Object)}) |> description_includes_predication({EX.predicate(), iri(EX.Object)})
assert Description.add(description(), {iri(EX.Subject), EX.predicate(), bnode(:foo)})
|> description_includes_predication({EX.predicate(), bnode(:foo)})
assert Description.add(description(), {iri(EX.Subject), EX.predicate(), literal(42)})
|> description_includes_predication({EX.predicate(), literal(42)})
end
test "with a predicate-object tuple" do
assert Description.add(description(), {EX.predicate(), iri(EX.Object)}) assert Description.add(description(), {EX.predicate(), iri(EX.Object)})
|> description_includes_predication({EX.predicate(), iri(EX.Object)}) |> description_includes_predication({EX.predicate(), iri(EX.Object)})
end end
test "a predicate-object-pair of coercible RDF terms" do test "with a predicate-object tuple and a list of objects" do
assert Description.add(description(), "http://example.com/predicate", iri(EX.Object)) desc = Description.add(description(), {EX.p(), [iri(EX.O1), iri(EX.O2)]})
|> description_includes_predication({EX.predicate(), iri(EX.Object)}) assert description_includes_predication(desc, {EX.p(), iri(EX.O1)})
assert description_includes_predication(desc, {EX.p(), iri(EX.O2)})
assert Description.add(
description(),
{"http://example.com/predicate", 42}
)
|> description_includes_predication({EX.predicate(), literal(42)})
assert Description.add(
description(),
{"http://example.com/predicate", true}
)
|> description_includes_predication({EX.predicate(), literal(true)})
assert Description.add(
description(),
{"http://example.com/predicate", bnode(:foo)}
)
|> description_includes_predication({EX.predicate(), bnode(:foo)})
end end
test "a proper triple" do test "with a list of predicate-object tuples" do
assert Description.add(
description(),
{iri(EX.Subject), EX.predicate(), iri(EX.Object)}
)
|> description_includes_predication({EX.predicate(), iri(EX.Object)})
assert Description.add(
description(),
{iri(EX.Subject), EX.predicate(), literal(42)}
)
|> description_includes_predication({EX.predicate(), literal(42)})
assert Description.add(
description(),
{iri(EX.Subject), EX.predicate(), bnode(:foo)}
)
|> description_includes_predication({EX.predicate(), bnode(:foo)})
end
test "add ignores triples not about the subject of the Description struct" do
assert empty_description(
Description.add(description(), {EX.Other, EX.predicate(), iri(EX.Object)})
)
end
test "a list of predicate-object-pairs" do
desc = desc =
Description.add( Description.add(description(), [
description(), {EX.predicate(), EX.Object1},
[{EX.predicate(), EX.Object1}, {EX.predicate(), EX.Object2}] {EX.predicate(), EX.Object2}
) ])
assert description_includes_predication(desc, {EX.predicate(), iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate(), iri(EX.Object2)}) assert description_includes_predication(desc, {EX.predicate(), iri(EX.Object2)})
desc =
Description.add(description(), [
{EX.p1(), EX.O1},
{EX.p2(), [EX.O2, ~L"foo", "bar", 42]}
])
assert description_includes_predication(desc, {EX.p1(), iri(EX.O1)})
assert description_includes_predication(desc, {EX.p2(), iri(EX.O2)})
assert description_includes_predication(desc, {EX.p2(), ~L"foo"})
assert description_includes_predication(desc, {EX.p2(), ~L"bar"})
assert description_includes_predication(desc, {EX.p2(), RDF.literal(42)})
end end
test "a list of triples" do test "with a list of triples" do
desc = desc =
Description.add(description(), [ Description.add(description(), [
{EX.Subject, EX.predicate1(), EX.Object1}, {EX.Subject, EX.predicate1(), EX.Object1},
@ -156,6 +89,16 @@ defmodule RDF.DescriptionTest do
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)}) assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)}) assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
desc =
Description.add(description(), [
{EX.Subject, EX.predicate1(), EX.Object1},
{EX.Subject, EX.predicate2(), [EX.Object2, EX.Object3]}
])
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object3)})
end end
test "a list of mixed triples and predicate-object-pairs" do test "a list of mixed triples and predicate-object-pairs" do
@ -172,24 +115,7 @@ defmodule RDF.DescriptionTest do
refute description_includes_predication(desc, {EX.predicate(), iri(EX.Object3)}) refute description_includes_predication(desc, {EX.predicate(), iri(EX.Object3)})
end end
test "another description" do test "with a description map with coercible RDF terms" do
desc =
description([{EX.predicate1(), EX.Object1}, {EX.predicate2(), EX.Object2}])
|> Description.add(Description.new({EX.Other, EX.predicate3(), EX.Object3}))
assert description_of_subject(desc, iri(EX.Subject))
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.predicate3(), iri(EX.Object3)})
desc = Description.add(desc, Description.new({EX.Other, EX.predicate1(), EX.Object4}))
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.predicate3(), iri(EX.Object3)})
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object4)})
end
test "a map of predications with coercible RDF terms" do
desc = desc =
description([{EX.predicate1(), EX.Object1}, {EX.predicate2(), EX.Object2}]) description([{EX.predicate1(), EX.Object1}, {EX.predicate2(), EX.Object2}])
|> Description.add(%{EX.predicate3() => EX.Object3}) |> Description.add(%{EX.predicate3() => EX.Object3})
@ -224,6 +150,38 @@ defmodule RDF.DescriptionTest do
end end
end end
test "with an empty map" do
assert Description.add(description(), %{}) == description()
end
test "with empty object lists" do
assert Description.add(description(), {EX.p(), []}) == description()
assert Description.add(description(), %{EX.p() => []}) == description()
end
test "with another description" do
desc =
description([{EX.predicate1(), EX.Object1}, {EX.predicate2(), EX.Object2}])
|> Description.add(Description.new({EX.Other, EX.predicate3(), EX.Object3}))
assert description_of_subject(desc, iri(EX.Subject))
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.predicate3(), iri(EX.Object3)})
desc = Description.add(desc, Description.new({EX.Other, EX.predicate1(), EX.Object4}))
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.predicate3(), iri(EX.Object3)})
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object4)})
end
test "triples with another subject are ignored" do
assert empty_description(
Description.add(description(), {EX.Other, EX.predicate(), iri(EX.Object)})
)
end
test "duplicates are ignored" do test "duplicates are ignored" do
desc = Description.add(description(), {EX.predicate(), EX.Object}) desc = Description.add(description(), {EX.predicate(), EX.Object})
assert Description.add(desc, {EX.predicate(), EX.Object}) == desc assert Description.add(desc, {EX.predicate(), EX.Object}) == desc
@ -233,6 +191,18 @@ defmodule RDF.DescriptionTest do
assert Description.add(desc, {EX.predicate(), literal(42)}) == desc assert Description.add(desc, {EX.predicate(), literal(42)}) == desc
end end
test "coercion" do
assert Description.add(description(), {EX.Subject, EX.P, EX.O})
|> description_includes_predication({iri(EX.P), iri(EX.O)})
assert Description.add(description(), {"http://example.com/predicate", EX.Object})
|> description_includes_predication({EX.predicate(), iri(EX.Object)})
desc = Description.add(description(), {"http://example.com/predicate", [42, true]})
assert description_includes_predication(desc, {EX.predicate(), literal(42)})
assert description_includes_predication(desc, {EX.predicate(), literal(true)})
end
test "non-coercible Triple elements are causing an error" do test "non-coercible Triple elements are causing an error" do
assert_raise RDF.IRI.InvalidError, fn -> assert_raise RDF.IRI.InvalidError, fn ->
Description.add(description(), {"not a IRI", iri(EX.Object)}) Description.add(description(), {"not a IRI", iri(EX.Object)})
@ -244,33 +214,116 @@ defmodule RDF.DescriptionTest do
end end
end end
describe "put/3" do
test "with a triple" do
assert Description.put(description(), {iri(EX.Subject), EX.predicate(), iri(EX.Object)})
|> description_includes_predication({EX.predicate(), iri(EX.Object)})
end
test "with a predicate-object tuple" do
desc = Description.put(description(), {EX.p(), [iri(EX.O1), iri(EX.O2)]})
assert description_includes_predication(desc, {EX.p(), iri(EX.O1)})
assert description_includes_predication(desc, {EX.p(), iri(EX.O2)})
end
test "with a list of predicate-object tuples" do
desc =
Description.put(description(), [
{EX.p1(), EX.O1},
{EX.p2(), [EX.O2]},
{EX.p2(), [~L"foo", "bar", 42]}
])
assert description_includes_predication(desc, {EX.p1(), iri(EX.O1)})
assert description_includes_predication(desc, {EX.p2(), iri(EX.O2)})
assert description_includes_predication(desc, {EX.p2(), ~L"foo"})
assert description_includes_predication(desc, {EX.p2(), ~L"bar"})
assert description_includes_predication(desc, {EX.p2(), RDF.literal(42)})
end
test "with a list of triples" do
desc =
Description.put(description(), [
{EX.Subject, EX.predicate1(), EX.Object1},
{EX.Subject, EX.predicate2(), [EX.Object2, EX.Object3]},
{EX.Subject, EX.predicate2(), [EX.Object4]}
])
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object3)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object4)})
end
test "with a description map with coercible RDF terms" do
desc =
description([{EX.predicate1(), EX.Object1}, {EX.predicate2(), EX.Object2}])
|> Description.put(%{
EX.predicate2() => [EX.Object3, 42],
EX.predicate3() => bnode(:foo)
})
assert Description.count(desc) == 4
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object3)})
assert description_includes_predication(desc, {EX.predicate2(), literal(42)})
assert description_includes_predication(desc, {EX.predicate3(), bnode(:foo)})
refute description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
end
test "with an empty map" do
desc = description([{EX.predicate(), EX.Object}])
assert Description.put(desc, %{}) == desc
end
test "with a description on the same subject" do
desc =
description([{EX.predicate1(), EX.Object1}, {EX.predicate2(), EX.Object2}])
|> Description.put(
description([
{EX.predicate1(), EX.Object4},
{EX.predicate3(), EX.Object3}
])
)
assert Description.count(desc) == 3
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object4)})
assert description_includes_predication(desc, {EX.predicate2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.predicate3(), iri(EX.Object3)})
end
test "with a description on another subject" do
desc = description([{EX.predicate1(), EX.Object1}, {EX.predicate2(), EX.Object2}])
assert Description.put(
desc,
Description.new({EX.Other, EX.predicate(), iri(EX.Object)})
) == desc
end
test "triples with another subject are ignored" do
assert empty_description(
Description.put(description(), {EX.Other, EX.predicate(), iri(EX.Object)})
)
end
end
describe "delete" do describe "delete" do
setup do setup do
{:ok, {:ok,
empty_description: Description.new(EX.S), empty_description: Description.new(EX.S),
description1: Description.new(EX.S, EX.p(), EX.O), description1: Description.new({EX.S, EX.p(), EX.O}),
description2: Description.new(EX.S, EX.p(), [EX.O1, EX.O2]), description2: Description.new({EX.S, EX.p(), [EX.O1, EX.O2]}),
description3: description3:
Description.new(EX.S, [ Description.new(EX.S)
|> Description.add([
{EX.p1(), [EX.O1, EX.O2]}, {EX.p1(), [EX.O1, EX.O2]},
{EX.p2(), EX.O3}, {EX.p2(), EX.O3},
{EX.p3(), [~B<foo>, ~L"bar"]} {EX.p3(), [~B<foo>, ~L"bar"]}
])} ])}
end end
test "a single statement as a predicate object",
%{
empty_description: empty_description,
description1: description1,
description2: description2
} do
assert Description.delete(empty_description, EX.p(), EX.O) == empty_description
assert Description.delete(description1, EX.p(), EX.O) == empty_description
assert Description.delete(description2, EX.p(), EX.O1) ==
Description.new(EX.S, EX.p(), EX.O2)
end
test "a single statement as a predicate-object tuple", test "a single statement as a predicate-object tuple",
%{ %{
empty_description: empty_description, empty_description: empty_description,
@ -281,7 +334,7 @@ defmodule RDF.DescriptionTest do
assert Description.delete(description1, {EX.p(), EX.O}) == empty_description assert Description.delete(description1, {EX.p(), EX.O}) == empty_description
assert Description.delete(description2, {EX.p(), EX.O2}) == assert Description.delete(description2, {EX.p(), EX.O2}) ==
Description.new(EX.S, EX.p(), EX.O1) Description.new({EX.S, EX.p(), EX.O1})
end end
test "a single statement as a subject-predicate-object tuple and the proper description subject", test "a single statement as a subject-predicate-object tuple and the proper description subject",
@ -294,7 +347,7 @@ defmodule RDF.DescriptionTest do
assert Description.delete(description1, {EX.S, EX.p(), EX.O}) == empty_description assert Description.delete(description1, {EX.S, EX.p(), EX.O}) == empty_description
assert Description.delete(description2, {EX.S, EX.p(), EX.O2}) == assert Description.delete(description2, {EX.S, EX.p(), EX.O2}) ==
Description.new(EX.S, EX.p(), EX.O1) Description.new({EX.S, EX.p(), EX.O1})
end end
test "a single statement as a subject-predicate-object tuple and another description subject", test "a single statement as a subject-predicate-object tuple and another description subject",
@ -328,7 +381,7 @@ defmodule RDF.DescriptionTest do
{EX.p1(), EX.O1}, {EX.p1(), EX.O1},
{EX.p2(), [EX.O2, EX.O3]}, {EX.p2(), [EX.O2, EX.O3]},
{EX.S, EX.p3(), [~B<foo>, ~L"bar"]} {EX.S, EX.p3(), [~B<foo>, ~L"bar"]}
]) == Description.new(EX.S, EX.p1(), EX.O2) ]) == Description.new({EX.S, EX.p1(), EX.O2})
end end
test "multiple statements with a map of predications", test "multiple statements with a map of predications",
@ -339,7 +392,7 @@ defmodule RDF.DescriptionTest do
EX.p1() => EX.O1, EX.p1() => EX.O1,
EX.p2() => [EX.O2, EX.O3], EX.p2() => [EX.O2, EX.O3],
EX.p3() => [~B<foo>, ~L"bar"] EX.p3() => [~B<foo>, ~L"bar"]
}) == Description.new(EX.S, EX.p1(), EX.O2) }) == Description.new({EX.S, EX.p1(), EX.O2})
end end
test "multiple statements with another description", test "multiple statements with another description",
@ -352,12 +405,13 @@ defmodule RDF.DescriptionTest do
assert Description.delete( assert Description.delete(
description3, description3,
Description.new(EX.S, %{ Description.new(EX.S)
|> Description.add(%{
EX.p1() => EX.O1, EX.p1() => EX.O1,
EX.p2() => [EX.O2, EX.O3], EX.p2() => [EX.O2, EX.O3],
EX.p3() => [~B<foo>, ~L"bar"] EX.p3() => [~B<foo>, ~L"bar"]
}) })
) == Description.new(EX.S, EX.p1(), EX.O2) ) == Description.new({EX.S, EX.p1(), EX.O2})
end end
end end
@ -365,9 +419,10 @@ defmodule RDF.DescriptionTest do
setup do setup do
{:ok, {:ok,
empty_description: Description.new(EX.S), empty_description: Description.new(EX.S),
description1: Description.new(EX.S, EX.p(), [EX.O1, EX.O2]), description1: Description.new({EX.S, EX.p(), [EX.O1, EX.O2]}),
description2: description2:
Description.new(EX.S, [ Description.new(EX.S)
|> Description.add([
{EX.P1, [EX.O1, EX.O2]}, {EX.P1, [EX.O1, EX.O2]},
{EX.p2(), [~B<foo>, ~L"bar"]} {EX.p2(), [~B<foo>, ~L"bar"]}
])} ])}
@ -382,7 +437,7 @@ defmodule RDF.DescriptionTest do
assert Description.delete_predicates(description1, EX.p()) == empty_description assert Description.delete_predicates(description1, EX.p()) == empty_description
assert Description.delete_predicates(description2, EX.P1) == assert Description.delete_predicates(description2, EX.P1) ==
Description.new(EX.S, EX.p2(), [~B<foo>, ~L"bar"]) Description.new({EX.S, EX.p2(), [~B<foo>, ~L"bar"]})
end end
test "a list of properties", test "a list of properties",
@ -400,18 +455,18 @@ defmodule RDF.DescriptionTest do
describe "update/4" do describe "update/4" do
test "list values returned from the update function become new coerced objects of the predicate" do test "list values returned from the update function become new coerced objects of the predicate" do
assert Description.new(EX.S, EX.P, [EX.O1, EX.O2]) assert Description.new({EX.S, EX.P, [EX.O1, EX.O2]})
|> Description.update( |> Description.update(
EX.P, EX.P,
fn [_object | other] -> [EX.O3 | other] end fn [_object | other] -> [EX.O3 | other] end
) == ) ==
Description.new(EX.S, EX.P, [EX.O3, EX.O2]) Description.new({EX.S, EX.P, [EX.O3, EX.O2]})
end end
test "single values returned from the update function becomes new object of the predicate" do test "single values returned from the update function becomes new object of the predicate" do
assert Description.new(EX.S, EX.P, [EX.O1, EX.O2]) assert Description.new({EX.S, EX.P, [EX.O1, EX.O2]})
|> Description.update(EX.P, fn _ -> EX.O3 end) == |> Description.update(EX.P, fn _ -> EX.O3 end) ==
Description.new(EX.S, EX.P, EX.O3) Description.new({EX.S, EX.P, EX.O3})
end end
test "returning an empty list or nil from the update function causes a removal of the predications" do test "returning an empty list or nil from the update function causes a removal of the predications" do
@ -421,11 +476,11 @@ defmodule RDF.DescriptionTest do
assert description assert description
|> Description.update(EX.p(), fn _ -> [] end) == |> Description.update(EX.p(), fn _ -> [] end) ==
Description.new(EX.S, {EX.p(), []}) Description.new(EX.S)
assert description assert description
|> Description.update(EX.p(), fn _ -> nil end) == |> Description.update(EX.p(), fn _ -> nil end) ==
Description.new(EX.S, {EX.p(), []}) Description.new(EX.S)
end end
test "when the property is not present the initial object value is added for the predicate and the update function not called" do test "when the property is not present the initial object value is added for the predicate and the update function not called" do
@ -433,7 +488,7 @@ defmodule RDF.DescriptionTest do
assert Description.new(EX.S) assert Description.new(EX.S)
|> Description.update(EX.P, EX.O, fun) == |> Description.update(EX.P, EX.O, fun) ==
Description.new(EX.S, EX.P, EX.O) Description.new({EX.S, EX.P, EX.O})
assert Description.new(EX.S) assert Description.new(EX.S)
|> Description.update(EX.P, fun) == |> Description.update(EX.P, fun) ==
@ -449,20 +504,47 @@ defmodule RDF.DescriptionTest do
assert Enum.count(desc.predications) == 0 assert Enum.count(desc.predications) == 0
{{subject, predicate, _}, desc} = {{subject, predicate, _}, desc} =
Description.new([{EX.S, EX.p(), EX.O1}, {EX.S, EX.p(), EX.O2}]) Description.new(EX.S)
|> Description.add([{EX.S, EX.p(), EX.O1}, {EX.S, EX.p(), EX.O2}])
|> Description.pop() |> Description.pop()
assert {subject, predicate} == {iri(EX.S), iri(EX.p())} assert {subject, predicate} == {iri(EX.S), iri(EX.p())}
assert Enum.count(desc.predications) == 1 assert Enum.count(desc.predications) == 1
{{subject, _, _}, desc} = {{subject, _, _}, desc} =
Description.new([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}]) Description.new(EX.S)
|> Description.add([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}])
|> Description.pop() |> Description.pop()
assert subject == iri(EX.S) assert subject == iri(EX.S)
assert Enum.count(desc.predications) == 1 assert Enum.count(desc.predications) == 1
end end
test "include?/2" do
desc =
Description.new(EX.S)
|> Description.add([
{EX.p1(), [EX.O1, EX.O2]},
{EX.p2(), EX.O3},
{EX.p3(), [~B<foo>, ~L"bar"]}
])
assert Description.include?(desc, {EX.p1(), EX.O2})
assert Description.include?(desc, {EX.p1(), [EX.O1, EX.O2]})
assert Description.include?(desc, [{EX.p1(), [EX.O1]}, {EX.p2(), EX.O3}])
assert Description.include?(desc, %{EX.p1() => [EX.O1, EX.O2], EX.p2() => EX.O3})
assert Description.include?(
desc,
Description.new(EX.S)
|> Description.add(%{EX.p1() => [EX.O1, EX.O2], EX.p2() => EX.O3})
)
refute Description.include?(desc, {EX.p4(), EX.O1})
refute Description.include?(desc, {EX.p1(), EX.O3})
refute Description.include?(desc, {EX.p1(), [EX.O1, EX.O3]})
end
test "values/1" do test "values/1" do
assert Description.new(EX.s()) |> Description.values() == %{} assert Description.new(EX.s()) |> Description.values() == %{}
@ -487,20 +569,24 @@ defmodule RDF.DescriptionTest do
describe "take/2" do describe "take/2" do
test "with a non-empty property list" do test "with a non-empty property list" do
assert Description.new([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}]) assert Description.new(EX.S)
|> Description.add([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}])
|> Description.take([EX.p2(), EX.p3()]) == |> Description.take([EX.p2(), EX.p3()]) ==
Description.new({EX.S, EX.p2(), EX.O2}) Description.new({EX.S, EX.p2(), EX.O2})
end end
test "with an empty property list" do test "with an empty property list" do
assert Description.new([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}]) assert Description.new(EX.S)
|> Description.add([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}])
|> Description.take([]) == Description.new(EX.S) |> Description.take([]) == Description.new(EX.S)
end end
test "with nil" do test "with nil" do
assert Description.new([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}]) assert Description.new(EX.S)
|> Description.add([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}])
|> Description.take(nil) == |> Description.take(nil) ==
Description.new([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}]) Description.new(EX.S)
|> Description.add([{EX.S, EX.p1(), EX.O1}, {EX.S, EX.p2(), EX.O2}])
end end
end end
@ -516,7 +602,11 @@ defmodule RDF.DescriptionTest do
test "Enum.count" do test "Enum.count" do
assert Enum.count(Description.new(EX.foo())) == 0 assert Enum.count(Description.new(EX.foo())) == 0
assert Enum.count(Description.new({EX.S, EX.p(), EX.O})) == 1 assert Enum.count(Description.new({EX.S, EX.p(), EX.O})) == 1
assert Enum.count(Description.new([{EX.S, EX.p(), EX.O1}, {EX.S, EX.p(), EX.O2}])) == 2
assert Enum.count(
Description.new(EX.S)
|> Description.add([{EX.S, EX.p(), EX.O1}, {EX.S, EX.p(), EX.O2}])
) == 2
end end
test "Enum.member?" do test "Enum.member?" do
@ -524,7 +614,8 @@ defmodule RDF.DescriptionTest do
assert Enum.member?(Description.new({EX.S, EX.p(), EX.O}), {EX.S, EX.p(), EX.O}) assert Enum.member?(Description.new({EX.S, EX.p(), EX.O}), {EX.S, EX.p(), EX.O})
desc = desc =
Description.new([ Description.new(EX.Subject)
|> Description.add([
{EX.Subject, EX.predicate1(), EX.Object1}, {EX.Subject, EX.predicate1(), EX.Object1},
{EX.Subject, EX.predicate2(), EX.Object2}, {EX.Subject, EX.predicate2(), EX.Object2},
{EX.predicate2(), EX.Object3} {EX.predicate2(), EX.Object3}
@ -538,7 +629,8 @@ defmodule RDF.DescriptionTest do
test "Enum.reduce" do test "Enum.reduce" do
desc = desc =
Description.new([ Description.new(EX.Subject)
|> Description.add([
{EX.Subject, EX.predicate1(), EX.Object1}, {EX.Subject, EX.predicate1(), EX.Object1},
{EX.Subject, EX.predicate2(), EX.Object2}, {EX.Subject, EX.predicate2(), EX.Object2},
{EX.predicate2(), EX.Object3} {EX.predicate2(), EX.Object3}
@ -558,7 +650,8 @@ defmodule RDF.DescriptionTest do
EX.predicate2() => EX.Object2 EX.predicate2() => EX.Object2
} }
assert Enum.into(map, Description.new(EX.Subject)) == Description.new(EX.Subject, map) assert Enum.into(map, Description.new(EX.Subject)) ==
Description.new(EX.Subject) |> Description.add(map)
end end
test "with a list of triples" do test "with a list of triples" do
@ -567,7 +660,9 @@ defmodule RDF.DescriptionTest do
{EX.Subject, EX.predicate2(), EX.Object2} {EX.Subject, EX.predicate2(), EX.Object2}
] ]
assert Enum.into(triples, Description.new(EX.Subject)) == Description.new(triples) assert Enum.into(triples, Description.new(EX.Subject)) ==
Description.new(EX.Subject)
|> Description.add(triples)
end end
test "with a list of predicate-object pairs" do test "with a list of predicate-object pairs" do
@ -576,7 +671,9 @@ defmodule RDF.DescriptionTest do
{EX.predicate2(), EX.Object2} {EX.predicate2(), EX.Object2}
] ]
assert Enum.into(pairs, Description.new(EX.Subject)) == Description.new(EX.Subject, pairs) assert Enum.into(pairs, Description.new(EX.Subject)) ==
Description.new(EX.Subject)
|> Description.add(pairs)
end end
test "with a list of lists" do test "with a list of lists" do
@ -586,7 +683,8 @@ defmodule RDF.DescriptionTest do
] ]
assert Enum.into(lists, Description.new(EX.Subject)) == assert Enum.into(lists, Description.new(EX.Subject)) ==
Description.new(Enum.map(lists, &List.to_tuple/1)) Description.new(EX.Subject)
|> Description.add(Enum.map(lists, &List.to_tuple/1))
end end
end end
@ -594,23 +692,24 @@ defmodule RDF.DescriptionTest do
test "access with the [] operator" do test "access with the [] operator" do
assert Description.new(EX.Subject)[EX.predicate()] == nil assert Description.new(EX.Subject)[EX.predicate()] == nil
assert Description.new(EX.Subject, EX.predicate(), EX.Object)[EX.predicate()] == [ assert Description.new({EX.Subject, EX.predicate(), EX.Object})[EX.predicate()] == [
iri(EX.Object) iri(EX.Object)
] ]
assert Description.new(EX.Subject, EX.Predicate, EX.Object)[EX.Predicate] == [ assert Description.new({EX.Subject, EX.Predicate, EX.Object})[EX.Predicate] == [
iri(EX.Object) iri(EX.Object)
] ]
assert Description.new(EX.Subject, EX.predicate(), EX.Object)[ assert Description.new({EX.Subject, EX.predicate(), EX.Object})[
"http://example.com/predicate" "http://example.com/predicate"
] == [iri(EX.Object)] ] == [iri(EX.Object)]
assert Description.new([ assert (Description.new(EX.Subject)
{EX.Subject, EX.predicate1(), EX.Object1}, |> Description.add([
{EX.Subject, EX.predicate1(), EX.Object2}, {EX.Subject, EX.predicate1(), EX.Object1},
{EX.Subject, EX.predicate2(), EX.Object3} {EX.Subject, EX.predicate1(), EX.Object2},
])[EX.predicate1()] == {EX.Subject, EX.predicate2(), EX.Object3}
]))[EX.predicate1()] ==
[iri(EX.Object1), iri(EX.Object2)] [iri(EX.Object1), iri(EX.Object2)]
end end
end end

View file

@ -216,7 +216,8 @@ defmodule RDF.GraphTest do
g = g =
Graph.add( Graph.add(
graph(), graph(),
Description.new(EX.Subject1, [ Description.new(EX.Subject1)
|> Description.add([
{EX.predicate1(), EX.Object1}, {EX.predicate1(), EX.Object1},
{EX.predicate2(), EX.Object2} {EX.predicate2(), EX.Object2}
]) ])
@ -346,7 +347,10 @@ defmodule RDF.GraphTest do
test "a Description" do test "a Description" do
g = g =
Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}, {EX.S1, EX.P3, EX.O3}]) Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}, {EX.S1, EX.P3, EX.O3}])
|> RDF.Graph.put(Description.new(EX.S1, [{EX.P3, EX.O4}, {EX.P2, bnode(:foo)}])) |> RDF.Graph.put(
Description.new(EX.S1)
|> Description.add([{EX.P3, EX.O4}, {EX.P2, bnode(:foo)}])
)
assert Graph.triple_count(g) == 4 assert Graph.triple_count(g) == 4
assert graph_includes_statement?(g, {EX.S1, EX.P1, EX.O1}) assert graph_includes_statement?(g, {EX.S1, EX.P1, EX.O1})
@ -454,16 +458,14 @@ defmodule RDF.GraphTest do
%{graph1: graph1, graph2: graph2, graph3: graph3} do %{graph1: graph1, graph2: graph2, graph3: graph3} do
assert Graph.delete( assert Graph.delete(
graph1, graph1,
Description.new( Description.new(EX.S)
EX.S, |> Description.add([{EX.p(), EX.O}, {EX.p2(), EX.O2}])
[{EX.p(), EX.O}, {EX.p2(), EX.O2}]
)
) == Graph.new() ) == Graph.new()
assert Graph.delete(graph2, Description.new(EX.S, EX.p(), [EX.O1, EX.O2])) == assert Graph.delete(graph2, Description.new({EX.S, EX.p(), [EX.O1, EX.O2]})) ==
Graph.new(name: EX.Graph) Graph.new(name: EX.Graph)
assert Graph.delete(graph3, Description.new(EX.S3, EX.p3(), ~B<foo>)) == assert Graph.delete(graph3, Description.new({EX.S3, EX.p3(), ~B<foo>})) ==
Graph.new([ Graph.new([
{EX.S1, EX.p1(), [EX.O1, EX.O2]}, {EX.S1, EX.p1(), [EX.O1, EX.O2]},
{EX.S2, EX.p2(), EX.O3}, {EX.S2, EX.p2(), EX.O3},
@ -553,7 +555,9 @@ defmodule RDF.GraphTest do
]) ])
|> Graph.update( |> Graph.update(
EX.S2, EX.S2,
fn ^old_description -> Description.new(EX.S3, new_description) end fn ^old_description ->
Description.new(EX.S3) |> Description.add(new_description)
end
) == ) ==
Graph.new([ Graph.new([
{EX.S1, EX.p1(), [EX.O1, EX.O2]}, {EX.S1, EX.p1(), [EX.O1, EX.O2]},
@ -575,7 +579,8 @@ defmodule RDF.GraphTest do
) == ) ==
Graph.new([ Graph.new([
{EX.S1, EX.p1(), [EX.O1, EX.O2]}, {EX.S1, EX.p1(), [EX.O1, EX.O2]},
Description.new(EX.S2, new_description) Description.new(EX.S2)
|> Description.add(new_description)
]) ])
end end

View file

@ -282,21 +282,21 @@ defmodule RDF.Vocabulary.NamespaceTest do
assert Example.__ENV__() == ~I<http://example.com/ex#__ENV__> assert Example.__ENV__() == ~I<http://example.com/ex#__ENV__>
assert Example.__CALLER__() == ~I<http://example.com/ex#__CALLER__> assert Example.__CALLER__() == ~I<http://example.com/ex#__CALLER__>
assert Example.nil(EX.S, 1) == RDF.description(EX.S, Example.nil(), 1) assert Example.nil(EX.S, 1) == RDF.description({EX.S, Example.nil(), 1})
assert Example.true(EX.S, 1) == RDF.description(EX.S, Example.true(), 1) assert Example.true(EX.S, 1) == RDF.description({EX.S, Example.true(), 1})
assert Example.false(EX.S, 1) == RDF.description(EX.S, Example.false(), 1) assert Example.false(EX.S, 1) == RDF.description({EX.S, Example.false(), 1})
assert Example.do(EX.S, 1) == RDF.description(EX.S, Example.do(), 1) assert Example.do(EX.S, 1) == RDF.description({EX.S, Example.do(), 1})
assert Example.end(EX.S, 1) == RDF.description(EX.S, Example.end(), 1) assert Example.end(EX.S, 1) == RDF.description({EX.S, Example.end(), 1})
assert Example.else(EX.S, 1) == RDF.description(EX.S, Example.else(), 1) assert Example.else(EX.S, 1) == RDF.description({EX.S, Example.else(), 1})
assert Example.try(EX.S, 1) == RDF.description(EX.S, Example.try(), 1) assert Example.try(EX.S, 1) == RDF.description({EX.S, Example.try(), 1})
assert Example.rescue(EX.S, 1) == RDF.description(EX.S, Example.rescue(), 1) assert Example.rescue(EX.S, 1) == RDF.description({EX.S, Example.rescue(), 1})
assert Example.catch(EX.S, 1) == RDF.description(EX.S, Example.catch(), 1) assert Example.catch(EX.S, 1) == RDF.description({EX.S, Example.catch(), 1})
assert Example.after(EX.S, 1) == RDF.description(EX.S, Example.after(), 1) assert Example.after(EX.S, 1) == RDF.description({EX.S, Example.after(), 1})
assert Example.not(EX.S, 1) == RDF.description(EX.S, Example.not(), 1) assert Example.not(EX.S, 1) == RDF.description({EX.S, Example.not(), 1})
assert Example.cond(EX.S, 1) == RDF.description(EX.S, Example.cond(), 1) assert Example.cond(EX.S, 1) == RDF.description({EX.S, Example.cond(), 1})
assert Example.inbits(EX.S, 1) == RDF.description(EX.S, Example.inbits(), 1) assert Example.inbits(EX.S, 1) == RDF.description({EX.S, Example.inbits(), 1})
assert Example.inlist(EX.S, 1) == RDF.description(EX.S, Example.inlist(), 1) assert Example.inlist(EX.S, 1) == RDF.description({EX.S, Example.inlist(), 1})
assert Example.receive(EX.S, 1) == RDF.description(EX.S, Example.receive(), 1) assert Example.receive(EX.S, 1) == RDF.description({EX.S, Example.receive(), 1})
end end
describe "defvocab with invalid terms" do describe "defvocab with invalid terms" do
@ -885,7 +885,7 @@ defmodule RDF.Vocabulary.NamespaceTest do
alias TestNS.EX alias TestNS.EX
assert Example._foo() == ~I<http://example.com/ex#_foo> assert Example._foo() == ~I<http://example.com/ex#_foo>
assert Example._foo(EX.S, 1) == RDF.description(EX.S, Example._foo(), 1) assert Example._foo(EX.S, 1) == RDF.description({EX.S, Example._foo(), 1})
end end
end end
@ -966,7 +966,7 @@ defmodule RDF.Vocabulary.NamespaceTest do
alias TestNS.{EX, EXS} alias TestNS.{EX, EXS}
test "one statement with a strict property term" do test "one statement with a strict property term" do
assert EXS.foo(EX.S, EX.O) == Description.new(EX.S, EXS.foo(), EX.O) assert EXS.foo(EX.S, EX.O) == Description.new({EX.S, EXS.foo(), EX.O})
end end
test "multiple statements with strict property terms and one object" do test "multiple statements with strict property terms and one object" do
@ -975,7 +975,8 @@ defmodule RDF.Vocabulary.NamespaceTest do
|> EXS.foo(EX.O1) |> EXS.foo(EX.O1)
|> EXS.bar(EX.O2) |> EXS.bar(EX.O2)
assert description == Description.new(EX.S, [{EXS.foo(), EX.O1}, {EXS.bar(), EX.O2}]) assert description ==
Description.new(EX.S) |> Description.add([{EXS.foo(), EX.O1}, {EXS.bar(), EX.O2}])
end end
test "multiple statements with strict property terms and multiple objects in a list" do test "multiple statements with strict property terms and multiple objects in a list" do
@ -985,7 +986,8 @@ defmodule RDF.Vocabulary.NamespaceTest do
|> EXS.bar([EX.O3, EX.O4]) |> EXS.bar([EX.O3, EX.O4])
assert description == assert description ==
Description.new(EX.S, [ Description.new(EX.S)
|> Description.add([
{EXS.foo(), EX.O1}, {EXS.foo(), EX.O1},
{EXS.foo(), EX.O2}, {EXS.foo(), EX.O2},
{EXS.bar(), EX.O3}, {EXS.bar(), EX.O3},
@ -1000,7 +1002,8 @@ defmodule RDF.Vocabulary.NamespaceTest do
|> EXS.bar(EX.O3, EX.O4, EX.O5) |> EXS.bar(EX.O3, EX.O4, EX.O5)
assert description == assert description ==
Description.new(EX.S, [ Description.new(EX.S)
|> Description.add([
{EXS.foo(), EX.O1}, {EXS.foo(), EX.O1},
{EXS.foo(), EX.O2}, {EXS.foo(), EX.O2},
{EXS.bar(), EX.O3}, {EXS.bar(), EX.O3},
@ -1010,7 +1013,7 @@ defmodule RDF.Vocabulary.NamespaceTest do
end end
test "one statement with a non-strict property term" do test "one statement with a non-strict property term" do
assert EX.p(EX.S, EX.O) == Description.new(EX.S, EX.p(), EX.O) assert EX.p(EX.S, EX.O) == Description.new({EX.S, EX.p(), EX.O})
end end
test "multiple statements with non-strict property terms and one object" do test "multiple statements with non-strict property terms and one object" do
@ -1019,7 +1022,8 @@ defmodule RDF.Vocabulary.NamespaceTest do
|> EX.p1(EX.O1) |> EX.p1(EX.O1)
|> EX.p2(EX.O2) |> EX.p2(EX.O2)
assert description == Description.new(EX.S, [{EX.p1(), EX.O1}, {EX.p2(), EX.O2}]) assert description ==
Description.new(EX.S) |> Description.add([{EX.p1(), EX.O1}, {EX.p2(), EX.O2}])
end end
test "multiple statements with non-strict property terms and multiple objects in a list" do test "multiple statements with non-strict property terms and multiple objects in a list" do
@ -1029,7 +1033,8 @@ defmodule RDF.Vocabulary.NamespaceTest do
|> EX.p2([EX.O3, EX.O4]) |> EX.p2([EX.O3, EX.O4])
assert description == assert description ==
Description.new(EX.S, [ Description.new(EX.S)
|> Description.add([
{EX.p1(), EX.O1}, {EX.p1(), EX.O1},
{EX.p1(), EX.O2}, {EX.p1(), EX.O2},
{EX.p2(), EX.O3}, {EX.p2(), EX.O3},
@ -1044,7 +1049,8 @@ defmodule RDF.Vocabulary.NamespaceTest do
|> EX.p2(EX.O3, EX.O4) |> EX.p2(EX.O3, EX.O4)
assert description == assert description ==
Description.new(EX.S, [ Description.new(EX.S)
|> Description.add([
{EX.p1(), EX.O1}, {EX.p1(), EX.O1},
{EX.p1(), EX.O2}, {EX.p1(), EX.O2},
{EX.p2(), EX.O3}, {EX.p2(), EX.O3},