Extract map/2 function from values/2 on all RDF data structures
and add support for RDF.PropertyMaps on values/2 instead
This commit is contained in:
parent
2fbf57a172
commit
a49229384e
14 changed files with 350 additions and 109 deletions
|
@ -59,6 +59,11 @@ are specified.
|
|||
of overwriting only statements with the same subject and predicate, which was
|
||||
almost never the expected behaviour. This is fixed now by relying on the new
|
||||
`put/2` behaviour.
|
||||
- the `values/2` functions of `RDF.Statement`, `RDF.Triple`, `RDF.Quad`, `RDF.Description`,
|
||||
`RDF.Graph` and `RDF.Dataset` now accept on their second argument an optional
|
||||
`RDF.PropertyMap`which will be used to map predicates accordingly; the variant of
|
||||
these `values/2` functions to provide a custom mapping function was extracted into
|
||||
a new function `map/2` on all of these modules
|
||||
- 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
|
||||
|
|
|
@ -94,13 +94,16 @@ defprotocol RDF.Data do
|
|||
|
||||
@doc """
|
||||
Returns a nested map of the native Elixir values of a RDF data structure.
|
||||
|
||||
When the optional `property_map` argument is given, predicates will be mapped
|
||||
to the terms defined in the `RDF.PropertyMap` if present.
|
||||
"""
|
||||
def values(data)
|
||||
def values(data, property_map \\ nil)
|
||||
|
||||
@doc """
|
||||
Returns a nested map of the native Elixir values of a RDF data structure with values mapped with the given function.
|
||||
Returns a map representation of a RDF data structure where each element from its statements is mapped with the given function.
|
||||
"""
|
||||
def values(data, mapping)
|
||||
def map(data, fun)
|
||||
|
||||
@doc """
|
||||
Checks if two RDF data structures are equal.
|
||||
|
@ -191,8 +194,11 @@ defimpl RDF.Data, for: RDF.Description do
|
|||
|
||||
def subject_count(_), do: 1
|
||||
def statement_count(description), do: RDF.Description.count(description)
|
||||
def values(description), do: RDF.Description.values(description)
|
||||
def values(description, mapping), do: RDF.Description.values(description, mapping)
|
||||
|
||||
def values(description, property_map \\ nil),
|
||||
do: RDF.Description.values(description, property_map)
|
||||
|
||||
def map(description, fun), do: RDF.Description.map(description, fun)
|
||||
|
||||
def equal?(description, %RDF.Description{} = other_description) do
|
||||
RDF.Description.equal?(description, other_description)
|
||||
|
@ -273,8 +279,8 @@ defimpl RDF.Data, for: RDF.Graph do
|
|||
|
||||
def subject_count(graph), do: RDF.Graph.subject_count(graph)
|
||||
def statement_count(graph), do: RDF.Graph.triple_count(graph)
|
||||
def values(graph), do: RDF.Graph.values(graph)
|
||||
def values(graph, mapping), do: RDF.Graph.values(graph, mapping)
|
||||
def values(graph, property_map \\ nil), do: RDF.Graph.values(graph, property_map)
|
||||
def map(graph, fun), do: RDF.Graph.map(graph, fun)
|
||||
|
||||
def equal?(graph, %RDF.Description{} = description),
|
||||
do: RDF.Data.equal?(description, graph)
|
||||
|
@ -348,8 +354,8 @@ defimpl RDF.Data, for: RDF.Dataset do
|
|||
|
||||
def subject_count(dataset), do: dataset |> subjects |> Enum.count()
|
||||
def statement_count(dataset), do: RDF.Dataset.statement_count(dataset)
|
||||
def values(dataset), do: RDF.Dataset.values(dataset)
|
||||
def values(dataset, mapping), do: RDF.Dataset.values(dataset, mapping)
|
||||
def values(dataset, property_map \\ nil), do: RDF.Dataset.values(dataset, property_map)
|
||||
def map(dataset, fun), do: RDF.Dataset.map(dataset, fun)
|
||||
|
||||
def equal?(dataset, %RDF.Description{} = description) do
|
||||
with [graph] <- RDF.Dataset.graphs(dataset) do
|
||||
|
|
|
@ -17,8 +17,8 @@ defmodule RDF.Dataset do
|
|||
|
||||
@behaviour Access
|
||||
|
||||
alias RDF.{Graph, Description, IRI, Statement}
|
||||
import RDF.Statement
|
||||
alias RDF.{Graph, Description, IRI, Statement, PropertyMap}
|
||||
import RDF.Statement, only: [coerce_subject: 1, coerce_graph_name: 1]
|
||||
import RDF.Utils
|
||||
|
||||
@type graph_name :: IRI.t() | nil
|
||||
|
@ -761,10 +761,8 @@ defmodule RDF.Dataset do
|
|||
@doc """
|
||||
Returns a nested map of the native Elixir values of a `RDF.Dataset`.
|
||||
|
||||
The optional second argument allows to specify a custom mapping with a function
|
||||
which will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:subject`, `:predicate`, `:object`,
|
||||
or `graph_name` while `rdf_term` is the RDF term to be mapped.
|
||||
When the optional `property_map` argument is given, predicates will be mapped
|
||||
to the terms defined in the `RDF.PropertyMap` if present.
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -783,12 +781,35 @@ defmodule RDF.Dataset do
|
|||
}
|
||||
}
|
||||
|
||||
"""
|
||||
@spec values(t, PropertyMap.t() | nil) :: map
|
||||
def values(dataset, property_map \\ nil)
|
||||
|
||||
def values(%__MODULE__{} = dataset, nil) do
|
||||
map(dataset, &Statement.default_term_mapping/1)
|
||||
end
|
||||
|
||||
def values(%__MODULE__{} = dataset, %PropertyMap{} = property_map) do
|
||||
map(dataset, Statement.default_property_mapping(property_map))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a nested map of a `RDF.Dataset` where each element from its quads is mapped with the given function.
|
||||
|
||||
The function `fun` will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:subject`, `:predicate`, `:object` or
|
||||
`:graph_name` while `rdf_term` is the RDF term to be mapped. When the given function
|
||||
returns `nil` this will be interpreted as an error and will become the overhaul
|
||||
result of the `map/2` call.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> [
|
||||
...> {~I<http://example.com/S>, ~I<http://example.com/p>, ~L"Foo", ~I<http://example.com/Graph>},
|
||||
...> {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.XSD.integer(42), }
|
||||
...> ]
|
||||
...> |> RDF.Dataset.new()
|
||||
...> |> RDF.Dataset.values(fn
|
||||
...> |> RDF.Dataset.map(fn
|
||||
...> {:graph_name, graph_name} ->
|
||||
...> graph_name
|
||||
...> {:predicate, predicate} ->
|
||||
|
@ -810,12 +831,12 @@ defmodule RDF.Dataset do
|
|||
}
|
||||
|
||||
"""
|
||||
@spec values(t, Statement.term_mapping()) :: map
|
||||
def values(dataset, mapping \\ &Statement.default_term_mapping/1)
|
||||
@spec map(t, Statement.term_mapping()) :: map
|
||||
def map(dataset, fun)
|
||||
|
||||
def values(%__MODULE__{} = dataset, mapping) do
|
||||
def map(%__MODULE__{} = dataset, fun) do
|
||||
Map.new(dataset.graphs, fn {graph_name, graph} ->
|
||||
{mapping.({:graph_name, graph_name}), Graph.values(graph, mapping)}
|
||||
{fun.({:graph_name, graph_name}), Graph.map(graph, fun)}
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
@ -15,7 +15,9 @@ defmodule RDF.Description do
|
|||
|
||||
@behaviour Access
|
||||
|
||||
import RDF.Statement
|
||||
import RDF.Statement,
|
||||
only: [coerce_subject: 1, coerce_predicate: 1, coerce_predicate: 2, coerce_object: 1]
|
||||
|
||||
alias RDF.{Statement, Triple, PropertyMap}
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
|
@ -719,12 +721,11 @@ defmodule RDF.Description do
|
|||
Returns a map of the native Elixir values of a `RDF.Description`.
|
||||
|
||||
The subject is not part of the result. It can be converted separately with
|
||||
`RDF.Term.value/1`.
|
||||
`RDF.Term.value/1`, or, if you want the subject in an outer map, just put the
|
||||
the description in a graph and use `RDF.Graph.values/2`.
|
||||
|
||||
The optional second argument allows to specify a custom mapping with a function
|
||||
which will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:predicate` or `:object`,
|
||||
while `rdf_term` is the RDF term to be mapped.
|
||||
When the optional `property_map` argument is given, predicates will be mapped
|
||||
to the terms defined in the `RDF.PropertyMap` if present.
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -733,7 +734,37 @@ defmodule RDF.Description do
|
|||
%{"http://example.com/p" => ["Foo"]}
|
||||
|
||||
iex> RDF.Description.new(~I<http://example.com/S>, init: {~I<http://example.com/p>, ~L"Foo"})
|
||||
...> |> RDF.Description.values(fn
|
||||
...> |> RDF.Description.values(PropertyMap.new(p: ~I<http://example.com/p>))
|
||||
%{p: ["Foo"]}
|
||||
|
||||
"""
|
||||
@spec values(t, PropertyMap.t() | nil) :: map
|
||||
def values(description, property_map \\ nil)
|
||||
|
||||
def values(%__MODULE__{} = description, nil) do
|
||||
map(description, &Statement.default_term_mapping/1)
|
||||
end
|
||||
|
||||
def values(%__MODULE__{} = description, %PropertyMap{} = property_map) do
|
||||
map(description, Statement.default_property_mapping(property_map))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a map of a `RDF.Description` where each element from its triples is mapped with the given function.
|
||||
|
||||
The subject is not part of the result. If you want the subject in an outer map,
|
||||
just put the the description in a graph and use `RDF.Graph.map/2`.
|
||||
|
||||
The function `fun` will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:predicate` or `:object`, while
|
||||
`rdf_term` is the RDF term to be mapped. When the given function returns
|
||||
`nil` this will be interpreted as an error and will become the overhaul result
|
||||
of the `map/2` call.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> RDF.Description.new(~I<http://example.com/S>, init: {~I<http://example.com/p>, ~L"Foo"})
|
||||
...> |> RDF.Description.map(fn
|
||||
...> {:predicate, predicate} ->
|
||||
...> predicate
|
||||
...> |> to_string()
|
||||
|
@ -746,14 +777,14 @@ defmodule RDF.Description do
|
|||
%{p: ["Foo"]}
|
||||
|
||||
"""
|
||||
@spec values(t, Statement.term_mapping()) :: map
|
||||
def values(description, mapping \\ &Statement.default_term_mapping/1)
|
||||
@spec map(t, Statement.term_mapping()) :: map
|
||||
def map(description, fun)
|
||||
|
||||
def values(%__MODULE__{} = description, mapping) do
|
||||
def map(%__MODULE__{} = description, fun) do
|
||||
Map.new(description.predications, fn {predicate, objects} ->
|
||||
{
|
||||
mapping.({:predicate, predicate}),
|
||||
objects |> Map.keys() |> Enum.map(&mapping.({:object, &1}))
|
||||
fun.({:predicate, predicate}),
|
||||
objects |> Map.keys() |> Enum.map(&fun.({:object, &1}))
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -15,9 +15,9 @@ defmodule RDF.Graph do
|
|||
|
||||
@behaviour Access
|
||||
|
||||
import RDF.Statement
|
||||
import RDF.Statement, only: [coerce_subject: 1, coerce_graph_name: 1]
|
||||
import RDF.Utils
|
||||
alias RDF.{Description, IRI, PrefixMap, Statement}
|
||||
alias RDF.{Description, IRI, PrefixMap, Statement, PropertyMap}
|
||||
|
||||
@type graph_description :: %{Statement.subject() => Description.t()}
|
||||
|
||||
|
@ -851,10 +851,8 @@ defmodule RDF.Graph do
|
|||
@doc """
|
||||
Returns a nested map of the native Elixir values of a `RDF.Graph`.
|
||||
|
||||
The optional second argument allows to specify a custom mapping with a function
|
||||
which will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:subject`, `:predicate` or `:object`,
|
||||
while `rdf_term` is the RDF term to be mapped.
|
||||
When the optional `property_map` argument is given, predicates will be mapped
|
||||
to the terms defined in the `RDF.PropertyMap` if present.
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -872,7 +870,40 @@ defmodule RDF.Graph do
|
|||
...> {~I<http://example.com/S1>, ~I<http://example.com/p>, ~L"Foo"},
|
||||
...> {~I<http://example.com/S2>, ~I<http://example.com/p>, RDF.XSD.integer(42)}
|
||||
...> ])
|
||||
...> |> RDF.Graph.values(fn
|
||||
...> |> RDF.Graph.values(PropertyMap.new(p: ~I<http://example.com/p>))
|
||||
%{
|
||||
"http://example.com/S1" => %{p: ["Foo"]},
|
||||
"http://example.com/S2" => %{p: [42]}
|
||||
}
|
||||
|
||||
"""
|
||||
@spec values(t, PropertyMap.t() | nil) :: map
|
||||
def values(graph, property_map \\ nil)
|
||||
|
||||
def values(%__MODULE__{} = graph, nil) do
|
||||
map(graph, &Statement.default_term_mapping/1)
|
||||
end
|
||||
|
||||
def values(%__MODULE__{} = graph, %PropertyMap{} = property_map) do
|
||||
map(graph, Statement.default_property_mapping(property_map))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a nested map of a `RDF.Graph` where each element from its triples is mapped with the given function.
|
||||
|
||||
The function `fun` will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:subject`, `:predicate` or `:object`,
|
||||
while `rdf_term` is the RDF term to be mapped. When the given function returns
|
||||
`nil` this will be interpreted as an error and will become the overhaul result
|
||||
of the `map/2` call.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> RDF.Graph.new([
|
||||
...> {~I<http://example.com/S1>, ~I<http://example.com/p>, ~L"Foo"},
|
||||
...> {~I<http://example.com/S2>, ~I<http://example.com/p>, RDF.XSD.integer(42)}
|
||||
...> ])
|
||||
...> |> RDF.Graph.map(fn
|
||||
...> {:predicate, predicate} ->
|
||||
...> predicate
|
||||
...> |> to_string()
|
||||
|
@ -888,14 +919,14 @@ defmodule RDF.Graph do
|
|||
}
|
||||
|
||||
"""
|
||||
@spec values(t, Statement.term_mapping()) :: map
|
||||
def values(graph, mapping \\ &Statement.default_term_mapping/1)
|
||||
@spec map(t, Statement.term_mapping()) :: map
|
||||
def map(description, fun)
|
||||
|
||||
def values(%__MODULE__{} = graph, mapping) do
|
||||
def map(%__MODULE__{} = graph, fun) do
|
||||
Map.new(graph.descriptions, fn {subject, description} ->
|
||||
{
|
||||
mapping.({:subject, subject}),
|
||||
Description.values(description, mapping)
|
||||
fun.({:subject, subject}),
|
||||
Description.map(description, fun)
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -8,8 +8,12 @@ defmodule RDF.Quad do
|
|||
|
||||
alias RDF.{Statement, PropertyMap}
|
||||
|
||||
@type t ::
|
||||
{Statement.subject(), Statement.predicate(), Statement.object(), Statement.graph_name()}
|
||||
@type t :: {
|
||||
Statement.subject(),
|
||||
Statement.predicate(),
|
||||
Statement.object(),
|
||||
Statement.graph_name()
|
||||
}
|
||||
|
||||
@type t_values :: {String.t(), String.t(), any, String.t()}
|
||||
|
||||
|
@ -93,14 +97,10 @@ defmodule RDF.Quad do
|
|||
@doc """
|
||||
Returns a tuple of native Elixir values from a `RDF.Quad` of RDF terms.
|
||||
|
||||
Returns `nil` if one of the components of the given tuple is not convertible via `RDF.Term.value/1`.
|
||||
When the optional `property_map` argument is given, predicates will be mapped
|
||||
to the terms defined in the `RDF.PropertyMap` if present.
|
||||
|
||||
The optional second argument allows to specify a custom mapping with a function
|
||||
which will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:subject`, `:predicate`, `:object` or
|
||||
`:graph_name`, while `rdf_term` is the RDF term to be mapped. When the given
|
||||
function returns `nil` this will be interpreted as an error and will become
|
||||
the overhaul result of the `values/2` call.
|
||||
Returns `nil` if one of the components of the given tuple is not convertible via `RDF.Term.value/1`.
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -108,7 +108,36 @@ defmodule RDF.Quad do
|
|||
{"http://example.com/S", "http://example.com/p", 42, "http://example.com/Graph"}
|
||||
|
||||
iex> {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42), ~I<http://example.com/Graph>}
|
||||
...> |> RDF.Quad.values(fn
|
||||
...> |> RDF.Quad.values(PropertyMap.new(p: ~I<http://example.com/p>))
|
||||
{"http://example.com/S", :p, 42, "http://example.com/Graph"}
|
||||
|
||||
"""
|
||||
@spec values(t, PropertyMap.t() | nil) :: t_values | nil
|
||||
def values(quad, property_map \\ nil)
|
||||
|
||||
def values(quad, nil) do
|
||||
map(quad, &Statement.default_term_mapping/1)
|
||||
end
|
||||
|
||||
def values(quad, %PropertyMap{} = property_map) do
|
||||
map(quad, Statement.default_property_mapping(property_map))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a tuple where each element from a `RDF.Quad` is mapped with the given function.
|
||||
|
||||
Returns `nil` if one of the components of the given tuple is not convertible via `RDF.Term.value/1`.
|
||||
|
||||
The function `fun` will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:subject`, `:predicate`, `:object` or
|
||||
`:graph_name` while `rdf_term` is the RDF term to be mapped. When the given function
|
||||
returns `nil` this will be interpreted as an error and will become the overhaul
|
||||
result of the `map/2` call.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42), ~I<http://example.com/Graph>}
|
||||
...> |> RDF.Quad.map(fn
|
||||
...> {:object, object} ->
|
||||
...> RDF.Term.value(object)
|
||||
...> {:graph_name, graph_name} ->
|
||||
|
@ -119,22 +148,18 @@ defmodule RDF.Quad do
|
|||
{:S, :p, 42, ~I<http://example.com/Graph>}
|
||||
|
||||
"""
|
||||
@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_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_name_value <- mapping.({:graph_name, graph_name}) do
|
||||
@spec map(t, Statement.term_mapping()) :: t_values | nil
|
||||
def map({subject, predicate, object, graph_name}, fun) do
|
||||
with subject_value when not is_nil(subject_value) <- fun.({:subject, subject}),
|
||||
predicate_value when not is_nil(predicate_value) <- fun.({:predicate, predicate}),
|
||||
object_value when not is_nil(object_value) <- fun.({:object, object}),
|
||||
graph_name_value <- fun.({:graph_name, graph_name}) do
|
||||
{subject_value, predicate_value, object_value, graph_name_value}
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
def values(_, _), do: nil
|
||||
|
||||
@doc """
|
||||
Checks if the given tuple is a valid RDF quad.
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ defmodule RDF.Statement do
|
|||
@type coercible_object :: object | any
|
||||
@type coercible_graph_name :: graph_name | atom | String.t()
|
||||
|
||||
@type qualified_term :: {atom, Term.t() | nil}
|
||||
@type position :: :subject | :predicate | :object | :graph_name
|
||||
@type qualified_term :: {position, Term.t() | nil}
|
||||
@type term_mapping :: (qualified_term -> any | nil)
|
||||
|
||||
@type t :: Triple.t() | Quad.t()
|
||||
|
@ -67,8 +68,8 @@ defmodule RDF.Statement do
|
|||
@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)
|
||||
def coerce_predicate(term, %PropertyMap{} = property_map) when is_atom(term) do
|
||||
PropertyMap.iri(property_map, term) || coerce_predicate(term)
|
||||
end
|
||||
|
||||
def coerce_predicate(term, _), do: coerce_predicate(term)
|
||||
|
@ -98,6 +99,32 @@ defmodule RDF.Statement do
|
|||
@doc """
|
||||
Returns a tuple of native Elixir values from a `RDF.Statement` of RDF terms.
|
||||
|
||||
When the optional `property_map` argument is given, predicates will be mapped
|
||||
to the terms defined in the `RDF.PropertyMap` if present.
|
||||
|
||||
Returns `nil` if one of the components of the given tuple is not convertible via `RDF.Term.value/1`.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> RDF.Statement.values {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42)}
|
||||
{"http://example.com/S", "http://example.com/p", 42}
|
||||
|
||||
iex> RDF.Statement.values {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42), ~I<http://example.com/Graph>}
|
||||
{"http://example.com/S", "http://example.com/p", 42, "http://example.com/Graph"}
|
||||
|
||||
iex> {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42)}
|
||||
...> |> RDF.Statement.values(PropertyMap.new(p: ~I<http://example.com/p>))
|
||||
{"http://example.com/S", :p, 42}
|
||||
|
||||
"""
|
||||
@spec values(t, PropertyMap.t() | nil) :: Triple.t_values() | Quad.t_values() | nil
|
||||
def values(quad, property_map \\ nil)
|
||||
def values({_, _, _} = triple, property_map), do: Triple.values(triple, property_map)
|
||||
def values({_, _, _, _} = quad, property_map), do: Quad.values(quad, property_map)
|
||||
|
||||
@doc """
|
||||
Returns a tuple of native Elixir values from a `RDF.Statement` of RDF terms.
|
||||
|
||||
Returns `nil` if one of the components of the given tuple is not convertible via `RDF.Term.value/1`.
|
||||
|
||||
The optional second argument allows to specify a custom mapping with a function
|
||||
|
@ -109,13 +136,8 @@ defmodule RDF.Statement do
|
|||
|
||||
## Examples
|
||||
|
||||
iex> RDF.Statement.values {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42)}
|
||||
{"http://example.com/S", "http://example.com/p", 42}
|
||||
iex> RDF.Statement.values {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42), ~I<http://example.com/Graph>}
|
||||
{"http://example.com/S", "http://example.com/p", 42, "http://example.com/Graph"}
|
||||
|
||||
iex> {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42), ~I<http://example.com/Graph>}
|
||||
...> |> RDF.Statement.values(fn
|
||||
...> |> RDF.Statement.map(fn
|
||||
...> {:subject, subject} ->
|
||||
...> subject |> to_string() |> String.last()
|
||||
...> {:predicate, predicate} ->
|
||||
|
@ -128,11 +150,10 @@ defmodule RDF.Statement do
|
|||
{"S", :p, 42, ~I<http://example.com/Graph>}
|
||||
|
||||
"""
|
||||
@spec values(t | any, term_mapping) :: Triple.t_values() | Quad.t_values() | nil
|
||||
def values(statement, mapping \\ &default_term_mapping/1)
|
||||
def values({_, _, _} = triple, mapping), do: RDF.Triple.values(triple, mapping)
|
||||
def values({_, _, _, _} = quad, mapping), do: RDF.Quad.values(quad, mapping)
|
||||
def values(_, _), do: nil
|
||||
@spec map(t, term_mapping()) :: Triple.t_values() | Quad.t_values() | nil | nil
|
||||
def map(statement, fun)
|
||||
def map({_, _, _} = triple, fun), do: RDF.Triple.map(triple, fun)
|
||||
def map({_, _, _, _} = quad, fun), do: RDF.Quad.map(quad, fun)
|
||||
|
||||
@doc false
|
||||
@spec default_term_mapping(qualified_term) :: any | nil
|
||||
|
@ -140,6 +161,17 @@ defmodule RDF.Statement do
|
|||
def default_term_mapping({:graph_name, nil}), do: nil
|
||||
def default_term_mapping({_, term}), do: RDF.Term.value(term)
|
||||
|
||||
@spec default_property_mapping(PropertyMap.t()) :: term_mapping
|
||||
def default_property_mapping(%PropertyMap{} = property_map) do
|
||||
fn
|
||||
{:predicate, predicate} ->
|
||||
PropertyMap.term(property_map, predicate) || default_term_mapping({:predicate, predicate})
|
||||
|
||||
other ->
|
||||
default_term_mapping(other)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if the given tuple is a valid RDF statement, i.e. RDF triple or quad.
|
||||
|
||||
|
|
|
@ -87,14 +87,10 @@ defmodule RDF.Triple do
|
|||
@doc """
|
||||
Returns a tuple of native Elixir values from a `RDF.Triple` of RDF terms.
|
||||
|
||||
Returns `nil` if one of the components of the given tuple is not convertible via `RDF.Term.value/1`.
|
||||
When the optional `property_map` argument is given, predicates will be mapped
|
||||
to the terms defined in the `RDF.PropertyMap` if present.
|
||||
|
||||
The optional second argument allows to specify a custom mapping with a function
|
||||
which will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:subject`, `:predicate` or `:object`,
|
||||
while `rdf_term` is the RDF term to be mapped. When the given function returns
|
||||
`nil` this will be interpreted as an error and will become the overhaul result
|
||||
of the `values/2` call.
|
||||
Returns `nil` if one of the components of the given tuple is not convertible via `RDF.Term.value/1`.
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -102,28 +98,54 @@ defmodule RDF.Triple do
|
|||
{"http://example.com/S", "http://example.com/p", 42}
|
||||
|
||||
iex> {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42)}
|
||||
...> |> RDF.Triple.values(fn
|
||||
...> |> RDF.Triple.values(PropertyMap.new(p: ~I<http://example.com/p>))
|
||||
{"http://example.com/S", :p, 42}
|
||||
|
||||
|
||||
"""
|
||||
@spec values(t, PropertyMap.t() | nil) :: t_values | nil
|
||||
def values(triple, property_map \\ nil)
|
||||
|
||||
def values(triple, nil) do
|
||||
map(triple, &Statement.default_term_mapping/1)
|
||||
end
|
||||
|
||||
def values(triple, %PropertyMap{} = property_map) do
|
||||
map(triple, Statement.default_property_mapping(property_map))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a triple where each element from a `RDF.Triple` is mapped with the given function.
|
||||
|
||||
Returns `nil` if one of the components of the given tuple is not convertible via `RDF.Term.value/1`.
|
||||
|
||||
The function `fun` will receive a tuple `{statement_position, rdf_term}` where
|
||||
`statement_position` is one of the atoms `:subject`, `:predicate` or `:object`,
|
||||
while `rdf_term` is the RDF term to be mapped. When the given function returns
|
||||
`nil` this will be interpreted as an error and will become the overhaul result
|
||||
of the `map/2` call.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> {~I<http://example.com/S>, ~I<http://example.com/p>, RDF.literal(42)}
|
||||
...> |> RDF.Triple.map(fn
|
||||
...> {:object, object} -> RDF.Term.value(object)
|
||||
...> {_, term} -> term |> to_string() |> String.last()
|
||||
...> end)
|
||||
{"S", "p", 42}
|
||||
|
||||
"""
|
||||
@spec values(t | any, Statement.term_mapping()) :: t_values | nil
|
||||
def values(triple, mapping \\ &Statement.default_term_mapping/1)
|
||||
|
||||
def values({subject, predicate, object}, 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}) do
|
||||
@spec map(t, Statement.term_mapping()) :: t_values | nil
|
||||
def map({subject, predicate, object}, fun) do
|
||||
with subject_value when not is_nil(subject_value) <- fun.({:subject, subject}),
|
||||
predicate_value when not is_nil(predicate_value) <- fun.({:predicate, predicate}),
|
||||
object_value when not is_nil(object_value) <- fun.({:object, object}) do
|
||||
{subject_value, predicate_value, object_value}
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
def values(_, _), do: nil
|
||||
|
||||
@doc """
|
||||
Checks if the given tuple is a valid RDF triple.
|
||||
|
||||
|
|
|
@ -534,7 +534,7 @@ defmodule RDF.DataTest do
|
|||
}
|
||||
end
|
||||
|
||||
test "values/2", %{dataset: dataset} do
|
||||
test "map/2", %{dataset: dataset} do
|
||||
mapping = fn
|
||||
{:graph_name, graph_name} ->
|
||||
graph_name
|
||||
|
@ -546,7 +546,7 @@ defmodule RDF.DataTest do
|
|||
RDF.Term.value(term)
|
||||
end
|
||||
|
||||
assert RDF.Data.values(dataset, mapping) ==
|
||||
assert RDF.Data.map(dataset, mapping) ==
|
||||
%{
|
||||
nil => %{
|
||||
RDF.Term.value(RDF.iri(EX.S)) => %{
|
||||
|
|
|
@ -1681,6 +1681,19 @@ defmodule RDF.DatasetTest do
|
|||
end
|
||||
|
||||
test "values/2" do
|
||||
assert Dataset.new([{EX.s1(), EX.p(), EX.o1()}, {EX.s2(), EX.p(), EX.o2(), EX.graph()}])
|
||||
|> Dataset.values(PropertyMap.new(p: EX.p())) ==
|
||||
%{
|
||||
nil => %{
|
||||
RDF.Term.value(EX.s1()) => %{p: [RDF.Term.value(EX.o1())]}
|
||||
},
|
||||
RDF.Term.value(EX.graph()) => %{
|
||||
RDF.Term.value(EX.s2()) => %{p: [RDF.Term.value(EX.o2())]}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
test "map/2" do
|
||||
mapping = fn
|
||||
{:graph_name, graph_name} ->
|
||||
graph_name
|
||||
|
@ -1692,10 +1705,10 @@ defmodule RDF.DatasetTest do
|
|||
RDF.Term.value(term)
|
||||
end
|
||||
|
||||
assert Dataset.new() |> Dataset.values(mapping) == %{}
|
||||
assert Dataset.new() |> Dataset.map(mapping) == %{}
|
||||
|
||||
assert Dataset.new([{EX.s1(), EX.p(), EX.o1()}, {EX.s2(), EX.p(), EX.o2(), EX.graph()}])
|
||||
|> Dataset.values(mapping) ==
|
||||
|> Dataset.map(mapping) ==
|
||||
%{
|
||||
nil => %{
|
||||
RDF.Term.value(EX.s1()) => %{p: [RDF.Term.value(EX.o1())]}
|
||||
|
|
|
@ -839,6 +839,12 @@ defmodule RDF.DescriptionTest do
|
|||
end
|
||||
|
||||
test "values/2" do
|
||||
assert Description.new(EX.s(), init: {EX.s(), EX.p(), ~L"Foo"})
|
||||
|> Description.values(PropertyMap.new(p: EX.p())) ==
|
||||
%{p: ["Foo"]}
|
||||
end
|
||||
|
||||
test "map/2" do
|
||||
mapping = fn
|
||||
{:predicate, predicate} ->
|
||||
predicate |> to_string() |> String.split("/") |> List.last() |> String.to_atom()
|
||||
|
@ -847,9 +853,9 @@ defmodule RDF.DescriptionTest do
|
|||
RDF.Term.value(term)
|
||||
end
|
||||
|
||||
assert Description.new(EX.s()) |> Description.values(mapping) == %{}
|
||||
assert Description.new(EX.s()) |> Description.map(mapping) == %{}
|
||||
|
||||
assert Description.new(EX.s(), init: {EX.s(), EX.p(), ~L"Foo"}) |> Description.values(mapping) ==
|
||||
assert Description.new(EX.s(), init: {EX.s(), EX.p(), ~L"Foo"}) |> Description.map(mapping) ==
|
||||
%{p: ["Foo"]}
|
||||
end
|
||||
|
||||
|
|
|
@ -1252,6 +1252,15 @@ defmodule RDF.GraphTest do
|
|||
end
|
||||
|
||||
test "values/2" do
|
||||
assert Graph.new([{EX.s1(), EX.p(), EX.o1()}, {EX.s2(), EX.p(), EX.o2()}])
|
||||
|> Graph.values(PropertyMap.new(p: EX.p())) ==
|
||||
%{
|
||||
RDF.Term.value(EX.s1()) => %{p: [RDF.Term.value(EX.o1())]},
|
||||
RDF.Term.value(EX.s2()) => %{p: [RDF.Term.value(EX.o2())]}
|
||||
}
|
||||
end
|
||||
|
||||
test "map/2" do
|
||||
mapping = fn
|
||||
{:predicate, predicate} ->
|
||||
predicate |> to_string() |> String.split("/") |> List.last() |> String.to_atom()
|
||||
|
@ -1260,10 +1269,10 @@ defmodule RDF.GraphTest do
|
|||
RDF.Term.value(term)
|
||||
end
|
||||
|
||||
assert Graph.new() |> Graph.values(mapping) == %{}
|
||||
assert Graph.new() |> Graph.map(mapping) == %{}
|
||||
|
||||
assert Graph.new([{EX.s1(), EX.p(), EX.o1()}, {EX.s2(), EX.p(), EX.o2()}])
|
||||
|> Graph.values(mapping) ==
|
||||
|> Graph.map(mapping) ==
|
||||
%{
|
||||
RDF.Term.value(EX.s1()) => %{p: [RDF.Term.value(EX.o1())]},
|
||||
RDF.Term.value(EX.s2()) => %{p: [RDF.Term.value(EX.o2())]}
|
||||
|
|
|
@ -20,15 +20,36 @@ defmodule RDF.QuadTest do
|
|||
end
|
||||
|
||||
test "with an invalid RDF.Quad" do
|
||||
refute Quad.values({~I<http://example.com/S>, ~I<http://example.com/p>})
|
||||
refute Quad.values({self(), self(), self(), self()})
|
||||
end
|
||||
end
|
||||
|
||||
test "values/2" do
|
||||
describe "values/2" do
|
||||
test "with a valid RDF.Quad and RDF.PropertyMap" do
|
||||
assert Quad.values(
|
||||
{~I<http://example.com/S>, ~I<http://example.com/p>, XSD.integer(42),
|
||||
~I<http://example.com/Graph>},
|
||||
PropertyMap.new(p: ~I<http://example.com/p>)
|
||||
) ==
|
||||
{"http://example.com/S", :p, 42, "http://example.com/Graph"}
|
||||
|
||||
assert Quad.values(
|
||||
{~I<http://example.com/S>, ~I<http://example.com/p>, XSD.integer(42),
|
||||
~I<http://example.com/Graph>},
|
||||
PropertyMap.new()
|
||||
) ==
|
||||
{"http://example.com/S", "http://example.com/p", 42, "http://example.com/Graph"}
|
||||
end
|
||||
|
||||
test "with an invalid RDF.Triple" do
|
||||
refute Quad.values({self(), self(), self(), self()}, PropertyMap.new())
|
||||
end
|
||||
end
|
||||
|
||||
test "map/2" do
|
||||
assert {~I<http://example.com/S>, ~I<http://example.com/p>, XSD.integer(42),
|
||||
~I<http://example.com/Graph>}
|
||||
|> Quad.values(fn
|
||||
|> Quad.map(fn
|
||||
{:subject, subject} -> subject |> to_string() |> String.last() |> String.to_atom()
|
||||
{:predicate, _} -> :p
|
||||
{:object, object} -> object |> RDF.Term.value() |> Kernel.+(1)
|
||||
|
|
|
@ -12,14 +12,33 @@ defmodule RDF.TripleTest do
|
|||
end
|
||||
|
||||
test "with an invalid RDF.Triple" do
|
||||
refute Triple.values({~I<http://example.com/S>, ~I<http://example.com/p>})
|
||||
refute Triple.values({self(), self(), self()})
|
||||
end
|
||||
end
|
||||
|
||||
test "values/2" do
|
||||
describe "values/2" do
|
||||
test "with a valid RDF.Triple and RDF.PropertyMap" do
|
||||
assert Triple.values(
|
||||
{~I<http://example.com/S>, ~I<http://example.com/p>, XSD.integer(42)},
|
||||
PropertyMap.new(p: ~I<http://example.com/p>)
|
||||
) ==
|
||||
{"http://example.com/S", :p, 42}
|
||||
|
||||
assert Triple.values(
|
||||
{~I<http://example.com/S>, ~I<http://example.com/p>, XSD.integer(42)},
|
||||
PropertyMap.new()
|
||||
) ==
|
||||
{"http://example.com/S", "http://example.com/p", 42}
|
||||
end
|
||||
|
||||
test "with an invalid RDF.Triple" do
|
||||
refute Triple.values({self(), self(), self()}, PropertyMap.new())
|
||||
end
|
||||
end
|
||||
|
||||
test "map/2" do
|
||||
assert {~I<http://example.com/S>, ~I<http://example.com/p>, XSD.integer(42)}
|
||||
|> Triple.values(fn
|
||||
|> Triple.map(fn
|
||||
{:object, object} -> object |> RDF.Term.value() |> Kernel.+(1)
|
||||
{_, term} -> term |> to_string() |> String.last()
|
||||
end) ==
|
||||
|
|
Loading…
Reference in a new issue