core: Access behaviour for Graph

This commit is contained in:
Marcel Otto 2016-11-27 13:49:42 +01:00
parent ebd751827f
commit 6a9daa30e6
2 changed files with 108 additions and 1 deletions

View file

@ -8,6 +8,8 @@ defmodule RDF.Graph do
"""
defstruct name: nil, descriptions: %{}
@behaviour Access
alias RDF.{Description, Triple}
@type t :: module
@ -155,6 +157,103 @@ defmodule RDF.Graph do
end
end
def put(graph, subject, predications = {_predicate, _objects}),
do: put(graph, subject, [predications])
@doc """
Fetches the description of the given subject.
When the subject can not be found `:error` is returned.
# Examples
iex> RDF.Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}]) |>
...> RDF.Graph.fetch(EX.S1)
{:ok, RDF.Description.new({EX.S1, EX.P1, EX.O1})}
iex> RDF.Graph.fetch(RDF.Graph.new, EX.foo)
:error
"""
def fetch(%RDF.Graph{descriptions: descriptions}, subject) do
Access.fetch(descriptions, Triple.convert_subject(subject))
end
@doc """
Gets the description of the given subject.
When the subject can not be found the optionally given default value or `nil` is returned.
# Examples
iex> RDF.Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}]) |>
...> RDF.Graph.get(EX.S1)
RDF.Description.new({EX.S1, EX.P1, EX.O1})
iex> RDF.Graph.get(RDF.Graph.new, EX.Foo)
nil
iex> RDF.Graph.get(RDF.Graph.new, EX.Foo, :bar)
:bar
"""
def get(graph = %RDF.Graph{}, subject, default \\ nil) do
case fetch(graph, subject) do
{:ok, value} -> value
:error -> default
end
end
@doc """
Gets and updates the description of the given subject, in a single pass.
Invokes the passed function on the `RDF.Description` of the given subject;
this function should return either `{description_to_return, new_description}` or `:pop`.
If the passed function returns `{description_to_return, new_description}`, the
return value of `get_and_update` is `{description_to_return, new_graph}` where
`new_graph` is the input `Graph` updated with `new_description` for
the given subject.
If the passed function returns `:pop` the description for the given subject is
removed and a `{removed_description, new_graph}` tuple gets returned.
# Examples
iex> RDF.Graph.new({EX.S, EX.P, EX.O}) |>
...> RDF.Graph.get_and_update(EX.S, fn current_description ->
...> {current_description, {EX.P, EX.NEW}}
...> end)
{RDF.Description.new(EX.S, EX.P, EX.O), RDF.Graph.new(EX.S, EX.P, EX.NEW)}
"""
def get_and_update(graph = %RDF.Graph{}, subject, fun) do
with triple_subject = Triple.convert_subject(subject) do
case fun.(get(graph, triple_subject)) do
{old_description, new_description} ->
{old_description, put(graph, triple_subject, new_description)}
:pop -> pop(graph, triple_subject)
end
end
end
@doc """
Pops the description of the given subject.
When the subject can not be found the optionally given default value or `nil` is returned.
# Examples
iex> RDF.Graph.new([{EX.S1, EX.P1, EX.O1}, {EX.S2, EX.P2, EX.O2}]) |>
...> RDF.Graph.pop(EX.S1)
{RDF.Description.new({EX.S1, EX.P1, EX.O1}), RDF.Graph.new({EX.S2, EX.P2, EX.O2})}
iex> RDF.Graph.pop(RDF.Graph.new({EX.S, EX.P, EX.O}), EX.Missing)
{nil, RDF.Graph.new({EX.S, EX.P, EX.O})}
"""
def pop(graph = %RDF.Graph{name: name, descriptions: descriptions}, subject) do
case Access.pop(descriptions, Triple.convert_subject(subject)) do
{nil, _} ->
{nil, graph}
{description, new_descriptions} ->
{description, %RDF.Graph{name: name, descriptions: new_descriptions}}
end
end
def subject_count(graph), do: Enum.count(graph.descriptions)

View file

@ -7,7 +7,7 @@ defmodule RDF.GraphTest do
doctest RDF.Graph
alias RDF.{Graph, Description}
import RDF, only: [uri: 1, literal: 1, bnode: 1]
import RDF, only: [uri: 1, bnode: 1]
def graph, do: unnamed_graph
@ -220,4 +220,12 @@ defmodule RDF.GraphTest do
end
end
describe "Access behaviour" do
test "access with the [] operator" do
assert Graph.new[EX.Subject] == nil
assert Graph.new({EX.S, EX.p, EX.O})[EX.S] ==
Description.new({EX.S, EX.p, EX.O})
end
end
end