Support for RDF.PropertyMaps as :context opt on functions with input data

This commit is contained in:
Marcel Otto 2020-10-09 16:32:24 +02:00
parent e681733652
commit d0e5b625fd
13 changed files with 618 additions and 156 deletions

View file

@ -18,6 +18,10 @@ are specified.
### Added
- `RDF.PropertyMap` which allow definition of atoms for RDF properties.
Such property maps can be provided to all RDF data structure functions
accepting input data with the `:context` opt, allowing the use of the atoms
from the property map in the input data.
- to `RDF.Description`
- `RDF.Description.subject/1`
- `RDF.Description.change_subject/2`
@ -26,9 +30,11 @@ are specified.
- `RDF.Graph.change_name/2`
- `RDF.Graph.base_iri/1`
- `RDF.Graph.prefixes/1`
- `RDF.Graph.put_properties/3`
- to `RDF.Dataset`
- `RDF.Dataset.name/1`
- `RDF.Dataset.change_name/2`
- `RDF.Dataset.put_properties/3`
- `RDF.IRI.append/2`
### Changed
@ -41,7 +47,9 @@ are specified.
- The `put/3` functions on `RDF.Graph` and `RDF.Dataset` now overwrite all
statements with same subject. Previously only statements with the same subject
AND predicate were overwritten, which was probably not the expected behaviour,
since it's not inline with the common `put` semantics in Elixir.
since it's not inline with the common `put` semantics in Elixir.
A function with the previous behaviour was added on `RDF.Graph` and `RDF.Dataset`
with the `put_properties/3` function.
- **CAUTION: This means the `RDF.Graph.put/2` and `RDF.Dataset.put/2` function have become more destructive now when not specified otherwise.**
- Note: Although one could argue, that following this route `RDF.Dataset.put/3`
would consequently have to overwrite whole graphs, this was not implemented
@ -54,7 +62,10 @@ are specified.
- for consistency reasons the internal `:id` struct field of `RDF.BlankNode` was renamed
to `:value`
- allow the `base_iri` of `RDF.Vocabulary.Namespace`s to end with a `.` to support
vocabularies which use dots in the IRIs for further structuring (eg. CIM-based formats like CGMES)
vocabularies which use dots in the IRIs for further structuring (eg. CIM-based formats like CGMES)
- `RDF.Triple.new/1` now also accepts four-element tuples and simple ignores fourth element
- `RDF.Quad.new/1` now also accepts three-element tuples and simple assumes the fourth
element to be `nil`
### Fixed

View file

