Add RDF.Description.update/4

This commit is contained in:
Marcel Otto 2019-10-23 17:31:21 +02:00
parent 2cfa89125f
commit 623577b35e
3 changed files with 80 additions and 6 deletions

View file

@ -9,6 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
### Added ### Added
- `RDF.Description.update/4` updates the objects of a predicate in a description
with a custom update function
- `RDF.Description.take/2` creates a description from another one by limiting - `RDF.Description.take/2` creates a description from another one by limiting
its statements to a set of predicates its statements to a set of predicates
- `RDF.Graph.take/3` creates a graph from another one by limiting - `RDF.Graph.take/3` creates a graph from another one by limiting

View file

@ -367,13 +367,49 @@ defmodule RDF.Description do
|> List.first |> List.first
end end
@doc """
Updates the objects of the `predicate` in `description` with the given function.
# def update(description = %RDF.Description{}, predicate, initial \\ [], fun) do If `predicate` is present in `description` with `objects` as value,
# triple_predicate = coerce_predicate(predicate) `fun` is invoked with argument `objects` and its result is used as the new
# description.predicates list of objects of `predicate`. If `predicate` is not present in `description`,
# |> Map.update(triple_predicate, initial, fn objects -> `initial` is inserted as the `objects` of `predicate`. The initial value will
# end) not be passed through the update function.
# end
The initial value and the returned objects by the update function will automatically
coerced to proper RDF object values before added.
## Examples
iex> RDF.Description.new({EX.S, EX.p, EX.O}) |>
...> 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}])
iex> RDF.Description.new(EX.S) |>
...> RDF.Description.update(EX.p, EX.O, fn _ -> EX.O2 end)
RDF.Description.new({EX.S, EX.p, EX.O})
"""
def update(description = %RDF.Description{}, predicate, initial \\ nil, fun) do
predicate = coerce_predicate(predicate)
case get(description, predicate) do
nil ->
if initial do
put(description, predicate, initial)
else
description
end
objects ->
objects
|> fun.()
|> List.wrap()
|> case do
[] -> delete_predicates(description, predicate)
objects -> put(description, predicate, objects)
end
end
end
@doc """ @doc """

View file

@ -317,6 +317,42 @@ defmodule RDF.DescriptionTest do
end end
end end
describe "update/4" 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])
|> Description.update(EX.P,
fn [_object | other] -> [EX.O3 | other] end) ==
Description.new(EX.S, EX.P, [EX.O3, EX.O2])
end
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])
|> Description.update(EX.P, fn _ -> EX.O3 end) ==
Description.new(EX.S, EX.P, EX.O3)
end
test "returning an empty list or nil from the update function causes a removal of the predications" do
description = EX.S
|> EX.p(EX.O1, EX.O2)
assert description
|> Description.update(EX.p, fn _ -> [] end) ==
Description.new(EX.S, {EX.p, []})
assert description
|> Description.update(EX.p, fn _ -> nil end) ==
Description.new(EX.S, {EX.p, []})
end
test "when the property is not present the initial object value is added for the predicate and the update function not called" do
fun = fn _ -> raise "should not be called" end
assert Description.new(EX.S)
|> Description.update(EX.P, EX.O, fun) ==
Description.new(EX.S, EX.P, EX.O)
assert Description.new(EX.S)
|> Description.update(EX.P, fun) ==
Description.new(EX.S)
end
end
test "pop" do test "pop" do
assert Description.pop(Description.new(EX.S)) == {nil, Description.new(EX.S)} assert Description.pop(Description.new(EX.S)) == {nil, Description.new(EX.S)}