@ -56,15 +56,14 @@ defmodule RDF.Dataset do
"""
@spec new(input | keyword) :: t
def new(data_or_options)
def new(data_or_opts)
def new(data_or_options)
when is_list(data_or_options) and length(data_or_options) != 0 do
if Keyword.keyword?(data_or_options) do
{data, options} = Keyword.pop(data_or_options, :init)
def new(data_or_opts) when is_list(data_or_opts) and length(data_or_opts) != 0 do
if Keyword.keyword?(data_or_opts) do
{data, options} = Keyword.pop(data_or_opts, :init)
new(data, options)
else
new(data_or_options, [])
new(data_or_opts, [])
end
end
@ -84,21 +83,21 @@ defmodule RDF.Dataset do
"""
@spec new(input, keyword) :: t
def new(data, options)
def new(data, opts)
def new(%__MODULE__{} = graph, options) do
%__MODULE__{graph | name: options |> Keyword.get(:name) |> coerce_graph_name()}
def new(%__MODULE__{} = graph, opts) do
%__MODULE__{graph | name: opts |> Keyword.get(:name) |> coerce_graph_name()}
end
def new(data, options) do
def new(data, opts) do
%__MODULE__{}
|> new(options)
|> init(data)
|> new(opts)
|> init(data, opts)
end
defp init(dataset, nil), do: dataset
defp init(dataset, fun) when is_function(fun), do: add(dataset, fun.())
defp init(dataset, data), do: add(dataset, data)
defp init(dataset, nil, _), do: dataset
defp init(dataset, fun, opts) when is_function(fun), do: add(dataset, fun.(), opts)
defp init(dataset, data, opts), do: add(dataset, data, opts)
@doc """
Returns the dataset name IRI of `dataset`.
@ -143,13 +142,13 @@ defmodule RDF.Dataset do
def add(dataset, input, opts \\ [])
def add(%__MODULE__{} = dataset, {_, _, _, graph} = quad, opts),
do: do_add(dataset, destination_graph(opts, graph), quad)
do: do_add(dataset, destination_graph(opts, graph), quad, opts)
def add(%__MODULE__{} = dataset, %Description{} = description, opts),
do: do_add(dataset, destination_graph(opts), description)
do: do_add(dataset, destination_graph(opts), description, opts)
def add(%__MODULE__{} = dataset, %Graph{} = graph, opts),
do: do_add(dataset, destination_graph(opts, graph.name), graph)
do: do_add(dataset, destination_graph(opts, graph.name), graph, opts)
def add(%__MODULE__{} = dataset, %__MODULE__{} = other_dataset, opts) do
other_dataset
@ -171,9 +170,9 @@ defmodule RDF.Dataset do
end
def add(%__MODULE__{} = dataset, input, opts),
do: do_add(dataset, destination_graph(opts), input)
do: do_add(dataset, destination_graph(opts), input, opts)
defp do_add(dataset, graph_name, input) do
defp do_add(dataset, graph_name, input, opts) do
%__MODULE__{
dataset
| graphs:
@ -181,9 +180,9 @@ defmodule RDF.Dataset do
dataset.graphs,
graph_name,
# when new:
fn -> Graph.new(input, name: graph_name) end,
fn -> Graph.new(input, Keyword.put(opts, :name, graph_name)) end,
# when update:
fn graph -> Graph.add(graph, input) end
fn graph -> Graph.add(graph, input, opts) end
)
}
end
@ -299,13 +298,13 @@ defmodule RDF.Dataset do
def delete(dataset, input, opts \\ [])
def delete(%__MODULE__{} = dataset, {_, _, _, graph} = quad, opts),
do: do_delete(dataset, destination_graph(opts, graph), quad)
do: do_delete(dataset, destination_graph(opts, graph), quad, opts)
def delete(%__MODULE__{} = dataset, %Description{} = description, opts),
do: do_delete(dataset, destination_graph(opts), description)
do: do_delete(dataset, destination_graph(opts), description, opts)
def delete(%__MODULE__{} = dataset, %Graph{} = graph, opts),
do: do_delete(dataset, destination_graph(opts, graph.name), graph)
do: do_delete(dataset, destination_graph(opts, graph.name), graph, opts)
def delete(%__MODULE__{} = dataset, %__MODULE__{} = other_dataset, opts) do
other_dataset
@ -320,7 +319,7 @@ defmodule RDF.Dataset do
end
def delete(%__MODULE__{} = dataset, input, opts) when not is_struct(input),
do: do_delete(dataset, destination_graph(opts), input)
do: do_delete(dataset, destination_graph(opts), input, opts)
else
def delete(_, %_{}, _), do: raise(ArgumentError, "structs are not allowed as input")
@ -329,12 +328,12 @@ defmodule RDF.Dataset do
end
def delete(%__MODULE__{} = dataset, input, opts),
do: do_delete(dataset, destination_graph(opts), input)
do: do_delete(dataset, destination_graph(opts), input, opts)
end
defp do_delete(dataset, graph_name, input) do
defp do_delete(dataset, graph_name, input, opts) do
if existing_graph = dataset.graphs[graph_name] do
new_graph = Graph.delete(existing_graph, input)
new_graph = Graph.delete(existing_graph, input, opts)
%__MODULE__{
dataset
@ -677,13 +676,13 @@ defmodule RDF.Dataset do
def include?(dataset, input, opts \\ [])
def include?(%__MODULE__{} = dataset, {_, _, _, graph} = quad, opts),
do: do_include?(dataset, destination_graph(opts, graph), quad)
do: do_include?(dataset, destination_graph(opts, graph), quad, opts)
def include?(%__MODULE__{} = dataset, %Description{} = description, opts),
do: do_include?(dataset, destination_graph(opts), description)
do: do_include?(dataset, destination_graph(opts), description, opts)
def include?(%__MODULE__{} = dataset, %Graph{} = graph, opts),
do: do_include?(dataset, destination_graph(opts, graph.name), graph)
do: do_include?(dataset, destination_graph(opts, graph.name), graph, opts)
def include?(%__MODULE__{} = dataset, %__MODULE__{} = other_dataset, opts) do
other_dataset
@ -698,7 +697,7 @@ defmodule RDF.Dataset do
end
def include?(dataset, input, opts) when not is_struct(input),
do: do_include?(dataset, destination_graph(opts), input)
do: do_include?(dataset, destination_graph(opts), input, opts)
else
def include?(_, %_{}, _), do: raise(ArgumentError, "structs are not allowed as input")
@ -706,12 +705,13 @@ defmodule RDF.Dataset do
Enum.all?(input, &include?(dataset, &1, opts))
end
def include?(dataset, input, opts), do: do_include?(dataset, destination_graph(opts), input)
def include?(dataset, input, opts),
do: do_include?(dataset, destination_graph(opts), input, opts)
end
defp do_include?(%__MODULE__{} = dataset, graph_name, input) do
defp do_include?(%__MODULE__{} = dataset, graph_name, input, opts) do
if graph = dataset.graphs[graph_name] do
Graph.include?(graph, input)
Graph.include?(graph, input, opts)
else
false
end

View file

@ -26,7 +26,7 @@ defmodule RDF.Description do
@type predications :: %{Statement.predicate() => %{Statement.object() => nil}}
@type input ::
Triple.coercible_t()
Statement.coercible_t()
| {
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]
@ -36,7 +36,7 @@ defmodule RDF.Description do
Statement.coercible_object() | [Statement.coercible_object()]
}
| [
Triple.coercible_t()
Statement.coercible_t()
| {
Statement.coercible_predicate(),
Statement.coercible_object() | [Statement.coercible_object()]
@ -68,13 +68,15 @@ defmodule RDF.Description do
def new(%__MODULE__{} = description, opts), do: new(description.subject, opts)
def new(subject, opts) do
{data, opts} = Keyword.pop(opts, :init)
%__MODULE__{subject: coerce_subject(subject)}
|> init(Keyword.get(opts, :init))
|> init(data, opts)
end
defp init(description, nil), do: description
defp init(description, fun) when is_function(fun), do: add(description, fun.())
defp init(description, data), do: add(description, data)
defp init(description, nil, _), do: description
defp init(description, fun, opts) when is_function(fun), do: add(description, fun.(), opts)
defp init(description, data, opts), do: add(description, data, opts)
@doc """
Returns the subject IRI or blank node of a description.
@ -90,6 +92,14 @@ defmodule RDF.Description do
%__MODULE__{description | subject: coerce_subject(new_subject)}
end
defp context(nil), do: nil
defp context(opts) do
if property_map = Keyword.get(opts, :context) do
PropertyMap.new(property_map)
end
end
@doc """
Add statements to a `RDF.Description`.
@ -123,11 +133,11 @@ defmodule RDF.Description do
end
end
def add(%__MODULE__{} = description, {predicate, objects}, _opts) do
def add(%__MODULE__{} = description, {predicate, objects}, opts) do
normalized_objects =
objects
|> List.wrap()
|> Map.new(fn object -> {coerce_object(object), nil} end)
|> Map.new(&{coerce_object(&1), nil})
if Enum.empty?(normalized_objects) do
description
@ -137,7 +147,7 @@ defmodule RDF.Description do
| predications:
Map.update(
description.predications,
coerce_predicate(predicate),
coerce_predicate(predicate, context(opts)),
normalized_objects,
fn objects ->
Map.merge(objects, normalized_objects)
@ -217,7 +227,7 @@ defmodule RDF.Description do
def put(%__MODULE__{} = description, %__MODULE__{}, _opts), do: description
def put(%__MODULE__{} = description, input, opts) do
put(description, description.subject |> new() |> add(input), opts)
put(description, description.subject |> new() |> add(input, opts), opts)
end
@doc """
@ -243,8 +253,8 @@ defmodule RDF.Description do
delete(description, {subject, predicate, objects}, opts)
end
def delete(%__MODULE__{} = description, {predicate, objects}, _opts) do
predicate = coerce_predicate(predicate)
def delete(%__MODULE__{} = description, {predicate, objects}, opts) do
predicate = coerce_predicate(predicate, context(opts))
if current_objects = Map.get(description.predications, predicate) do
normalized_objects =
@ -634,20 +644,20 @@ defmodule RDF.Description do
@doc """
Checks if the given `input` statements exist within `description`.
"""
@spec include?(t, input) :: boolean
def include?(description, input)
@spec include?(t, input, keyword) :: boolean
def include?(description, input, opts \\ [])
def include?(%__MODULE__{} = description, {subject, predicate, objects}) do
def include?(%__MODULE__{} = description, {subject, predicate, objects}, opts) do
coerce_subject(subject) == description.subject &&
include?(description, {predicate, objects})
include?(description, {predicate, objects}, opts)
end
def include?(%__MODULE__{} = description, {subject, predicate, objects, _}) do
include?(description, {subject, predicate, objects})
def include?(%__MODULE__{} = description, {subject, predicate, objects, _}, opts) do
include?(description, {subject, predicate, objects}, opts)
end
def include?(%__MODULE__{} = description, {predicate, objects}) do
if existing_objects = description.predications[coerce_predicate(predicate)] do
def include?(%__MODULE__{} = description, {predicate, objects}, opts) do
if existing_objects = description.predications[coerce_predicate(predicate, context(opts))] do
objects
|> List.wrap()
|> Enum.map(&coerce_object/1)
@ -659,7 +669,8 @@ defmodule RDF.Description do
def include?(
%__MODULE__{subject: subject, predications: predications},
%__MODULE__{subject: subject} = input
%__MODULE__{subject: subject} = input,
_opts
) do
Enum.all?(input.predications, fn {predicate, objects} ->
if existing_objects = predications[predicate] do
@ -672,18 +683,18 @@ defmodule RDF.Description do
end)
end
def include?(%__MODULE__{}, %__MODULE__{}), do: false
def include?(%__MODULE__{}, %__MODULE__{}, _), do: false
if Version.match?(System.version(), "~> 1.10") do
def include?(description, input)
def include?(description, input, opts)
when is_list(input) or (is_map(input) and not is_struct(input)) do
Enum.all?(input, &include?(description, &1))
Enum.all?(input, &include?(description, &1, opts))
end
else
def include?(_, %_{}), do: raise(ArgumentError, "structs are not allowed as input")
def include?(_, %_{}, _), do: raise(ArgumentError, "structs are not allowed as input")
def include?(description, input) when is_list(input) or is_map(input) do
Enum.all?(input, &include?(description, &1))
def include?(description, input, opts) when is_list(input) or is_map(input) do
Enum.all?(input, &include?(description, &1, opts))
end
end

View file

@ -71,15 +71,14 @@ defmodule RDF.Graph do
"""
@spec new(input | keyword) :: t
def new(data_or_options)
def new(data_or_opts)
def new(data_or_options)
when is_list(data_or_options) and length(data_or_options) != 0 do
if Keyword.keyword?(data_or_options) do
{data, options} = Keyword.pop(data_or_options, :init)
def new(data_or_opts) when is_list(data_or_opts) and length(data_or_opts) != 0 do
if Keyword.keyword?(data_or_opts) do
{data, options} = Keyword.pop(data_or_opts, :init)
new(data, options)
else
new(data_or_options, [])
new(data_or_opts, [])
end
end
@ -118,23 +117,23 @@ defmodule RDF.Graph do
"""
@spec new(input, keyword) :: t
def new(data, options)
def new(data, opts)
def new(%__MODULE__{} = graph, options) do
%__MODULE__{graph | name: options |> Keyword.get(:name) |> coerce_graph_name()}
|> add_prefixes(Keyword.get(options, :prefixes))
|> set_base_iri(Keyword.get(options, :base_iri))
def new(%__MODULE__{} = graph, opts) do
%__MODULE__{graph | name: opts |> Keyword.get(:name) |> coerce_graph_name()}
|> add_prefixes(Keyword.get(opts, :prefixes))
|> set_base_iri(Keyword.get(opts, :base_iri))
end
def new(data, options) do
def new(data, opts) do
new()
|> new(options)
|> init(data)
|> new(opts)
|> init(data, opts)
end
defp init(graph, nil), do: graph
defp init(graph, fun) when is_function(fun), do: add(graph, fun.())
defp init(graph, data), do: add(graph, data)
defp init(graph, nil, _), do: graph
defp init(graph, fun, opts) when is_function(fun), do: add(graph, fun.(), opts)
defp init(graph, data, opts), do: add(graph, data, opts)
@doc """
Removes all triples from `graph`.
@ -337,43 +336,43 @@ defmodule RDF.Graph do
use `RDF.Data.delete/2`.
"""
@spec delete(t, input) :: t
def delete(graph, input)
@spec delete(t, input, keyword) :: t
def delete(graph, input, opts \\ [])
def delete(%__MODULE__{} = graph, {subject, _, _} = triple),
do: do_delete(graph, coerce_subject(subject), triple)
def delete(%__MODULE__{} = graph, {subject, _, _} = triple, opts),
do: do_delete(graph, coerce_subject(subject), triple, opts)
def delete(%__MODULE__{} = graph, {subject, predications}),
do: do_delete(graph, coerce_subject(subject), predications)
def delete(%__MODULE__{} = graph, {subject, predications}, opts),
do: do_delete(graph, coerce_subject(subject), predications, opts)
def delete(graph, {subject, predicate, object, _}),
do: delete(graph, {subject, predicate, object})
def delete(graph, {subject, predicate, object, _}, opts),
do: delete(graph, {subject, predicate, object}, opts)
def delete(%__MODULE__{} = graph, %Description{} = description),
do: do_delete(graph, description.subject, description)
def delete(%__MODULE__{} = graph, %Description{} = description, opts),
do: do_delete(graph, description.subject, description, opts)
def delete(%__MODULE__{} = graph, %__MODULE__{} = input) do
def delete(%__MODULE__{} = graph, %__MODULE__{} = input, opts) do
Enum.reduce(input.descriptions, graph, fn {_, description}, graph ->
delete(graph, description)
delete(graph, description, opts)
end)
end
if Version.match?(System.version(), "~> 1.10") do
def delete(%__MODULE__{} = graph, input)
def delete(%__MODULE__{} = graph, input, opts)
when is_list(input) or (is_map(input) and not is_struct(input)) do
Enum.reduce(input, graph, &delete(&2, &1))
Enum.reduce(input, graph, &delete(&2, &1, opts))
end
else
def delete(_, %_{}), do: raise(ArgumentError, "structs are not allowed as input")
def delete(_, %_{}, _), do: raise(ArgumentError, "structs are not allowed as input")
def delete(%__MODULE__{} = graph, input) when is_list(input) or is_map(input) do
Enum.reduce(input, graph, &delete(&2, &1))
def delete(%__MODULE__{} = graph, input, opts) when is_list(input) or is_map(input) do
Enum.reduce(input, graph, &delete(&2, &1, opts))
end
end
defp do_delete(%__MODULE__{descriptions: descriptions} = graph, subject, input) do
defp do_delete(%__MODULE__{descriptions: descriptions} = graph, subject, input, opts) do
if description = descriptions[subject] do
new_description = Description.delete(description, input)
new_description = Description.delete(description, input, opts)
%__MODULE__{
graph
@ -792,42 +791,43 @@ defmodule RDF.Graph do
@doc """
Checks if the given `input` statements exist within `graph`.
"""
@spec include?(t, input) :: boolean
def include?(graph, input)
@spec include?(t, input, keyword) :: boolean
def include?(graph, input, opts \\ [])
def include?(%__MODULE__{} = graph, {subject, _, _} = triple),
do: do_include?(graph, coerce_subject(subject), triple)
def include?(%__MODULE__{} = graph, {subject, _, _} = triple, opts),
do: do_include?(graph, coerce_subject(subject), triple, opts)
def include?(graph, {subject, predicate, object, _}),
do: include?(graph, {subject, predicate, object})
def include?(graph, {subject, predicate, object, _}, opts),
do: include?(graph, {subject, predicate, object}, opts)
def include?(%__MODULE__{} = graph, {subject, predications}),
do: do_include?(graph, coerce_subject(subject), predications)
def include?(%__MODULE__{} = graph, {subject, predications}, opts),
do: do_include?(graph, coerce_subject(subject), predications, opts)
def include?(%__MODULE__{} = graph, %Description{subject: subject} = description),
do: do_include?(graph, subject, description)
def include?(%__MODULE__{} = graph, %Description{subject: subject} = description, opts),
do: do_include?(graph, subject, description, opts)
def include?(graph, %__MODULE__{} = other_graph) do
def include?(graph, %__MODULE__{} = other_graph, opts) do
other_graph
|> descriptions()
|> Enum.all?(&include?(graph, &1))
|> Enum.all?(&include?(graph, &1, opts))
end
if Version.match?(System.version(), "~> 1.10") do
def include?(graph, input) when is_list(input) or (is_map(input) and not is_struct(input)) do
Enum.all?(input, &include?(graph, &1))
def include?(graph, input, opts)
when is_list(input) or (is_map(input) and not is_struct(input)) do
Enum.all?(input, &include?(graph, &1, opts))
end
else
def include?(_, %_{}), do: raise(ArgumentError, "structs are not allowed as input")
def include?(_, %_{}, _), do: raise(ArgumentError, "structs are not allowed as input")
def include?(graph, input) when is_list(input) or is_map(input) do
Enum.all?(input, &include?(graph, &1))
def include?(graph, input, opts) when is_list(input) or is_map(input) do
Enum.all?(input, &include?(graph, &1, opts))
end
end
defp do_include?(%__MODULE__{descriptions: descriptions}, subject, input) do
defp do_include?(%__MODULE__{descriptions: descriptions}, subject, input, opts) do
if description = descriptions[subject] do
Description.include?(description, input)
Description.include?(description, input, opts)
else
false
end

View file

@ -140,8 +140,8 @@ defmodule RDF.PrefixMap do
and `namespace2` (the namespace for the prefix in the second prefix map).
The value returned by the `conflict_resolver` function 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.
Non-`RDF.IRI` values will be tried to be converted to `RDF.IRI`s via
`RDF.IRI.new` implicitly.
The most common conflict resolution strategies on can be chosen directly with
the following atoms:

View file

@ -13,6 +13,8 @@ defmodule RDF.PropertyMap do
def new(), do: %__MODULE__{}
def new(%__MODULE__{} = initial), do: initial
def new(initial) do
{:ok, property_map} = new() |> add(initial)

View file

@ -3,18 +3,14 @@ defmodule RDF.Quad do
Helper functions for RDF quads.
A RDF Quad is represented as a plain Elixir tuple consisting of four valid
RDF values for subject, predicate, object and a graph context.
RDF values for subject, predicate, object and a graph name.
"""
alias RDF.Statement
alias RDF.{Statement, PropertyMap}
@type t ::
{Statement.subject(), Statement.predicate(), Statement.object(), Statement.graph_name()}
@type coercible_t ::
{Statement.coercible_subject(), Statement.coercible_predicate(),
Statement.coercible_object(), Statement.coercible_graph_name()}
@type t_values :: {String.t(), String.t(), any, String.t()}
@doc """
@ -28,21 +24,37 @@ defmodule RDF.Quad do
iex> RDF.Quad.new("http://example.com/S", "http://example.com/p", 42, "http://example.com/Graph")
{~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42), ~I<http://example.com/Graph>}
iex> RDF.Quad.new(EX.S, EX.p, 42, EX.Graph)
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42), RDF.iri("http://example.com/Graph")}
iex> RDF.Quad.new(EX.S, :p, 42, EX.Graph, RDF.PropertyMap.new(p: EX.p))
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42), RDF.iri("http://example.com/Graph")}
"""
@spec new(
Statement.coercible_subject(),
Statement.coercible_predicate(),
Statement.coercible_object(),
Statement.coercible_graph_name()
Statement.coercible_graph_name(),
PropertyMap.t() | nil
) :: t
def new(subject, predicate, object, graph_context) do
def new(subject, predicate, object, graph_name, property_map \\ nil)
def new(subject, predicate, object, graph_name, nil) do
{
Statement.coerce_subject(subject),
Statement.coerce_predicate(predicate),
Statement.coerce_object(object),
Statement.coerce_graph_name(graph_context)
Statement.coerce_graph_name(graph_name)
}
end
def new(subject, predicate, object, graph_name, %PropertyMap{} = property_map) do
{
Statement.coerce_subject(subject),
Statement.coerce_predicate(predicate, property_map),
Statement.coerce_object(object),
Statement.coerce_graph_name(graph_name)
}
end
@ -57,12 +69,26 @@ defmodule RDF.Quad do
iex> RDF.Quad.new {"http://example.com/S", "http://example.com/p", 42, "http://example.com/Graph"}
{~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42), ~I<http://example.com/Graph>}
iex> RDF.Quad.new {EX.S, EX.p, 42, EX.Graph}
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42), RDF.iri("http://example.com/Graph")}
iex> RDF.Quad.new {EX.S, EX.p, 42}
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42), nil}
iex> RDF.Quad.new {EX.S, :p, 42, EX.Graph}, RDF.PropertyMap.new(p: EX.p)
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42), RDF.iri("http://example.com/Graph")}
"""
@spec new(coercible_t) :: t
def new({subject, predicate, object, graph_context}),
do: new(subject, predicate, object, graph_context)
@spec new(Statement.coercible_t(), PropertyMap.t() | nil) :: t
def new(statement, property_map \\ nil)
def new({subject, predicate, object, graph_name}, property_map) do
new(subject, predicate, object, graph_name, property_map)
end
def new({subject, predicate, object}, property_map) do
new(subject, predicate, object, nil, property_map)
end
@doc """
Returns a tuple of native Elixir values from a `RDF.Quad` of RDF terms.
@ -96,12 +122,12 @@ defmodule RDF.Quad do
@spec values(t | any, Statement.term_mapping()) :: t_values | nil
def values(quad, mapping \\ &Statement.default_term_mapping/1)
def values({subject, predicate, object, graph_context}, mapping) do
def values({subject, predicate, object, graph_name}, mapping) do
with subject_value when not is_nil(subject_value) <- mapping.({:subject, subject}),
predicate_value when not is_nil(predicate_value) <- mapping.({:predicate, predicate}),
object_value when not is_nil(object_value) <- mapping.({:object, object}),
graph_context_value <- mapping.({:graph_name, graph_context}) do
{subject_value, predicate_value, object_value, graph_context_value}
graph_name_value <- mapping.({:graph_name, graph_name}) do
{subject_value, predicate_value, object_value, graph_name_value}
else
_ -> nil
end
@ -114,7 +140,7 @@ defmodule RDF.Quad do
The elements of a valid RDF quad must be RDF terms. On the subject
position only IRIs and blank nodes allowed, while on the predicate and graph
context position only IRIs allowed. The object position can be any RDF term.
name position only IRIs allowed. The object position can be any RDF term.
"""
@spec valid?(t | any) :: boolean
def valid?(tuple)

View file

@ -5,7 +5,7 @@ defmodule RDF.Statement do
A RDF statement is either a `RDF.Triple` or a `RDF.Quad`.
"""
alias RDF.{BlankNode, IRI, Literal, Quad, Term, Triple}
alias RDF.{BlankNode, IRI, Literal, Quad, Term, Triple, PropertyMap}
import RDF.Guards
@type subject :: IRI.t() | BlankNode.t()
@ -22,7 +22,9 @@ defmodule RDF.Statement do
@type term_mapping :: (qualified_term -> any | nil)
@type t :: Triple.t() | Quad.t()
@type coercible_t :: Triple.coercible_t() | Quad.coercible_t()
@type coercible_t ::
{coercible_subject(), coercible_predicate(), coercible_object(), coercible_graph_name()}
| {coercible_subject(), coercible_predicate(), coercible_object()}
@doc """
Creates a `RDF.Statement` tuple with proper RDF values.
@ -36,8 +38,7 @@ defmodule RDF.Statement do
iex> RDF.Statement.coerce {"http://example.com/S", "http://example.com/p", 42, "http://example.com/Graph"}
{~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42), ~I<http://example.com/Graph>}
"""
@spec coerce(Triple.coercible_t()) :: Triple.t()
@spec coerce(Quad.coercible_t()) :: Quad.t()
@spec coerce(coercible_t()) :: Triple.t() | Quad.t()
def coerce(statement)
def coerce({_, _, _} = triple), do: Triple.new(triple)
def coerce({_, _, _, _} = quad), do: Quad.new(quad)
@ -62,6 +63,16 @@ defmodule RDF.Statement do
def coerce_predicate(iri) when maybe_ns_term(iri) or is_binary(iri), do: RDF.iri!(iri)
def coerce_predicate(arg), do: raise(RDF.Triple.InvalidPredicateError, predicate: arg)
@doc false
@spec coerce_predicate(coercible_predicate, PropertyMap.t()) :: predicate
def coerce_predicate(term, context)
def coerce_predicate(term, %PropertyMap{} = context) when is_atom(term) do
PropertyMap.iri(context, term) || coerce_predicate(term)
end
def coerce_predicate(term, _), do: coerce_predicate(term)
@doc false
@spec coerce_object(coercible_object) :: object
def coerce_object(iri)

View file

@ -6,14 +6,10 @@ defmodule RDF.Triple do
RDF values for subject, predicate and object.
"""
alias RDF.Statement
alias RDF.{Statement, PropertyMap}
@type t :: {Statement.subject(), Statement.predicate(), Statement.object()}
@type coercible_t ::
{Statement.coercible_subject(), Statement.coercible_predicate(),
Statement.coercible_object()}
@type t_values :: {String.t(), String.t(), any}
@doc """
@ -27,15 +23,22 @@ defmodule RDF.Triple do
iex> RDF.Triple.new("http://example.com/S", "http://example.com/p", 42)
{~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42)}
iex> RDF.Triple.new(EX.S, EX.p, 42)
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42)}
iex> RDF.Triple.new(EX.S, :p, 42, RDF.PropertyMap.new(p: EX.p))
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42)}
"""
@spec new(
Statement.coercible_subject(),
Statement.coercible_predicate(),
Statement.coercible_object()
Statement.coercible_object(),
PropertyMap.t() | nil
) :: t
def new(subject, predicate, object) do
def new(subject, predicate, object, property_map \\ nil)
def new(subject, predicate, object, nil) do
{
Statement.coerce_subject(subject),
Statement.coerce_predicate(predicate),
@ -43,6 +46,14 @@ defmodule RDF.Triple do
}
end
def new(subject, predicate, object, %PropertyMap{} = property_map) do
{
Statement.coerce_subject(subject),
Statement.coerce_predicate(predicate, property_map),
Statement.coerce_object(object)
}
end
@doc """
Creates a `RDF.Triple` with proper RDF values.
@ -54,11 +65,24 @@ defmodule RDF.Triple do
iex> RDF.Triple.new {"http://example.com/S", "http://example.com/p", 42}
{~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42)}
iex> RDF.Triple.new {EX.S, EX.p, 42}
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42)}
iex> RDF.Triple.new {EX.S, EX.p, 42, EX.Graph}
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42)}
iex> RDF.Triple.new {EX.S, :p, 42}, RDF.PropertyMap.new(p: EX.p)
{RDF.iri("http://example.com/S"), RDF.iri("http://example.com/p"), RDF.literal(42)}
"""
@spec new(coercible_t) :: t
def new({subject, predicate, object}), do: new(subject, predicate, object)
@spec new(Statement.coercible_t(), PropertyMap.t() | nil) :: t
def new(statement, property_map \\ nil)
def new({subject, predicate, object}, property_map),
do: new(subject, predicate, object, property_map)
def new({subject, predicate, object, _}, property_map),
do: new(subject, predicate, object, property_map)
@doc """
Returns a tuple of native Elixir values from a `RDF.Triple` of RDF terms.

View file

@ -11,7 +11,7 @@ defmodule RDF.Test.Case do
using do
quote do
alias RDF.{Dataset, Graph, Description, IRI, XSD}
alias RDF.{Dataset, Graph, Description, IRI, XSD, PropertyMap}
alias unquote(__MODULE__).{EX, FOAF}
import RDF, only: [iri: 1, literal: 1, bnode: 1]

View file

@ -151,6 +151,11 @@ defmodule RDF.DatasetTest do
)
end
test "with a context" do
ds = Dataset.new({EX.S, :p, EX.O}, context: [p: EX.p()])
assert dataset_includes_statement?(ds, {EX.S, EX.p(), EX.O})
end
@tag skip: "This case is currently not supported, since it's indistinguishable from Keywords"
test "creating a dataset with a list of subject-predications pairs" do
ds =
@ -800,6 +805,54 @@ defmodule RDF.DatasetTest do
assert dataset_includes_statement?(ds, {EX.S2, EX.p(), EX.O5, EX.Graph2})
end
test "with a context" do
context =
PropertyMap.new(
p1: EX.p1(),
p2: EX.p2()
)
assert Dataset.add(dataset(), {EX.Subject, :p, 42}, context: [p: EX.predicate()])
|> dataset_includes_statement?({RDF.iri(EX.Subject), EX.predicate(), literal(42)})
assert Dataset.add(dataset(), {EX.Subject, :p, 42, EX.Graph}, context: %{p: EX.predicate()})
|> dataset_includes_statement?(
{RDF.iri(EX.Subject), EX.predicate(), literal(42), EX.Graph}
)
g =
Dataset.add(
dataset(),
[
{EX.S1, :p1, EX.O1},
{EX.S2, :p2, [EX.O21, EX.O22]}
],
context: context
)
assert Dataset.statement_count(g) == 3
assert dataset_includes_statement?(g, {EX.S1, EX.p1(), EX.O1})
assert dataset_includes_statement?(g, {EX.S2, EX.p2(), EX.O21})
assert dataset_includes_statement?(g, {EX.S2, EX.p2(), EX.O22})
g =
Dataset.add(
dataset(),
[
{EX.S1,
[
{:p1, EX.O1},
%{p2: [EX.O2]}
]}
],
context: context
)
assert Dataset.statement_count(g) == 2
assert dataset_includes_statement?(g, {EX.S1, EX.p1(), EX.O1})
assert dataset_includes_statement?(g, {EX.S1, EX.p2(), EX.O2})
end
test "duplicates are ignored" do
ds = Dataset.add(dataset(), {EX.Subject, EX.predicate(), EX.Object, EX.GraphName})
assert Dataset.add(ds, {EX.Subject, EX.predicate(), EX.Object, EX.GraphName}) == ds
@ -1104,6 +1157,14 @@ defmodule RDF.DatasetTest do
assert dataset_includes_statement?(ds, {EX.S2, EX.p2(), EX.O5})
end
test "with a context" do
ds =
Dataset.new()
|> Dataset.put({EX.S, :p, EX.O}, context: [p: EX.p()])
assert dataset_includes_statement?(ds, {EX.S, EX.p(), EX.O})
end
test "simultaneous use of the different forms to address the default context" do
ds =
Dataset.put(dataset(), [
@ -1283,6 +1344,14 @@ defmodule RDF.DatasetTest do
assert dataset_includes_statement?(ds, {EX.S2, EX.p(), EX.O5})
end
test "with a context" do
ds =
Dataset.new()
|> Dataset.put_properties({EX.S, :p, EX.O}, context: [p: EX.p()])
assert dataset_includes_statement?(ds, {EX.S, EX.p(), EX.O})
end
test "simultaneous use of the different forms to address the default context" do
ds =
Dataset.put_properties(dataset(), [
@ -1469,6 +1538,13 @@ defmodule RDF.DatasetTest do
assert Dataset.delete(dataset2, dataset1) == Dataset.new({EX.S2, EX.p2(), EX.O2, EX.Graph})
end
test "with a context", %{dataset2: dataset2} do
assert dataset2
|> Dataset.delete(%{EX.S2 => %{p2: EX.O2}}, graph: EX.Graph, context: [p2: EX.p2()])
|> Dataset.delete([{EX.S1, [p1: EX.O1]}], context: [p1: EX.p1()]) ==
Dataset.new()
end
test "structs are causing an error" do
assert_raise struct_not_allowed_as_input_error(), fn ->
Dataset.delete(dataset(), Date.utc_today())
@ -1571,6 +1647,15 @@ defmodule RDF.DatasetTest do
assert Dataset.include?(dataset, EX.p(EX.S2, EX.O2), graph: EX.Graph)
assert Dataset.include?(dataset, Graph.new(EX.S1 |> EX.p(EX.O1)))
assert Dataset.include?(dataset, dataset)
assert Dataset.include?(
dataset,
[
{EX.S1, :p, EX.O1},
{EX.S2, :p, EX.O2, EX.Graph}
],
context: [p: EX.p()]
)
end
test "structs are causing an error" do

View file

@ -55,6 +55,23 @@ defmodule RDF.DescriptionTest do
desc = Description.new(EX.Subject, init: other_desc)
assert description_of_subject(desc, iri(EX.Subject))
assert description_includes_predication(desc, {EX.predicate(), iri(EX.Object)})
desc =
Description.new(
EX.Subject,
init: %{
p1: EX.Object1,
p2: EX.Object2
},
context: %{
p1: EX.predicate1(),
p2: EX.predicate2()
}
)
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)})
end
test "with an initializer function" do
@ -233,6 +250,75 @@ defmodule RDF.DescriptionTest do
assert description_includes_predication(desc, {EX.predicate1(), iri(EX.Object4)})
end
test "with a context" do
context =
PropertyMap.new(
p1: EX.p1(),
p2: EX.p2()
)
assert Description.add(description(), {iri(EX.Subject), :p, literal(42)},
context: [p: EX.predicate()]
)
|> description_includes_predication({EX.predicate(), literal(42)})
assert Description.add(description(), {iri(EX.Subject), :p, literal(42), EX.Graph},
context: %{p: EX.predicate()}
)
|> description_includes_predication({EX.predicate(), literal(42)})
desc =
Description.add(
description(),
[
p1: EX.O1,
p2: [EX.O2, ~L"foo", "bar", 42]
],
context: context
)
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)})
desc =
Description.add(
description(),
%{
p1: EX.Object1,
p2: [EX.Object2, 42, bnode(:foo)]
},
context: context
)
assert Description.count(desc) == 4
assert description_includes_predication(desc, {EX.p1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.p2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.p2(), literal(42)})
assert description_includes_predication(desc, {EX.p2(), bnode(:foo)})
desc =
Description.add(
description(),
[
{:p1, EX.Object1},
{EX.Subject, :p2, EX.Object2},
%{p2: EX.Object3},
EX.predicate(EX.Other, EX.Object4)
],
context: context
)
assert Description.count(desc) == 4
assert description_of_subject(desc, iri(EX.Subject))
assert description_includes_predication(desc, {EX.p1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.p2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.p2(), iri(EX.Object3)})
assert description_includes_predication(desc, {EX.predicate(), 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)})
@ -382,7 +468,6 @@ defmodule RDF.DescriptionTest do
)
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)})
@ -397,6 +482,31 @@ defmodule RDF.DescriptionTest do
) == desc
end
test "with a context" do
desc =
Description.put(
description(),
[
{:p1, EX.Object1},
{EX.Subject, :p2, EX.Object2},
%{p3: EX.Object3},
EX.predicate(EX.Other, EX.Object4)
],
context: [
p1: EX.p1(),
p2: EX.p2(),
p3: EX.p3()
]
)
assert Description.count(desc) == 4
assert description_of_subject(desc, iri(EX.Subject))
assert description_includes_predication(desc, {EX.p1(), iri(EX.Object1)})
assert description_includes_predication(desc, {EX.p2(), iri(EX.Object2)})
assert description_includes_predication(desc, {EX.p3(), iri(EX.Object3)})
assert description_includes_predication(desc, {EX.predicate(), iri(EX.Object4)})
end
test "triples with another subject are ignored" do
assert empty_description(
Description.put(description(), {EX.Other, EX.predicate(), iri(EX.Object)})
@ -529,6 +639,28 @@ defmodule RDF.DescriptionTest do
) == Description.new(EX.S, init: {EX.S, EX.p1(), EX.O2})
end
test "with a context", %{description3: description3} do
desc =
Description.delete(
description3,
[
{:p1, EX.O1},
{EX.S, :p2, EX.O3},
%{p3: ~B<foo>},
EX.p3(EX.S, ~L"bar")
],
context: [
p1: EX.p1(),
p2: EX.p2(),
p3: EX.p3()
]
)
assert Description.count(desc) == 1
assert description_of_subject(desc, iri(EX.S))
assert description_includes_predication(desc, {EX.p1(), iri(EX.O2)})
end
test "structs are causing an error" do
assert_raise struct_not_allowed_as_input_error(), fn ->
Description.delete(description(), Date.utc_today())
@ -646,7 +778,7 @@ defmodule RDF.DescriptionTest do
assert Enum.count(desc.predications) == 1
end
describe "include?/2" do
describe "include?/3" do
test "valid cases" do
desc =
Description.new(EX.S,
@ -672,6 +804,16 @@ defmodule RDF.DescriptionTest do
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]})
assert Description.include?(
desc,
%{
p1: [EX.O1, EX.O2],
p2: EX.O3,
p3: [~B<foo>, "bar"]
},
context: %{p1: EX.p1(), p2: EX.p2(), p3: EX.p3()}
)
end
test "structs are causing an error" do

View file

@ -123,6 +123,21 @@ defmodule RDF.GraphTest do
assert graph_includes_statement?(g, {EX.Subject, EX.predicate(), EX.Object})
end
test "with a context" do
g =
Graph.new(
[
{EX.Subject1, p1: EX.Object1},
%{EX.Subject2 => %{p2: EX.Object2}}
],
context: %{p1: EX.predicate1(), p2: EX.predicate2()}
)
assert unnamed_graph?(g)
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject2, EX.predicate2(), EX.Object2})
end
test "with prefixes" do
assert Graph.new(prefixes: %{ex: EX}) ==
%Graph{prefixes: PrefixMap.new(ex: EX)}
@ -305,6 +320,7 @@ defmodule RDF.GraphTest do
EX.Subject3 => %{EX.predicate3() => EX.Object3}
})
assert Graph.triple_count(g) == 3
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate2(), EX.Object2})
assert graph_includes_statement?(g, {EX.Subject3, EX.predicate3(), EX.Object3})
@ -325,11 +341,13 @@ defmodule RDF.GraphTest do
])
)
assert Graph.triple_count(g) == 2
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate2(), EX.Object2})
g = Graph.add(g, Description.new(EX.Subject1, init: {EX.predicate3(), EX.Object3}))
assert Graph.triple_count(g) == 3
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate2(), EX.Object2})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate3(), EX.Object3})
@ -348,6 +366,7 @@ defmodule RDF.GraphTest do
Description.new(EX.Subject1, init: {EX.predicate3(), EX.Object3})
])
assert Graph.triple_count(g) == 3
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject2, EX.predicate2(), EX.Object2})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate3(), EX.Object3})
@ -369,6 +388,7 @@ defmodule RDF.GraphTest do
])
)
assert Graph.triple_count(g) == 3
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject2, EX.predicate2(), EX.Object2})
assert graph_includes_statement?(g, {EX.Subject3, EX.predicate3(), EX.Object3})
@ -382,6 +402,7 @@ defmodule RDF.GraphTest do
])
)
assert Graph.triple_count(g) == 5
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object1})
assert graph_includes_statement?(g, {EX.Subject1, EX.predicate1(), EX.Object2})
assert graph_includes_statement?(g, {EX.Subject2, EX.predicate2(), EX.Object2})
@ -422,6 +443,77 @@ defmodule RDF.GraphTest do
assert graph.prefixes == PrefixMap.new(ex: EX)
end
test "with a context" do
context =
PropertyMap.new(
p1: EX.p1(),
p2: EX.p2()
)
assert Graph.add(graph(), {EX.Subject, :p, 42}, context: [p: EX.predicate()])
|> graph_includes_statement?({RDF.iri(EX.Subject), EX.predicate(), literal(42)})
assert Graph.add(graph(), {EX.Subject, :p, 42, EX.Graph}, context: %{p: EX.predicate()})
|> graph_includes_statement?({RDF.iri(EX.Subject), EX.predicate(), literal(42)})
g =
Graph.add(
graph(),
[
{EX.S1, :p1, EX.O1},
{EX.S2, :p2, [EX.O21, EX.O22]}
],
context: context
)
assert Graph.triple_count(g) == 3
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O1})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O21})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O22})
g =
Graph.add(
graph(),
[
{EX.S1,
[
{:p1, EX.O1},
%{p2: [EX.O2]}
]}
],
context: context
)
assert Graph.triple_count(g) == 2
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O1})
assert graph_includes_statement?(g, {EX.S1, EX.p2(), EX.O2})
g =
Graph.add(
graph(),
[
%{EX.S1 => {:p1, EX.O1}},
%{
EX.S1 => %{p1: EX.O11},
EX.S2 => %{p1: EX.O2}
},
{EX.S2, {:p2, EX.O2}},
[{EX.S2, {:p2, EX.O21}}],
EX.p2(EX.S2, EX.O22)
],
context: context
)
assert Graph.triple_count(g) == 6
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O1})
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O11})
assert graph_includes_statement?(g, {EX.S2, EX.p1(), EX.O2})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O2})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O21})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O22})
end
test "non-coercible Triple elements are causing an error" do
assert_raise RDF.IRI.InvalidError, fn ->
Graph.add(graph(), {"not a IRI", EX.predicate(), iri(EX.Object)})
@ -612,6 +704,24 @@ defmodule RDF.GraphTest do
assert graph.base_iri == EX.base()
end
test "with a context" do
g =
Graph.new([{EX.S1, EX.p1(), EX.O1}, {EX.S2, EX.p2(), EX.O2}])
|> Graph.put(
%{
EX.S1 => [p1: EX.O2],
EX.S2 => %{p1: EX.O2},
EX.S3 => %{p3: EX.O3}
},
context: [p1: EX.p1(), p3: EX.p3()]
)
assert Graph.triple_count(g) == 3
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O2})
assert graph_includes_statement?(g, {EX.S2, EX.p1(), EX.O2})
assert graph_includes_statement?(g, {EX.S3, EX.p3(), EX.O3})
end
test "structs are causing an error" do
assert_raise struct_not_allowed_as_input_error(), fn ->
Graph.put(graph(), Date.utc_today())
@ -801,6 +911,25 @@ defmodule RDF.GraphTest do
assert graph.base_iri == EX.base()
end
test "with a context" do
g =
Graph.new([{EX.S1, EX.p1(), EX.O1}, {EX.S2, EX.p2(), EX.O2}])
|> Graph.put_properties(
%{
EX.S1 => [p1: EX.O2],
EX.S2 => %{p1: EX.O2},
EX.S3 => %{p3: EX.O3}
},
context: [p1: EX.p1(), p3: EX.p3()]
)
assert Graph.triple_count(g) == 4
assert graph_includes_statement?(g, {EX.S1, EX.p1(), EX.O2})
assert graph_includes_statement?(g, {EX.S2, EX.p1(), EX.O2})
assert graph_includes_statement?(g, {EX.S2, EX.p2(), EX.O2})
assert graph_includes_statement?(g, {EX.S3, EX.p3(), EX.O3})
end
test "structs are causing an error" do
assert_raise struct_not_allowed_as_input_error(), fn ->
Graph.put_properties(graph(), Date.utc_today())
@ -812,7 +941,7 @@ defmodule RDF.GraphTest do
end
end
describe "delete/2" do
describe "delete/3" do
setup do
{:ok,
graph1: Graph.new({EX.S, EX.p(), EX.O}),
@ -912,6 +1041,18 @@ defmodule RDF.GraphTest do
) == Graph.new({EX.S3, EX.p3(), ~L"bar"})
end
test "with a context", %{graph2: graph2} do
assert Graph.delete(
graph2,
[
%{EX.S => %{p: EX.O1}},
%{EX.S => {:p, [EX.O2]}}
],
context: [p: EX.p()]
) ==
Graph.new(name: EX.Graph)
end
test "preserves the name and prefixes" do
graph =
Graph.new({EX.Subject, EX.predicate(), EX.Object}, name: EX.GraphName, prefixes: %{ex: EX})
@ -1051,7 +1192,7 @@ defmodule RDF.GraphTest do
assert Enum.count(graph.descriptions) == 1
end
describe "include?/2" do
describe "include?/3" do
test "valid cases" do
graph =
Graph.new([
@ -1077,6 +1218,15 @@ defmodule RDF.GraphTest do
assert Graph.include?(graph, EX.S1 |> EX.p(EX.O1))
assert Graph.include?(graph, Graph.new(EX.S1 |> EX.p(EX.O1)))
assert Graph.include?(graph, graph)
assert Graph.include?(
graph,
[
%{EX.S1 => %{p: EX.O1}},
%{EX.S2 => {:p, [EX.O2]}}
],
context: [p: EX.p()]
)
end
test "structs are causing an error" do