Apply mix formatter
This commit is contained in:
parent
9314cc6757
commit
b06586c387
|
@ -0,0 +1,4 @@
|
|||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
||||
import_deps: [:rdf]
|
||||
]
|
File diff suppressed because it is too large
Load Diff
|
@ -11,26 +11,24 @@ defmodule JSON.LD.Context do
|
|||
alias JSON.LD.Context.TermDefinition
|
||||
alias RDF.IRI
|
||||
|
||||
|
||||
def base(%JSON.LD.Context{base_iri: false, api_base_iri: api_base_iri}),
|
||||
do: api_base_iri
|
||||
|
||||
def base(%JSON.LD.Context{base_iri: base_iri}),
|
||||
do: base_iri
|
||||
|
||||
|
||||
def new(options \\ %JSON.LD.Options{}),
|
||||
do: %JSON.LD.Context{api_base_iri: JSON.LD.Options.new(options).base}
|
||||
|
||||
def create(%{"@context" => json_ld_context}, options),
|
||||
do: new(options) |> update(json_ld_context, [], options)
|
||||
|
||||
|
||||
def update(active, local, remote \\ [], options \\ %JSON.LD.Options{})
|
||||
|
||||
def update(%JSON.LD.Context{} = active, local, remote, options) when is_list(local) do
|
||||
Enum.reduce local, active, fn (local, result) ->
|
||||
Enum.reduce(local, active, fn local, result ->
|
||||
do_update(result, local, remote, options)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
# 2) If local context is not an array, set it to an array containing only local context.
|
||||
|
@ -38,7 +36,6 @@ defmodule JSON.LD.Context do
|
|||
update(active, [local], remote, options)
|
||||
end
|
||||
|
||||
|
||||
# 3.1) If context is null, set result to a newly-initialized active context and continue with the next context. The base IRI of the active context is set to the IRI of the currently being processed document (which might be different from the currently being processed context), if available; otherwise to null. If set, the base option of a JSON-LD API Implementation overrides the base IRI.
|
||||
defp do_update(%JSON.LD.Context{}, nil, _remote, options) do
|
||||
new(options)
|
||||
|
@ -54,6 +51,7 @@ defmodule JSON.LD.Context do
|
|||
raise JSON.LD.RecursiveContextInclusionError,
|
||||
message: "Recursive context inclusion: #{local}"
|
||||
end
|
||||
|
||||
remote = remote ++ [local]
|
||||
|
||||
# 3.2.3)
|
||||
|
@ -61,25 +59,38 @@ defmodule JSON.LD.Context do
|
|||
|
||||
document =
|
||||
case apply(document_loader, :load, [local, options]) do
|
||||
{:ok, result} -> result.document
|
||||
{:error, reason} -> raise JSON.LD.LoadingRemoteContextFailedError,
|
||||
message: "Could not load remote context (#{local}): #{inspect reason}"
|
||||
{:ok, result} ->
|
||||
result.document
|
||||
|
||||
{:error, reason} ->
|
||||
raise JSON.LD.LoadingRemoteContextFailedError,
|
||||
message: "Could not load remote context (#{local}): #{inspect(reason)}"
|
||||
end
|
||||
|
||||
document = cond do
|
||||
is_map(document) -> document
|
||||
is_binary(document) -> case Jason.decode(document) do
|
||||
{:ok, result} -> result
|
||||
{:error, reason} -> raise JSON.LD.InvalidRemoteContextError,
|
||||
message: "Context is not a valid JSON document: #{inspect reason}"
|
||||
end
|
||||
true -> raise JSON.LD.InvalidRemoteContextError,
|
||||
message: "Context is not a valid JSON object: #{inspect document}"
|
||||
end
|
||||
document =
|
||||
cond do
|
||||
is_map(document) ->
|
||||
document
|
||||
|
||||
local = document["@context"] ||
|
||||
raise JSON.LD.InvalidRemoteContextError,
|
||||
message: "Invalid remote context: No @context key in #{inspect document}"
|
||||
is_binary(document) ->
|
||||
case Jason.decode(document) do
|
||||
{:ok, result} ->
|
||||
result
|
||||
|
||||
{:error, reason} ->
|
||||
raise JSON.LD.InvalidRemoteContextError,
|
||||
message: "Context is not a valid JSON document: #{inspect(reason)}"
|
||||
end
|
||||
|
||||
true ->
|
||||
raise JSON.LD.InvalidRemoteContextError,
|
||||
message: "Context is not a valid JSON object: #{inspect(document)}"
|
||||
end
|
||||
|
||||
local =
|
||||
document["@context"] ||
|
||||
raise JSON.LD.InvalidRemoteContextError,
|
||||
message: "Invalid remote context: No @context key in #{inspect(document)}"
|
||||
|
||||
# 3.2.4) - 3.2.5)
|
||||
do_update(active, local, remote, options)
|
||||
|
@ -87,8 +98,8 @@ defmodule JSON.LD.Context do
|
|||
|
||||
# 3.4) - 3.8)
|
||||
defp do_update(%JSON.LD.Context{} = active, local, remote, _) when is_map(local) do
|
||||
with {base, local} <- Map.pop(local, "@base", false),
|
||||
{vocab, local} <- Map.pop(local, "@vocab", false),
|
||||
with {base, local} <- Map.pop(local, "@base", false),
|
||||
{vocab, local} <- Map.pop(local, "@vocab", false),
|
||||
{language, local} <- Map.pop(local, "@language", false) do
|
||||
active
|
||||
|> set_base(base, remote)
|
||||
|
@ -100,58 +111,70 @@ defmodule JSON.LD.Context do
|
|||
|
||||
# 3.3) If context is not a JSON object, an invalid local context error has been detected and processing is aborted.
|
||||
defp do_update(_, local, _, _),
|
||||
do: raise JSON.LD.InvalidLocalContextError,
|
||||
message: "#{inspect local} is not a valid @context value"
|
||||
|
||||
do:
|
||||
raise(JSON.LD.InvalidLocalContextError,
|
||||
message: "#{inspect(local)} is not a valid @context value"
|
||||
)
|
||||
|
||||
defp set_base(active, false, _),
|
||||
do: active
|
||||
|
||||
defp set_base(active, _, remote) when is_list(remote) and length(remote) > 0,
|
||||
do: active
|
||||
|
||||
defp set_base(active, base, _) do
|
||||
cond do
|
||||
# TODO: this slightly differs from the spec, due to our false special value for base_iri; add more tests
|
||||
is_nil(base) or IRI.absolute?(base) ->
|
||||
%JSON.LD.Context{active | base_iri: base}
|
||||
|
||||
active.base_iri ->
|
||||
%JSON.LD.Context{active | base_iri: absolute_iri(base, active.base_iri)}
|
||||
|
||||
true ->
|
||||
raise JSON.LD.InvalidBaseIRIError,
|
||||
message: "#{inspect base} is a relative IRI, but no active base IRI defined"
|
||||
message: "#{inspect(base)} is a relative IRI, but no active base IRI defined"
|
||||
end
|
||||
end
|
||||
|
||||
defp set_vocab(active, false), do: active
|
||||
|
||||
defp set_vocab(active, vocab) do
|
||||
if is_nil(vocab) or IRI.absolute?(vocab) or blank_node_id?(vocab) do
|
||||
%JSON.LD.Context{active | vocab: vocab}
|
||||
else
|
||||
raise JSON.LD.InvalidVocabMappingError,
|
||||
message: "#{inspect vocab} is not a valid vocabulary mapping"
|
||||
message: "#{inspect(vocab)} is not a valid vocabulary mapping"
|
||||
end
|
||||
end
|
||||
|
||||
defp set_language(active, false), do: active
|
||||
|
||||
defp set_language(active, nil),
|
||||
do: %JSON.LD.Context{active | default_language: nil}
|
||||
|
||||
defp set_language(active, language) when is_binary(language),
|
||||
do: %JSON.LD.Context{active | default_language: String.downcase(language)}
|
||||
|
||||
defp set_language(_, language),
|
||||
do: raise JSON.LD.InvalidDefaultLanguageError,
|
||||
message: "#{inspect language} is not a valid language"
|
||||
do:
|
||||
raise(JSON.LD.InvalidDefaultLanguageError,
|
||||
message: "#{inspect(language)} is not a valid language"
|
||||
)
|
||||
|
||||
def language(active, term) do
|
||||
case Map.get(active.term_defs, term, %TermDefinition{}).language_mapping do
|
||||
false -> active.default_language
|
||||
false -> active.default_language
|
||||
language -> language
|
||||
end
|
||||
end
|
||||
|
||||
defp create_term_definitions(active, local, defined \\ %{}) do
|
||||
{active, _} =
|
||||
Enum.reduce local, {active, defined}, fn ({term, value}, {active, defined}) ->
|
||||
Enum.reduce(local, {active, defined}, fn {term, value}, {active, defined} ->
|
||||
create_term_definition(active, local, term, value, defined)
|
||||
end
|
||||
end)
|
||||
|
||||
active
|
||||
end
|
||||
|
||||
|
@ -162,31 +185,47 @@ defmodule JSON.LD.Context do
|
|||
"""
|
||||
def create_term_definition(active, local, term, value, defined)
|
||||
|
||||
def create_term_definition(active, _, "@base", _, defined), do: {active, defined}
|
||||
def create_term_definition(active, _, "@vocab", _, defined), do: {active, defined}
|
||||
def create_term_definition(active, _, "@base", _, defined), do: {active, defined}
|
||||
def create_term_definition(active, _, "@vocab", _, defined), do: {active, defined}
|
||||
def create_term_definition(active, _, "@language", _, defined), do: {active, defined}
|
||||
|
||||
def create_term_definition(active, local, term, value, defined) do
|
||||
# 3)
|
||||
if term in JSON.LD.keywords,
|
||||
do: raise JSON.LD.KeywordRedefinitionError,
|
||||
message: "#{inspect term} is a keyword and can not be defined in context"
|
||||
if term in JSON.LD.keywords(),
|
||||
do:
|
||||
raise(JSON.LD.KeywordRedefinitionError,
|
||||
message: "#{inspect(term)} is a keyword and can not be defined in context"
|
||||
)
|
||||
|
||||
# 1)
|
||||
case defined[term] do
|
||||
true -> {active, defined}
|
||||
false -> raise JSON.LD.CyclicIRIMappingError #, message: "#{inspect term} .."
|
||||
nil -> do_create_term_definition(active, local, term, value,
|
||||
Map.put(defined, term, false)) # 2)
|
||||
true ->
|
||||
{active, defined}
|
||||
|
||||
# , message: "#{inspect term} .."
|
||||
false ->
|
||||
raise JSON.LD.CyclicIRIMappingError
|
||||
|
||||
nil ->
|
||||
do_create_term_definition(
|
||||
active,
|
||||
local,
|
||||
term,
|
||||
value,
|
||||
# 2)
|
||||
Map.put(defined, term, false)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_create_term_definition(active, _local, term, nil, defined) do
|
||||
{
|
||||
# (if Map.has_key?(active.term_defs, term),
|
||||
# do: put_in(active, [:term_defs, term], nil),
|
||||
# else: raise "NotImplemented"),
|
||||
# (if Map.has_key?(active.term_defs, term),
|
||||
# do: put_in(active, [:term_defs, term], nil),
|
||||
# else: raise "NotImplemented"),
|
||||
%JSON.LD.Context{active | term_defs: Map.put(active.term_defs, term, nil)},
|
||||
Map.put(defined, term, true)}
|
||||
Map.put(defined, term, true)
|
||||
}
|
||||
end
|
||||
|
||||
defp do_create_term_definition(active, local, term, %{"@id" => nil}, defined),
|
||||
|
@ -196,46 +235,55 @@ defmodule JSON.LD.Context do
|
|||
do: do_create_term_definition(active, local, term, %{"@id" => value}, defined)
|
||||
|
||||
defp do_create_term_definition(active, local, term, %{} = value, defined) do
|
||||
definition = %TermDefinition{} # 9)
|
||||
# 9)
|
||||
definition = %TermDefinition{}
|
||||
|
||||
{definition, active, defined} =
|
||||
do_create_type_definition(definition, active, local, value, defined)
|
||||
|
||||
{done, definition, active, defined} =
|
||||
do_create_reverse_definition(definition, active, local, value, defined)
|
||||
do_create_reverse_definition(definition, active, local, value, defined)
|
||||
|
||||
{definition, active, defined} =
|
||||
unless done do
|
||||
{definition, active, defined} =
|
||||
do_create_id_definition(definition, active, local, term, value, defined)
|
||||
|
||||
definition = do_create_container_definition(definition, value)
|
||||
definition = do_create_language_definition(definition, value)
|
||||
{definition, active, defined}
|
||||
else
|
||||
{definition, active, defined}
|
||||
end
|
||||
|
||||
# 18 / 11.6) Set the term definition of term in active context to definition and set the value associated with defined's key term to true.
|
||||
{%JSON.LD.Context{active | term_defs: Map.put(active.term_defs, term, definition)},
|
||||
Map.put(defined, term, true)}
|
||||
Map.put(defined, term, true)}
|
||||
end
|
||||
|
||||
defp do_create_term_definition(_, _, _, value, _),
|
||||
do: raise JSON.LD.InvalidTermDefinitionError,
|
||||
message: "#{inspect value} is not a valid term definition"
|
||||
|
||||
do:
|
||||
raise(JSON.LD.InvalidTermDefinitionError,
|
||||
message: "#{inspect(value)} is not a valid term definition"
|
||||
)
|
||||
|
||||
# 10.1)
|
||||
# TODO: RDF.rb implementation says: "SPEC FIXME: @type may be nil"
|
||||
defp do_create_type_definition(_, _, _, %{"@type" => type}, _) when not is_binary(type),
|
||||
do: raise JSON.LD.InvalidTypeMappingError,
|
||||
message: "#{inspect type} is not a valid type mapping"
|
||||
do:
|
||||
raise(JSON.LD.InvalidTypeMappingError,
|
||||
message: "#{inspect(type)} is not a valid type mapping"
|
||||
)
|
||||
|
||||
# 10.2) and 10.3)
|
||||
defp do_create_type_definition(definition, active, local, %{"@type" => type}, defined) do
|
||||
{expanded_type, active, defined} =
|
||||
expand_iri(type, active, false, true, local, defined)
|
||||
{expanded_type, active, defined} = expand_iri(type, active, false, true, local, defined)
|
||||
|
||||
if IRI.absolute?(expanded_type) or expanded_type in ~w[@id @vocab] do
|
||||
{%TermDefinition{definition | type_mapping: expanded_type}, active, defined}
|
||||
else
|
||||
raise JSON.LD.InvalidTypeMappingError,
|
||||
message: "#{inspect type} is not a valid type mapping"
|
||||
message: "#{inspect(type)} is not a valid type mapping"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -243,35 +291,52 @@ defmodule JSON.LD.Context do
|
|||
do: {definition, active, defined}
|
||||
|
||||
# 11) If value contains the key @reverse
|
||||
defp do_create_reverse_definition(definition, active, local,
|
||||
%{"@reverse" => reverse} = value, defined) do
|
||||
defp do_create_reverse_definition(
|
||||
definition,
|
||||
active,
|
||||
local,
|
||||
%{"@reverse" => reverse} = value,
|
||||
defined
|
||||
) do
|
||||
cond do
|
||||
Map.has_key?(value, "@id") -> # 11.1)
|
||||
# 11.1)
|
||||
Map.has_key?(value, "@id") ->
|
||||
raise JSON.LD.InvalidReversePropertyError,
|
||||
message: "#{inspect reverse} is not a valid reverse property"
|
||||
not is_binary(reverse) -> # 11.2)
|
||||
message: "#{inspect(reverse)} is not a valid reverse property"
|
||||
|
||||
# 11.2)
|
||||
not is_binary(reverse) ->
|
||||
raise JSON.LD.InvalidIRIMappingError,
|
||||
message: "Expected String for @reverse value. got #{inspect reverse}"
|
||||
true -> # 11.3)
|
||||
message: "Expected String for @reverse value. got #{inspect(reverse)}"
|
||||
|
||||
# 11.3)
|
||||
true ->
|
||||
{expanded_reverse, active, defined} =
|
||||
expand_iri(reverse, active, false, true, local, defined)
|
||||
|
||||
definition =
|
||||
if IRI.absolute?(expanded_reverse) or blank_node_id?(expanded_reverse) do
|
||||
%TermDefinition{definition | iri_mapping: expanded_reverse}
|
||||
else
|
||||
raise JSON.LD.InvalidIRIMappingError,
|
||||
message: "Non-absolute @reverse IRI: #{inspect reverse}"
|
||||
message: "Non-absolute @reverse IRI: #{inspect(reverse)}"
|
||||
end
|
||||
|
||||
# 11.4)
|
||||
definition =
|
||||
case Map.get(value, "@container", {false}) do # 11.4)
|
||||
case Map.get(value, "@container", {false}) do
|
||||
{false} ->
|
||||
definition
|
||||
|
||||
container when is_nil(container) or container in ~w[@set @index] ->
|
||||
%TermDefinition{definition | container_mapping: container}
|
||||
|
||||
_ ->
|
||||
raise JSON.LD.InvalidReversePropertyError,
|
||||
message: "#{inspect reverse} is not a valid reverse property; reverse properties only support set- and index-containers"
|
||||
message:
|
||||
"#{inspect(reverse)} is not a valid reverse property; reverse properties only support set- and index-containers"
|
||||
end
|
||||
|
||||
# 11.5) & 11.6)
|
||||
{true, %TermDefinition{definition | reverse_property: true}, active, defined}
|
||||
end
|
||||
|
@ -280,29 +345,32 @@ defmodule JSON.LD.Context do
|
|||
defp do_create_reverse_definition(definition, active, _, _, defined),
|
||||
do: {false, definition, active, defined}
|
||||
|
||||
|
||||
# 13)
|
||||
defp do_create_id_definition(definition, active, local, term,
|
||||
%{"@id" => id}, defined) when id != term do
|
||||
defp do_create_id_definition(definition, active, local, term, %{"@id" => id}, defined)
|
||||
when id != term do
|
||||
# 13.1)
|
||||
if is_binary(id) do
|
||||
# 13.2)
|
||||
{expanded_id, active, defined} =
|
||||
expand_iri(id, active, false, true, local, defined)
|
||||
{expanded_id, active, defined} = expand_iri(id, active, false, true, local, defined)
|
||||
|
||||
cond do
|
||||
expanded_id == "@context" ->
|
||||
raise JSON.LD.InvalidKeywordAliasError,
|
||||
message: "cannot alias @context"
|
||||
message: "cannot alias @context"
|
||||
|
||||
JSON.LD.keyword?(expanded_id) or
|
||||
IRI.absolute?(expanded_id) or
|
||||
blank_node_id?(expanded_id) ->
|
||||
IRI.absolute?(expanded_id) or
|
||||
blank_node_id?(expanded_id) ->
|
||||
{%TermDefinition{definition | iri_mapping: expanded_id}, active, defined}
|
||||
|
||||
true ->
|
||||
raise JSON.LD.InvalidIRIMappingError,
|
||||
message: "#{inspect id} is not a valid IRI mapping; resulting IRI mapping should be a keyword, absolute IRI or blank node"
|
||||
message:
|
||||
"#{inspect(id)} is not a valid IRI mapping; resulting IRI mapping should be a keyword, absolute IRI or blank node"
|
||||
end
|
||||
else # 13.1)
|
||||
else
|
||||
raise JSON.LD.InvalidIRIMappingError,
|
||||
message: "expected value of @id to be a string, but got #{inspect id}"
|
||||
message: "expected value of @id to be a string, but got #{inspect(id)}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -314,59 +382,72 @@ defmodule JSON.LD.Context do
|
|||
case compact_iri_parts(term) do
|
||||
[prefix, suffix] ->
|
||||
prefix_mapping = local[prefix]
|
||||
|
||||
{active, defined} =
|
||||
if prefix_mapping do
|
||||
do_create_term_definition(active, local, prefix, prefix_mapping, defined)
|
||||
else
|
||||
{active, defined}
|
||||
end
|
||||
|
||||
if prefix_def = active.term_defs[prefix] do
|
||||
{%TermDefinition{definition | iri_mapping: prefix_def.iri_mapping <> suffix}, active, defined}
|
||||
{%TermDefinition{definition | iri_mapping: prefix_def.iri_mapping <> suffix}, active,
|
||||
defined}
|
||||
else
|
||||
{%TermDefinition{definition | iri_mapping: term}, active, defined}
|
||||
end
|
||||
nil -> {%TermDefinition{definition | iri_mapping: term}, active, defined}
|
||||
|
||||
nil ->
|
||||
{%TermDefinition{definition | iri_mapping: term}, active, defined}
|
||||
end
|
||||
# 15)
|
||||
|
||||
# 15)
|
||||
else
|
||||
if active.vocab do
|
||||
{%TermDefinition{definition | iri_mapping: active.vocab <> term}, active, defined}
|
||||
else
|
||||
raise JSON.LD.InvalidIRIMappingError,
|
||||
message: "#{inspect term} is not a valid IRI mapping; relative term definition without vocab mapping"
|
||||
message:
|
||||
"#{inspect(term)} is not a valid IRI mapping; relative term definition without vocab mapping"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# 16.1)
|
||||
defp do_create_container_definition(_, %{"@container" => container})
|
||||
when container not in ~w[@list @set @index @language],
|
||||
do: raise JSON.LD.InvalidContainerMappingError,
|
||||
message: "#{inspect container} is not a valid container mapping; @container must be either @list, @set, @index, or @language"
|
||||
when container not in ~w[@list @set @index @language],
|
||||
do:
|
||||
raise(JSON.LD.InvalidContainerMappingError,
|
||||
message:
|
||||
"#{inspect(container)} is not a valid container mapping; @container must be either @list, @set, @index, or @language"
|
||||
)
|
||||
|
||||
# 16.2)
|
||||
defp do_create_container_definition(definition, %{"@container" => container}),
|
||||
do: %TermDefinition{definition | container_mapping: container}
|
||||
|
||||
defp do_create_container_definition(definition, _),
|
||||
do: definition
|
||||
|
||||
|
||||
# 17)
|
||||
defp do_create_language_definition(definition, %{"@language" => language} = value) do
|
||||
unless Map.has_key?(value, "@type") do
|
||||
case language do
|
||||
language when is_binary(language) ->
|
||||
%TermDefinition{definition | language_mapping: String.downcase(language)}
|
||||
|
||||
language when is_nil(language) ->
|
||||
%TermDefinition{definition | language_mapping: nil}
|
||||
|
||||
_ ->
|
||||
raise JSON.LD.InvalidLanguageMappingError,
|
||||
message: "#{inspect language} is not a valid language mapping; @language must be a string or null"
|
||||
message:
|
||||
"#{inspect(language)} is not a valid language mapping; @language must be a string or null"
|
||||
end
|
||||
end
|
||||
end
|
||||
defp do_create_language_definition(definition, _), do: definition
|
||||
|
||||
defp do_create_language_definition(definition, _), do: definition
|
||||
|
||||
@doc """
|
||||
Inverse Context Creation algorithm
|
||||
|
@ -376,60 +457,69 @@ defmodule JSON.LD.Context do
|
|||
def inverse(%JSON.LD.Context{} = context) do
|
||||
# 2) Initialize default language to @none. If the active context has a default language, set default language to it.
|
||||
default_language = context.default_language || "@none"
|
||||
|
||||
# 3) For each key term and value term definition in the active context, ordered by shortest term first (breaking ties by choosing the lexicographically least term)
|
||||
context.term_defs
|
||||
|> Enum.sort_by(fn {term, _} -> String.length(term) end)
|
||||
|> Enum.reduce(%{}, fn ({term, term_def}, result) ->
|
||||
# 3.1) If the term definition is null, term cannot be selected during compaction, so continue to the next term.
|
||||
if term_def do
|
||||
# 3.2) Initialize container to @none. If there is a container mapping in term definition, set container to its associated value.
|
||||
container = term_def.container_mapping || "@none"
|
||||
# 3.3) Initialize iri to the value of the IRI mapping for the term definition.
|
||||
iri = term_def.iri_mapping
|
||||
|> Enum.reduce(%{}, fn {term, term_def}, result ->
|
||||
# 3.1) If the term definition is null, term cannot be selected during compaction, so continue to the next term.
|
||||
if term_def do
|
||||
# 3.2) Initialize container to @none. If there is a container mapping in term definition, set container to its associated value.
|
||||
container = term_def.container_mapping || "@none"
|
||||
# 3.3) Initialize iri to the value of the IRI mapping for the term definition.
|
||||
iri = term_def.iri_mapping
|
||||
|
||||
type_map = get_in(result, [iri, container, "@type"]) || %{}
|
||||
language_map = get_in(result, [iri, container, "@language"]) || %{}
|
||||
type_map = get_in(result, [iri, container, "@type"]) || %{}
|
||||
language_map = get_in(result, [iri, container, "@language"]) || %{}
|
||||
|
||||
{type_map, language_map} =
|
||||
case term_def do
|
||||
# 3.8) If the term definition indicates that the term represents a reverse property
|
||||
%TermDefinition{reverse_property: true} ->
|
||||
{Map.put_new(type_map, "@reverse", term), language_map}
|
||||
# 3.9) Otherwise, if term definition has a type mapping
|
||||
%TermDefinition{type_mapping: type_mapping}
|
||||
when type_mapping != false ->
|
||||
{Map.put_new(type_map, type_mapping, term), language_map}
|
||||
# 3.10) Otherwise, if term definition has a language mapping (might be null)
|
||||
%TermDefinition{language_mapping: language_mapping}
|
||||
when language_mapping != false ->
|
||||
language = language_mapping || "@null"
|
||||
{type_map, Map.put_new(language_map, language, term)}
|
||||
# 3.11) Otherwise
|
||||
_ ->
|
||||
language_map = Map.put_new(language_map, default_language, term)
|
||||
language_map = Map.put_new(language_map, "@none", term)
|
||||
type_map = Map.put_new(type_map, "@none", term)
|
||||
{type_map, language_map}
|
||||
end
|
||||
{type_map, language_map} =
|
||||
case term_def do
|
||||
# 3.8) If the term definition indicates that the term represents a reverse property
|
||||
%TermDefinition{reverse_property: true} ->
|
||||
{Map.put_new(type_map, "@reverse", term), language_map}
|
||||
|
||||
result
|
||||
|> Map.put_new(iri, %{})
|
||||
|> Map.update(iri, %{}, fn container_map ->
|
||||
Map.put container_map, container, %{
|
||||
"@type" => type_map,
|
||||
"@language" => language_map,
|
||||
}
|
||||
end)
|
||||
else
|
||||
result
|
||||
end
|
||||
end)
|
||||
# 3.9) Otherwise, if term definition has a type mapping
|
||||
%TermDefinition{type_mapping: type_mapping}
|
||||
when type_mapping != false ->
|
||||
{Map.put_new(type_map, type_mapping, term), language_map}
|
||||
|
||||
# 3.10) Otherwise, if term definition has a language mapping (might be null)
|
||||
%TermDefinition{language_mapping: language_mapping}
|
||||
when language_mapping != false ->
|
||||
language = language_mapping || "@null"
|
||||
{type_map, Map.put_new(language_map, language, term)}
|
||||
|
||||
# 3.11) Otherwise
|
||||
_ ->
|
||||
language_map = Map.put_new(language_map, default_language, term)
|
||||
language_map = Map.put_new(language_map, "@none", term)
|
||||
type_map = Map.put_new(type_map, "@none", term)
|
||||
{type_map, language_map}
|
||||
end
|
||||
|
||||
result
|
||||
|> Map.put_new(iri, %{})
|
||||
|> Map.update(iri, %{}, fn container_map ->
|
||||
Map.put(container_map, container, %{
|
||||
"@type" => type_map,
|
||||
"@language" => language_map
|
||||
})
|
||||
end)
|
||||
else
|
||||
result
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def empty?(%JSON.LD.Context{term_defs: term_defs, vocab: nil, base_iri: false, default_language: nil})
|
||||
when map_size(term_defs) == 0,
|
||||
do: true
|
||||
def empty?(%JSON.LD.Context{
|
||||
term_defs: term_defs,
|
||||
vocab: nil,
|
||||
base_iri: false,
|
||||
default_language: nil
|
||||
})
|
||||
when map_size(term_defs) == 0,
|
||||
do: true
|
||||
|
||||
def empty?(_),
|
||||
do: false
|
||||
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
defmodule JSON.LD.Context.TermDefinition do
|
||||
defstruct iri_mapping: nil,
|
||||
reverse_property: false,
|
||||
type_mapping: false, language_mapping: false,
|
||||
type_mapping: false,
|
||||
language_mapping: false,
|
||||
container_mapping: nil
|
||||
|
||||
end
|
||||
|
|
|
@ -8,77 +8,93 @@ defmodule JSON.LD.Decoder do
|
|||
alias JSON.LD.NodeIdentifierMap
|
||||
alias RDF.{Dataset, Graph, NS}
|
||||
|
||||
|
||||
@impl RDF.Serialization.Decoder
|
||||
def decode(content, opts \\ []) do
|
||||
with {:ok, json_ld_object} <- parse_json(content),
|
||||
dataset = to_rdf(json_ld_object, opts) do
|
||||
dataset = to_rdf(json_ld_object, opts) do
|
||||
{:ok, dataset}
|
||||
end
|
||||
end
|
||||
|
||||
def to_rdf(element, options \\ %JSON.LD.Options{}) do
|
||||
with options = JSON.LD.Options.new(options) do
|
||||
{:ok, node_id_map} = NodeIdentifierMap.start_link
|
||||
{:ok, node_id_map} = NodeIdentifierMap.start_link()
|
||||
|
||||
try do
|
||||
element
|
||||
|> JSON.LD.expand(options)
|
||||
|> JSON.LD.node_map(node_id_map)
|
||||
|> Enum.sort_by(fn {graph_name, _} -> graph_name end)
|
||||
|> Enum.reduce(Dataset.new, fn ({graph_name, graph}, dataset) ->
|
||||
unless relative_iri?(graph_name) do
|
||||
rdf_graph =
|
||||
graph
|
||||
|> Enum.sort_by(fn {subject, _} -> subject end)
|
||||
|> Enum.reduce(Graph.new, fn ({subject, node}, rdf_graph) ->
|
||||
unless relative_iri?(subject) do
|
||||
node
|
||||
|> Enum.sort_by(fn {property, _} -> property end)
|
||||
|> Enum.reduce(rdf_graph, fn ({property, values}, rdf_graph) ->
|
||||
cond do
|
||||
property == "@type" ->
|
||||
Graph.add rdf_graph,
|
||||
node_to_rdf(subject), RDF.NS.RDF.type,
|
||||
Enum.map(values, &node_to_rdf/1)
|
||||
JSON.LD.keyword?(property) ->
|
||||
rdf_graph
|
||||
not options.produce_generalized_rdf and
|
||||
blank_node_id?(property) ->
|
||||
rdf_graph
|
||||
relative_iri?(property) ->
|
||||
rdf_graph
|
||||
true ->
|
||||
Enum.reduce values, rdf_graph, fn
|
||||
(%{"@list" => list}, rdf_graph) ->
|
||||
with {list_triples, first} <-
|
||||
list_to_rdf(list, node_id_map) do
|
||||
rdf_graph
|
||||
|> Graph.add({node_to_rdf(subject), node_to_rdf(property), first})
|
||||
|> Graph.add(list_triples)
|
||||
end
|
||||
(item, rdf_graph) ->
|
||||
case object_to_rdf(item) do
|
||||
nil -> rdf_graph
|
||||
object ->
|
||||
Graph.add rdf_graph,
|
||||
{node_to_rdf(subject), node_to_rdf(property), object}
|
||||
end
|
||||
end
|
||||
|> Enum.reduce(Dataset.new(), fn {graph_name, graph}, dataset ->
|
||||
unless relative_iri?(graph_name) do
|
||||
rdf_graph =
|
||||
graph
|
||||
|> Enum.sort_by(fn {subject, _} -> subject end)
|
||||
|> Enum.reduce(Graph.new(), fn {subject, node}, rdf_graph ->
|
||||
unless relative_iri?(subject) do
|
||||
node
|
||||
|> Enum.sort_by(fn {property, _} -> property end)
|
||||
|> Enum.reduce(rdf_graph, fn {property, values}, rdf_graph ->
|
||||
cond do
|
||||
property == "@type" ->
|
||||
Graph.add(
|
||||
rdf_graph,
|
||||
node_to_rdf(subject),
|
||||
RDF.NS.RDF.type(),
|
||||
Enum.map(values, &node_to_rdf/1)
|
||||
)
|
||||
|
||||
JSON.LD.keyword?(property) ->
|
||||
rdf_graph
|
||||
|
||||
not options.produce_generalized_rdf and
|
||||
blank_node_id?(property) ->
|
||||
rdf_graph
|
||||
|
||||
relative_iri?(property) ->
|
||||
rdf_graph
|
||||
|
||||
true ->
|
||||
Enum.reduce(values, rdf_graph, fn
|
||||
%{"@list" => list}, rdf_graph ->
|
||||
with {list_triples, first} <-
|
||||
list_to_rdf(list, node_id_map) do
|
||||
rdf_graph
|
||||
|> Graph.add({node_to_rdf(subject), node_to_rdf(property), first})
|
||||
|> Graph.add(list_triples)
|
||||
end
|
||||
end)
|
||||
else
|
||||
rdf_graph
|
||||
end
|
||||
end)
|
||||
if Enum.empty?(rdf_graph) do
|
||||
dataset
|
||||
else
|
||||
Dataset.add(dataset, rdf_graph,
|
||||
if(graph_name == "@default", do: nil, else: graph_name))
|
||||
end
|
||||
else
|
||||
|
||||
item, rdf_graph ->
|
||||
case object_to_rdf(item) do
|
||||
nil ->
|
||||
rdf_graph
|
||||
|
||||
object ->
|
||||
Graph.add(
|
||||
rdf_graph,
|
||||
{node_to_rdf(subject), node_to_rdf(property), object}
|
||||
)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
else
|
||||
rdf_graph
|
||||
end
|
||||
end)
|
||||
|
||||
if Enum.empty?(rdf_graph) do
|
||||
dataset
|
||||
else
|
||||
Dataset.add(
|
||||
dataset,
|
||||
rdf_graph,
|
||||
if(graph_name == "@default", do: nil, else: graph_name)
|
||||
)
|
||||
end
|
||||
else
|
||||
dataset
|
||||
end
|
||||
end)
|
||||
after
|
||||
NodeIdentifierMap.stop(node_id_map)
|
||||
|
@ -95,11 +111,12 @@ defmodule JSON.LD.Decoder do
|
|||
end
|
||||
|
||||
def node_to_rdf(nil), do: nil
|
||||
|
||||
def node_to_rdf(node) do
|
||||
if blank_node_id?(node) do
|
||||
node
|
||||
|> String.trim_leading("_:")
|
||||
|> RDF.bnode
|
||||
|> RDF.bnode()
|
||||
else
|
||||
RDF.uri(node)
|
||||
end
|
||||
|
@ -113,28 +130,49 @@ defmodule JSON.LD.Decoder do
|
|||
|
||||
defp object_to_rdf(%{"@value" => value} = item) do
|
||||
datatype = item["@type"]
|
||||
|
||||
{value, datatype} =
|
||||
cond do
|
||||
is_boolean(value) ->
|
||||
value = value |> RDF.XSD.Boolean.new() |> RDF.XSD.Boolean.canonical() |> RDF.XSD.Boolean.lexical()
|
||||
datatype = if is_nil(datatype), do: NS.XSD.boolean, else: datatype
|
||||
value =
|
||||
value
|
||||
|> RDF.XSD.Boolean.new()
|
||||
|> RDF.XSD.Boolean.canonical()
|
||||
|> RDF.XSD.Boolean.lexical()
|
||||
|
||||
datatype = if is_nil(datatype), do: NS.XSD.boolean(), else: datatype
|
||||
{value, datatype}
|
||||
is_float(value) or (is_number(value) and datatype == to_string(NS.XSD.double)) ->
|
||||
value = value |> RDF.XSD.Double.new() |> RDF.XSD.Double.canonical() |> RDF.XSD.Double.lexical()
|
||||
datatype = if is_nil(datatype), do: NS.XSD.double, else: datatype
|
||||
|
||||
is_float(value) or (is_number(value) and datatype == to_string(NS.XSD.double())) ->
|
||||
value =
|
||||
value
|
||||
|> RDF.XSD.Double.new()
|
||||
|> RDF.XSD.Double.canonical()
|
||||
|> RDF.XSD.Double.lexical()
|
||||
|
||||
datatype = if is_nil(datatype), do: NS.XSD.double(), else: datatype
|
||||
{value, datatype}
|
||||
is_integer(value) or (is_number(value) and datatype == to_string(NS.XSD.integer)) ->
|
||||
value = value |> RDF.XSD.Integer.new() |> RDF.XSD.Integer.canonical() |> RDF.XSD.Integer.lexical()
|
||||
datatype = if is_nil(datatype), do: NS.XSD.integer, else: datatype
|
||||
|
||||
is_integer(value) or (is_number(value) and datatype == to_string(NS.XSD.integer())) ->
|
||||
value =
|
||||
value
|
||||
|> RDF.XSD.Integer.new()
|
||||
|> RDF.XSD.Integer.canonical()
|
||||
|> RDF.XSD.Integer.lexical()
|
||||
|
||||
datatype = if is_nil(datatype), do: NS.XSD.integer(), else: datatype
|
||||
{value, datatype}
|
||||
|
||||
is_nil(datatype) ->
|
||||
datatype =
|
||||
if Map.has_key?(item, "@language") do
|
||||
RDF.langString
|
||||
RDF.langString()
|
||||
else
|
||||
NS.XSD.string
|
||||
NS.XSD.string()
|
||||
end
|
||||
|
||||
{value, datatype}
|
||||
|
||||
true ->
|
||||
{value, datatype}
|
||||
end
|
||||
|
@ -149,54 +187,55 @@ defmodule JSON.LD.Decoder do
|
|||
defp list_to_rdf(list, node_id_map) do
|
||||
{list_triples, first, last} =
|
||||
list
|
||||
|> Enum.reduce({[], nil, nil}, fn (item, {list_triples, first, last}) ->
|
||||
case object_to_rdf(item) do
|
||||
nil -> {list_triples, first, last}
|
||||
object ->
|
||||
with bnode = node_to_rdf(generate_blank_node_id(node_id_map)) do
|
||||
if last do
|
||||
{
|
||||
list_triples ++
|
||||
[{last, RDF.NS.RDF.rest, bnode},
|
||||
{bnode, RDF.NS.RDF.first, object}],
|
||||
first,
|
||||
bnode
|
||||
}
|
||||
else
|
||||
{
|
||||
list_triples ++ [{bnode, RDF.NS.RDF.first, object}],
|
||||
bnode,
|
||||
bnode
|
||||
}
|
||||
end
|
||||
|> Enum.reduce({[], nil, nil}, fn item, {list_triples, first, last} ->
|
||||
case object_to_rdf(item) do
|
||||
nil ->
|
||||
{list_triples, first, last}
|
||||
|
||||
object ->
|
||||
with bnode = node_to_rdf(generate_blank_node_id(node_id_map)) do
|
||||
if last do
|
||||
{
|
||||
list_triples ++
|
||||
[{last, RDF.NS.RDF.rest(), bnode}, {bnode, RDF.NS.RDF.first(), object}],
|
||||
first,
|
||||
bnode
|
||||
}
|
||||
else
|
||||
{
|
||||
list_triples ++ [{bnode, RDF.NS.RDF.first(), object}],
|
||||
bnode,
|
||||
bnode
|
||||
}
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if last do
|
||||
{list_triples ++ [{last, RDF.NS.RDF.rest, RDF.NS.RDF.nil}], first}
|
||||
{list_triples ++ [{last, RDF.NS.RDF.rest(), RDF.NS.RDF.nil()}], first}
|
||||
else
|
||||
{[], RDF.NS.RDF.nil}
|
||||
{[], RDF.NS.RDF.nil()}
|
||||
end
|
||||
end
|
||||
|
||||
# This is a much nicer and faster version, but the blank node numbering is reversed.
|
||||
# Although this isn't relevant, I prefer to be more spec conform (for now).
|
||||
# defp list_to_rdf(list, node_id_map) do
|
||||
# list
|
||||
# |> Enum.reverse
|
||||
# |> Enum.reduce({[], RDF.NS.RDF.nil}, fn (item, {list_triples, last}) ->
|
||||
# case object_to_rdf(item) do
|
||||
# nil -> {list_triples, last}
|
||||
# object ->
|
||||
# with bnode = node_to_rdf(generate_blank_node_id(node_id_map)) do
|
||||
# {
|
||||
# [{bnode, RDF.NS.RDF.first, object},
|
||||
# {bnode, RDF.NS.RDF.rest, last } | list_triples],
|
||||
# bnode
|
||||
# }
|
||||
# end
|
||||
# end
|
||||
# end)
|
||||
# end
|
||||
|
||||
# This is a much nicer and faster version, but the blank node numbering is reversed.
|
||||
# Although this isn't relevant, I prefer to be more spec conform (for now).
|
||||
# defp list_to_rdf(list, node_id_map) do
|
||||
# list
|
||||
# |> Enum.reverse
|
||||
# |> Enum.reduce({[], RDF.NS.RDF.nil}, fn (item, {list_triples, last}) ->
|
||||
# case object_to_rdf(item) do
|
||||
# nil -> {list_triples, last}
|
||||
# object ->
|
||||
# with bnode = node_to_rdf(generate_blank_node_id(node_id_map)) do
|
||||
# {
|
||||
# [{bnode, RDF.NS.RDF.first, object},
|
||||
# {bnode, RDF.NS.RDF.rest, last } | list_triples],
|
||||
# bnode
|
||||
# }
|
||||
# end
|
||||
# end
|
||||
# end)
|
||||
# end
|
||||
end
|
||||
|
|
|
@ -7,5 +7,5 @@ defmodule JSON.LD.DocumentLoader do
|
|||
|
||||
alias JSON.LD.DocumentLoader.RemoteDocument
|
||||
|
||||
@callback load(String.t, JSON.LD.Options.t) :: {:ok, RemoteDocument.t} | {:error, any}
|
||||
@callback load(String.t(), JSON.LD.Options.t()) :: {:ok, RemoteDocument.t()} | {:error, any}
|
||||
end
|
||||
|
|
|
@ -13,6 +13,6 @@ defmodule JSON.LD.DocumentLoader.Default do
|
|||
defp http_get(url) do
|
||||
HTTPoison.get(url, [accept: "application/ld+json"], follow_redirect: true)
|
||||
rescue
|
||||
e -> {:error, "HTTPoison failed: #{inspect e}"}
|
||||
e -> {:error, "HTTPoison failed: #{inspect(e)}"}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
defmodule JSON.LD.DocumentLoader.RemoteDocument do
|
||||
@type t :: %__MODULE__{
|
||||
context_url: String.t,
|
||||
document_url: String.t,
|
||||
document: any
|
||||
}
|
||||
context_url: String.t(),
|
||||
document_url: String.t(),
|
||||
document: any
|
||||
}
|
||||
|
||||
defstruct [:context_url, :document_url, :document]
|
||||
end
|
||||
|
|
|
@ -6,11 +6,11 @@ defmodule JSON.LD.Encoder do
|
|||
|
||||
alias RDF.{IRI, BlankNode, Literal, XSD, NS}
|
||||
|
||||
@rdf_type to_string(RDF.NS.RDF.type)
|
||||
@rdf_nil to_string(RDF.NS.RDF.nil)
|
||||
@rdf_first to_string(RDF.NS.RDF.first)
|
||||
@rdf_rest to_string(RDF.NS.RDF.rest)
|
||||
@rdf_list to_string(RDF.uri(RDF.NS.RDF.List))
|
||||
@rdf_type to_string(RDF.NS.RDF.type())
|
||||
@rdf_nil to_string(RDF.NS.RDF.nil())
|
||||
@rdf_first to_string(RDF.NS.RDF.first())
|
||||
@rdf_rest to_string(RDF.NS.RDF.rest())
|
||||
@rdf_list to_string(RDF.uri(RDF.NS.RDF.List))
|
||||
|
||||
@impl RDF.Serialization.Encoder
|
||||
def encode(data, opts \\ []) do
|
||||
|
@ -38,66 +38,76 @@ defmodule JSON.LD.Encoder do
|
|||
def from_rdf!(%RDF.Dataset{} = dataset, options) do
|
||||
with options = JSON.LD.Options.new(options) do
|
||||
graph_map =
|
||||
Enum.reduce RDF.Dataset.graphs(dataset), %{},
|
||||
fn graph, graph_map ->
|
||||
# 3.1)
|
||||
name = to_string(graph.name || "@default")
|
||||
Enum.reduce(RDF.Dataset.graphs(dataset), %{}, fn graph, graph_map ->
|
||||
# 3.1)
|
||||
name = to_string(graph.name || "@default")
|
||||
|
||||
# 3.3)
|
||||
graph_map =
|
||||
if graph.name && !get_in(graph_map, ["@default", name]) do
|
||||
Map.update graph_map, "@default", %{name => %{"@id" => name}},
|
||||
fn default_graph ->
|
||||
Map.put(default_graph, name, %{"@id" => name})
|
||||
end
|
||||
else
|
||||
graph_map
|
||||
end
|
||||
# 3.3)
|
||||
graph_map =
|
||||
if graph.name && !get_in(graph_map, ["@default", name]) do
|
||||
Map.update(graph_map, "@default", %{name => %{"@id" => name}}, fn default_graph ->
|
||||
Map.put(default_graph, name, %{"@id" => name})
|
||||
end)
|
||||
else
|
||||
graph_map
|
||||
end
|
||||
|
||||
# 3.2 + 3.4)
|
||||
Map.put(graph_map, name,
|
||||
node_map_from_graph(graph, Map.get(graph_map, name, %{}),
|
||||
options.use_native_types, options.use_rdf_type))
|
||||
end
|
||||
# 3.2 + 3.4)
|
||||
Map.put(
|
||||
graph_map,
|
||||
name,
|
||||
node_map_from_graph(
|
||||
graph,
|
||||
Map.get(graph_map, name, %{}),
|
||||
options.use_native_types,
|
||||
options.use_rdf_type
|
||||
)
|
||||
)
|
||||
end)
|
||||
|
||||
# 4)
|
||||
graph_map =
|
||||
Enum.reduce graph_map, %{}, fn ({name, graph_object}, graph_map) ->
|
||||
Enum.reduce(graph_map, %{}, fn {name, graph_object}, graph_map ->
|
||||
Map.put(graph_map, name, convert_list(graph_object))
|
||||
end
|
||||
end)
|
||||
|
||||
# 5+6)
|
||||
Map.get(graph_map, "@default", %{})
|
||||
|> Enum.sort_by(fn {subject, _} -> subject end)
|
||||
|> Enum.reduce([], fn ({subject, node}, result) ->
|
||||
# 6.1)
|
||||
node =
|
||||
if Map.has_key?(graph_map, subject) do
|
||||
Map.put node, "@graph",
|
||||
graph_map[subject]
|
||||
|> Enum.sort_by(fn {s, _} -> s end)
|
||||
|> Enum.reduce([], fn ({_s, n}, graph_nodes) ->
|
||||
n = Map.delete(n, "usages")
|
||||
if map_size(n) == 1 and Map.has_key?(n, "@id") do
|
||||
graph_nodes
|
||||
else
|
||||
[n | graph_nodes]
|
||||
end
|
||||
end)
|
||||
|> Enum.reverse
|
||||
else
|
||||
node
|
||||
end
|
||||
|> Enum.reduce([], fn {subject, node}, result ->
|
||||
# 6.1)
|
||||
node =
|
||||
if Map.has_key?(graph_map, subject) do
|
||||
Map.put(
|
||||
node,
|
||||
"@graph",
|
||||
graph_map[subject]
|
||||
|> Enum.sort_by(fn {s, _} -> s end)
|
||||
|> Enum.reduce([], fn {_s, n}, graph_nodes ->
|
||||
n = Map.delete(n, "usages")
|
||||
|
||||
# 6.2)
|
||||
node = Map.delete(node, "usages")
|
||||
if map_size(node) == 1 and Map.has_key?(node, "@id") do
|
||||
result
|
||||
else
|
||||
[node | result]
|
||||
end
|
||||
end)
|
||||
|> Enum.reverse
|
||||
if map_size(n) == 1 and Map.has_key?(n, "@id") do
|
||||
graph_nodes
|
||||
else
|
||||
[n | graph_nodes]
|
||||
end
|
||||
end)
|
||||
|> Enum.reverse()
|
||||
)
|
||||
else
|
||||
node
|
||||
end
|
||||
|
||||
# 6.2)
|
||||
node = Map.delete(node, "usages")
|
||||
|
||||
if map_size(node) == 1 and Map.has_key?(node, "@id") do
|
||||
result
|
||||
else
|
||||
[node | result]
|
||||
end
|
||||
end)
|
||||
|> Enum.reverse()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -106,30 +116,34 @@ defmodule JSON.LD.Encoder do
|
|||
|
||||
# 3.5)
|
||||
defp node_map_from_graph(graph, current, use_native_types, use_rdf_type) do
|
||||
Enum.reduce(graph, current, fn ({subject, predicate, object}, node_map) ->
|
||||
{subject, predicate, node_object} =
|
||||
{to_string(subject), to_string(predicate), nil}
|
||||
Enum.reduce(graph, current, fn {subject, predicate, object}, node_map ->
|
||||
{subject, predicate, node_object} = {to_string(subject), to_string(predicate), nil}
|
||||
node = Map.get(node_map, subject, %{"@id" => subject})
|
||||
|
||||
{node_object, node_map} =
|
||||
if is_node_object = (match?(%IRI{}, object) || match?(%BlankNode{}, object)) do
|
||||
if is_node_object = match?(%IRI{}, object) || match?(%BlankNode{}, object) do
|
||||
node_object = to_string(object)
|
||||
node_map = Map.put_new(node_map, node_object, %{"@id" => node_object})
|
||||
{node_object, node_map}
|
||||
else
|
||||
{node_object, node_map}
|
||||
end
|
||||
|
||||
{node, node_map} =
|
||||
if is_node_object and !use_rdf_type and predicate == @rdf_type do
|
||||
node = Map.update(node, "@type", [node_object], fn types ->
|
||||
if node_object in types do
|
||||
types
|
||||
else
|
||||
types ++ [node_object]
|
||||
end
|
||||
end)
|
||||
node =
|
||||
Map.update(node, "@type", [node_object], fn types ->
|
||||
if node_object in types do
|
||||
types
|
||||
else
|
||||
types ++ [node_object]
|
||||
end
|
||||
end)
|
||||
|
||||
{node, node_map}
|
||||
else
|
||||
value = rdf_to_object(object, use_native_types)
|
||||
|
||||
node =
|
||||
Map.update(node, predicate, [value], fn objects ->
|
||||
if value in objects do
|
||||
|
@ -138,13 +152,15 @@ defmodule JSON.LD.Encoder do
|
|||
objects ++ [value]
|
||||
end
|
||||
end)
|
||||
|
||||
node_map =
|
||||
if is_node_object do
|
||||
usage = %{
|
||||
"node" => node,
|
||||
"property" => predicate,
|
||||
"value" => value,
|
||||
"node" => node,
|
||||
"property" => predicate,
|
||||
"value" => value
|
||||
}
|
||||
|
||||
Map.update(node_map, node_object, %{"usages" => [usage]}, fn object_node ->
|
||||
Map.update(object_node, "usages", [usage], fn usages ->
|
||||
usages ++ [usage]
|
||||
|
@ -153,8 +169,10 @@ defmodule JSON.LD.Encoder do
|
|||
else
|
||||
node_map
|
||||
end
|
||||
|
||||
{node, node_map}
|
||||
end
|
||||
|
||||
Map.put(node_map, subject, node)
|
||||
end)
|
||||
|> update_node_usages
|
||||
|
@ -163,38 +181,41 @@ defmodule JSON.LD.Encoder do
|
|||
# This function is necessary because we have no references and must update the
|
||||
# node member of the usage maps with later enhanced usages
|
||||
defp update_node_usages(node_map) do
|
||||
Enum.reduce node_map, node_map, fn
|
||||
({subject, %{"usages" => _usages} = _node}, node_map) ->
|
||||
update_in node_map, [subject, "usages"], fn usages ->
|
||||
Enum.map usages, fn usage ->
|
||||
Map.update! usage, "node", fn %{"@id" => subject} ->
|
||||
Enum.reduce(node_map, node_map, fn
|
||||
{subject, %{"usages" => _usages} = _node}, node_map ->
|
||||
update_in(node_map, [subject, "usages"], fn usages ->
|
||||
Enum.map(usages, fn usage ->
|
||||
Map.update!(usage, "node", fn %{"@id" => subject} ->
|
||||
node_map[subject]
|
||||
end
|
||||
end
|
||||
end
|
||||
(_, node_map) -> node_map
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
_, node_map ->
|
||||
node_map
|
||||
end)
|
||||
end
|
||||
|
||||
# This function is necessary because we have no references and use this
|
||||
# instead to update the head by path
|
||||
defp update_head(graph_object, path, old, new) do
|
||||
update_in graph_object, path, fn objects ->
|
||||
Enum.map objects, fn
|
||||
^old -> new
|
||||
update_in(graph_object, path, fn objects ->
|
||||
Enum.map(objects, fn
|
||||
^old -> new
|
||||
current -> current
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
# 4)
|
||||
defp convert_list(%{@rdf_nil => nil_node} = graph_object) do
|
||||
Enum.reduce nil_node["usages"], graph_object,
|
||||
Enum.reduce(
|
||||
nil_node["usages"],
|
||||
graph_object,
|
||||
# 4.3.1)
|
||||
fn (usage, graph_object) ->
|
||||
fn usage, graph_object ->
|
||||
# 4.3.2) + 4.3.3)
|
||||
{list, list_nodes, [subject, property] = head_path, head} =
|
||||
extract_list(usage)
|
||||
{list, list_nodes, [subject, property] = head_path, head} = extract_list(usage)
|
||||
|
||||
# 4.3.4)
|
||||
{skip, list, list_nodes, head_path, head} =
|
||||
|
@ -214,61 +235,84 @@ defmodule JSON.LD.Encoder do
|
|||
else
|
||||
{false, list, list_nodes, head_path, head}
|
||||
end
|
||||
|
||||
if skip do
|
||||
graph_object
|
||||
else
|
||||
graph_object =
|
||||
update_head graph_object, head_path, head,
|
||||
update_head(
|
||||
graph_object,
|
||||
head_path,
|
||||
head,
|
||||
head
|
||||
# 4.3.5)
|
||||
|> Map.delete("@id")
|
||||
# 4.3.6) isn't necessary, since we built the list in reverse order
|
||||
# 4.3.7)
|
||||
|> Map.put("@list", list)
|
||||
)
|
||||
|
||||
# 4.3.8)
|
||||
Enum.reduce(list_nodes, graph_object, fn (node_id, graph_object) ->
|
||||
Enum.reduce(list_nodes, graph_object, fn node_id, graph_object ->
|
||||
Map.delete(graph_object, node_id)
|
||||
end)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
defp convert_list(graph_object), do: graph_object
|
||||
|
||||
|
||||
# 4.3.3)
|
||||
defp extract_list(usage, list \\ [], list_nodes \\ [])
|
||||
|
||||
defp extract_list(
|
||||
%{"node" => %{
|
||||
# Spec FIXME: no mention of @id
|
||||
"@id" => id = ("_:" <> _), # contrary to spec we assume/require this to be even on the initial call to be a blank node
|
||||
"usages" => [usage],
|
||||
@rdf_first => [first],
|
||||
@rdf_rest => [_rest],
|
||||
} = node,
|
||||
"property" => @rdf_rest}, list, list_nodes) when map_size(node) == 4 do
|
||||
%{
|
||||
"node" =>
|
||||
%{
|
||||
# Spec FIXME: no mention of @id
|
||||
# contrary to spec we assume/require this to be even on the initial call to be a blank node
|
||||
"@id" => id = "_:" <> _,
|
||||
"usages" => [usage],
|
||||
@rdf_first => [first],
|
||||
@rdf_rest => [_rest]
|
||||
} = node,
|
||||
"property" => @rdf_rest
|
||||
},
|
||||
list,
|
||||
list_nodes
|
||||
)
|
||||
when map_size(node) == 4 do
|
||||
extract_list(usage, [first | list], [id | list_nodes])
|
||||
end
|
||||
|
||||
defp extract_list(
|
||||
%{"node" => %{
|
||||
# Spec FIXME: no mention of @id
|
||||
"@id" => id = ("_:" <> _), # contrary to spec we assume/require this to be even on the initial call to be a blank node
|
||||
"@type" => [@rdf_list],
|
||||
"usages" => [usage],
|
||||
@rdf_first => [first],
|
||||
@rdf_rest => [_rest],
|
||||
} = node,
|
||||
"property" => @rdf_rest}, list, list_nodes) when map_size(node) == 5 do
|
||||
%{
|
||||
"node" =>
|
||||
%{
|
||||
# Spec FIXME: no mention of @id
|
||||
# contrary to spec we assume/require this to be even on the initial call to be a blank node
|
||||
"@id" => id = "_:" <> _,
|
||||
"@type" => [@rdf_list],
|
||||
"usages" => [usage],
|
||||
@rdf_first => [first],
|
||||
@rdf_rest => [_rest]
|
||||
} = node,
|
||||
"property" => @rdf_rest
|
||||
},
|
||||
list,
|
||||
list_nodes
|
||||
)
|
||||
when map_size(node) == 5 do
|
||||
extract_list(usage, [first | list], [id | list_nodes])
|
||||
end
|
||||
|
||||
defp extract_list(%{"node" => %{"@id" => subject}, "property" => property, "value" => head},
|
||||
list, list_nodes),
|
||||
do: {list, list_nodes, [subject, property], head}
|
||||
|
||||
defp extract_list(
|
||||
%{"node" => %{"@id" => subject}, "property" => property, "value" => head},
|
||||
list,
|
||||
list_nodes
|
||||
),
|
||||
do: {list, list_nodes, [subject, property], head}
|
||||
|
||||
defp rdf_to_object(%IRI{} = iri, _use_native_types) do
|
||||
%{"@id" => to_string(iri)}
|
||||
|
@ -283,23 +327,27 @@ defmodule JSON.LD.Encoder do
|
|||
value = Literal.value(literal)
|
||||
converted_value = literal
|
||||
type = nil
|
||||
|
||||
{converted_value, type, result} =
|
||||
if use_native_types do
|
||||
cond do
|
||||
datatype == XSD.String ->
|
||||
{value, type, result}
|
||||
|
||||
datatype == XSD.Boolean ->
|
||||
if RDF.XSD.Boolean.valid?(literal) do
|
||||
{value, type, result}
|
||||
else
|
||||
{converted_value, NS.XSD.boolean, result}
|
||||
{converted_value, NS.XSD.boolean(), result}
|
||||
end
|
||||
|
||||
datatype in [XSD.Integer, XSD.Double] ->
|
||||
if Literal.valid?(literal) do
|
||||
{value, type, result}
|
||||
else
|
||||
{converted_value, type, result}
|
||||
end
|
||||
|
||||
true ->
|
||||
{converted_value, Literal.datatype_id(literal), result}
|
||||
end
|
||||
|
@ -307,18 +355,23 @@ defmodule JSON.LD.Encoder do
|
|||
cond do
|
||||
datatype == RDF.LangString ->
|
||||
{converted_value, type, Map.put(result, "@language", Literal.language(literal))}
|
||||
|
||||
datatype == XSD.String ->
|
||||
{converted_value, type, result}
|
||||
|
||||
true ->
|
||||
{Literal.lexical(literal), Literal.datatype_id(literal), result}
|
||||
end
|
||||
end
|
||||
|
||||
result = type && Map.put(result, "@type", to_string(type)) || result
|
||||
Map.put(result, "@value",
|
||||
match?(%Literal{}, converted_value) && Literal.lexical(converted_value) || converted_value)
|
||||
end
|
||||
result = (type && Map.put(result, "@type", to_string(type))) || result
|
||||
|
||||
Map.put(
|
||||
result,
|
||||
"@value",
|
||||
(match?(%Literal{}, converted_value) && Literal.lexical(converted_value)) || converted_value
|
||||
)
|
||||
end
|
||||
|
||||
defp encode_json(value, opts) do
|
||||
Jason.encode(value, opts)
|
||||
|
|
|
@ -187,7 +187,6 @@ defmodule JSON.LD.InvalidLanguageTaggedStringError do
|
|||
defexception code: "invalid language-tagged string", message: nil
|
||||
end
|
||||
|
||||
|
||||
defmodule JSON.LD.InvalidLanguageTaggedValueError do
|
||||
@moduledoc """
|
||||
A number, true, or false with an associated language tag was detected.
|
||||
|
|
|
@ -3,17 +3,17 @@ defmodule JSON.LD.Expansion do
|
|||
|
||||
import JSON.LD.{IRIExpansion, Utils}
|
||||
|
||||
|
||||
def expand(input, options \\ %JSON.LD.Options{}) do
|
||||
with options = JSON.LD.Options.new(options),
|
||||
active_context = JSON.LD.Context.new(options)
|
||||
do
|
||||
with options = JSON.LD.Options.new(options),
|
||||
active_context = JSON.LD.Context.new(options) do
|
||||
active_context =
|
||||
case options.expand_context do
|
||||
%{"@context" => context} ->
|
||||
JSON.LD.Context.update(active_context, context)
|
||||
|
||||
%{} = context ->
|
||||
JSON.LD.Context.update(active_context, context)
|
||||
|
||||
nil ->
|
||||
active_context
|
||||
end
|
||||
|
@ -21,11 +21,15 @@ defmodule JSON.LD.Expansion do
|
|||
case do_expand(active_context, nil, input, options) do
|
||||
result = %{"@graph" => graph} when map_size(result) == 1 ->
|
||||
graph
|
||||
|
||||
nil ->
|
||||
[]
|
||||
|
||||
result when not is_list(result) ->
|
||||
[result]
|
||||
result -> result
|
||||
|
||||
result ->
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -37,7 +41,7 @@ defmodule JSON.LD.Expansion do
|
|||
|
||||
# 2) If element is a scalar, ...
|
||||
defp do_expand(active_context, active_property, element, _options)
|
||||
when is_binary(element) or is_number(element) or is_boolean(element) do
|
||||
when is_binary(element) or is_number(element) or is_boolean(element) do
|
||||
if active_property in [nil, "@graph"] do
|
||||
nil
|
||||
else
|
||||
|
@ -47,29 +51,37 @@ defmodule JSON.LD.Expansion do
|
|||
|
||||
# 3) If element is an array, ...
|
||||
defp do_expand(active_context, active_property, element, options)
|
||||
when is_list(element) do
|
||||
when is_list(element) do
|
||||
term_def = active_context.term_defs[active_property]
|
||||
container_mapping = term_def && term_def.container_mapping
|
||||
|
||||
element
|
||||
|> Enum.reduce([], fn (item, result) ->
|
||||
expanded_item = do_expand(active_context, active_property, item, options)
|
||||
if (active_property == "@list" or container_mapping == "@list") and
|
||||
(is_list(expanded_item) or Map.has_key?(expanded_item, "@list")),
|
||||
do: raise JSON.LD.ListOfListsError,
|
||||
message: "List of lists in #{inspect element}"
|
||||
case expanded_item do
|
||||
nil -> result
|
||||
list when is_list(list) ->
|
||||
result ++ list
|
||||
expanded_item ->
|
||||
result ++ [expanded_item]
|
||||
end
|
||||
|> Enum.reduce([], fn item, result ->
|
||||
expanded_item = do_expand(active_context, active_property, item, options)
|
||||
|
||||
if (active_property == "@list" or container_mapping == "@list") and
|
||||
(is_list(expanded_item) or Map.has_key?(expanded_item, "@list")),
|
||||
do:
|
||||
raise(JSON.LD.ListOfListsError,
|
||||
message: "List of lists in #{inspect(element)}"
|
||||
)
|
||||
|
||||
case expanded_item do
|
||||
nil ->
|
||||
result
|
||||
|
||||
list when is_list(list) ->
|
||||
result ++ list
|
||||
|
||||
expanded_item ->
|
||||
result ++ [expanded_item]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
# 4) - 13)
|
||||
defp do_expand(active_context, active_property, element, options)
|
||||
when is_map(element) do
|
||||
when is_map(element) do
|
||||
# 5)
|
||||
active_context =
|
||||
if Map.has_key?(element, "@context") do
|
||||
|
@ -77,313 +89,433 @@ defmodule JSON.LD.Expansion do
|
|||
else
|
||||
active_context
|
||||
end
|
||||
|
||||
# 6) and 7)
|
||||
result = element
|
||||
|> Enum.sort_by(fn {key, _} -> key end)
|
||||
|> Enum.reduce(%{}, fn ({key, value}, result) ->
|
||||
if key != "@context" do # 7.1)
|
||||
expanded_property = expand_iri(key, active_context, false, true)
|
||||
if expanded_property && # 7.2)
|
||||
(String.contains?(expanded_property, ":") || JSON.LD.keyword?(expanded_property)) do # 7.3)
|
||||
if JSON.LD.keyword?(expanded_property) do # 7.4)
|
||||
if active_property == "@reverse", # 7.4.1)
|
||||
do: raise JSON.LD.InvalidReversePropertyMapError,
|
||||
message: "An invalid reverse property map has been detected. No keywords apart from @context are allowed in reverse property maps."
|
||||
if Map.has_key?(result, expanded_property), # 7.4.2)
|
||||
do: raise JSON.LD.CollidingKeywordsError,
|
||||
message: "Two properties which expand to the same keyword have been detected. This might occur if a keyword and an alias thereof are used at the same time."
|
||||
result =
|
||||
element
|
||||
|> Enum.sort_by(fn {key, _} -> key end)
|
||||
|> Enum.reduce(%{}, fn {key, value}, result ->
|
||||
# 7.1)
|
||||
if key != "@context" do
|
||||
expanded_property = expand_iri(key, active_context, false, true)
|
||||
# 7.2)
|
||||
# 7.3)
|
||||
if expanded_property &&
|
||||
(String.contains?(expanded_property, ":") || JSON.LD.keyword?(expanded_property)) do
|
||||
# 7.4)
|
||||
# expanded_property is not a keyword
|
||||
if JSON.LD.keyword?(expanded_property) do
|
||||
# 7.4.1)
|
||||
if active_property == "@reverse",
|
||||
do:
|
||||
raise(JSON.LD.InvalidReversePropertyMapError,
|
||||
message:
|
||||
"An invalid reverse property map has been detected. No keywords apart from @context are allowed in reverse property maps."
|
||||
)
|
||||
|
||||
expanded_value = case expanded_property do
|
||||
"@id" -> # 7.4.3)
|
||||
if is_binary(value) do
|
||||
expand_iri(value, active_context, true)
|
||||
else
|
||||
raise JSON.LD.InvalidIdValueError,
|
||||
message: "#{inspect value} is not a valid @id value"
|
||||
end
|
||||
"@type" -> # 7.4.4)
|
||||
cond do
|
||||
is_binary(value) ->
|
||||
expand_iri(value, active_context, true, true)
|
||||
is_list(value) and Enum.all?(value, &is_binary/1) ->
|
||||
Enum.map value, fn item ->
|
||||
expand_iri(item, active_context, true, true) end
|
||||
true ->
|
||||
raise JSON.LD.InvalidTypeValueError,
|
||||
message: "#{inspect value} is not a valid @type value"
|
||||
end
|
||||
"@graph" -> # 7.4.5)
|
||||
do_expand(active_context, "@graph", value, options)
|
||||
"@value" -> # 7.4.6)
|
||||
if scalar?(value) or is_nil(value) do
|
||||
if is_nil(value) do
|
||||
{:skip, Map.put(result, "@value", nil)}
|
||||
else
|
||||
value
|
||||
end
|
||||
else
|
||||
raise JSON.LD.InvalidValueObjectValueError,
|
||||
message: "#{inspect value} is not a valid value for the @value member of a value object; neither a scalar nor null"
|
||||
end
|
||||
"@language" -> # 7.4.7)
|
||||
if is_binary(value),
|
||||
do: String.downcase(value),
|
||||
else: raise JSON.LD.InvalidLanguageTaggedStringError,
|
||||
message: "#{inspect value} is not a valid language-tag"
|
||||
"@index" -> # 7.4.8)
|
||||
if is_binary(value),
|
||||
do: value,
|
||||
else: raise JSON.LD.InvalidIndexValueError,
|
||||
message: "#{inspect value} is not a valid @index value"
|
||||
"@list" -> # 7.4.9)
|
||||
if active_property in [nil, "@graph"] do # 7.4.9.1)
|
||||
{:skip, result}
|
||||
else
|
||||
value = do_expand(active_context, active_property, value, options)
|
||||
# 7.4.2)
|
||||
if Map.has_key?(result, expanded_property),
|
||||
do:
|
||||
raise(JSON.LD.CollidingKeywordsError,
|
||||
message:
|
||||
"Two properties which expand to the same keyword have been detected. This might occur if a keyword and an alias thereof are used at the same time."
|
||||
)
|
||||
|
||||
# Spec FIXME: need to be sure that result is a list [from RDF.rb implementation]
|
||||
value = if is_list(value),
|
||||
do: value,
|
||||
else: [value]
|
||||
|
||||
# If expanded value is a list object, a list of lists error has been detected and processing is aborted.
|
||||
# Spec FIXME: Also look at each object if result is a list [from RDF.rb implementation]
|
||||
if Enum.any?(value, fn v -> Map.has_key?(v, "@list") end),
|
||||
do: raise JSON.LD.ListOfListsError,
|
||||
message: "List of lists in #{inspect value}"
|
||||
value
|
||||
end
|
||||
"@set" -> # 7.4.10)
|
||||
do_expand(active_context, active_property, value, options)
|
||||
"@reverse" -> # 7.4.11)
|
||||
unless is_map(value),
|
||||
do: raise JSON.LD.InvalidReverseValueError,
|
||||
message: "#{inspect value} is not a valid @reverse value"
|
||||
expanded_value = do_expand(active_context, "@reverse", value, options) # 7.4.11.1)
|
||||
new_result =
|
||||
if Map.has_key?(expanded_value, "@reverse") do # 7.4.11.2) If expanded value contains an @reverse member, i.e., properties that are reversed twice, execute for each of its property and item the following steps:
|
||||
Enum.reduce expanded_value["@reverse"], result,
|
||||
fn ({property, item}, new_result) ->
|
||||
items = if is_list(item),
|
||||
do: item,
|
||||
else: [item]
|
||||
Map.update(new_result, property, items, fn members ->
|
||||
members ++ items
|
||||
end)
|
||||
expanded_value =
|
||||
case expanded_property do
|
||||
# 7.4.3)
|
||||
"@id" ->
|
||||
if is_binary(value) do
|
||||
expand_iri(value, active_context, true)
|
||||
else
|
||||
raise JSON.LD.InvalidIdValueError,
|
||||
message: "#{inspect(value)} is not a valid @id value"
|
||||
end
|
||||
else
|
||||
result
|
||||
end
|
||||
new_result =
|
||||
if Map.keys(expanded_value) != ["@reverse"] do # 7.4.11.3)
|
||||
reverse_map =
|
||||
Enum.reduce expanded_value, Map.get(new_result, "@reverse", %{}), fn
|
||||
({property, items}, reverse_map) when property != "@reverse" ->
|
||||
Enum.each(items, fn item ->
|
||||
if Map.has_key?(item, "@value") or Map.has_key?(item, "@list"),
|
||||
do: raise JSON.LD.InvalidReversePropertyValueError,
|
||||
message: "invalid value for a reverse property in #{inspect item}"
|
||||
end)
|
||||
Map.update(reverse_map, property, items, fn members ->
|
||||
|
||||
# 7.4.4)
|
||||
"@type" ->
|
||||
cond do
|
||||
is_binary(value) ->
|
||||
expand_iri(value, active_context, true, true)
|
||||
|
||||
is_list(value) and Enum.all?(value, &is_binary/1) ->
|
||||
Enum.map(value, fn item ->
|
||||
expand_iri(item, active_context, true, true)
|
||||
end)
|
||||
|
||||
true ->
|
||||
raise JSON.LD.InvalidTypeValueError,
|
||||
message: "#{inspect(value)} is not a valid @type value"
|
||||
end
|
||||
|
||||
# 7.4.5)
|
||||
"@graph" ->
|
||||
do_expand(active_context, "@graph", value, options)
|
||||
|
||||
# 7.4.6)
|
||||
"@value" ->
|
||||
if scalar?(value) or is_nil(value) do
|
||||
if is_nil(value) do
|
||||
{:skip, Map.put(result, "@value", nil)}
|
||||
else
|
||||
value
|
||||
end
|
||||
else
|
||||
raise JSON.LD.InvalidValueObjectValueError,
|
||||
message:
|
||||
"#{inspect(value)} is not a valid value for the @value member of a value object; neither a scalar nor null"
|
||||
end
|
||||
|
||||
# 7.4.7)
|
||||
"@language" ->
|
||||
if is_binary(value),
|
||||
do: String.downcase(value),
|
||||
else:
|
||||
raise(JSON.LD.InvalidLanguageTaggedStringError,
|
||||
message: "#{inspect(value)} is not a valid language-tag"
|
||||
)
|
||||
|
||||
# 7.4.8)
|
||||
"@index" ->
|
||||
if is_binary(value),
|
||||
do: value,
|
||||
else:
|
||||
raise(JSON.LD.InvalidIndexValueError,
|
||||
message: "#{inspect(value)} is not a valid @index value"
|
||||
)
|
||||
|
||||
# 7.4.9)
|
||||
"@list" ->
|
||||
# 7.4.9.1)
|
||||
if active_property in [nil, "@graph"] do
|
||||
{:skip, result}
|
||||
else
|
||||
value = do_expand(active_context, active_property, value, options)
|
||||
|
||||
# Spec FIXME: need to be sure that result is a list [from RDF.rb implementation]
|
||||
value =
|
||||
if is_list(value),
|
||||
do: value,
|
||||
else: [value]
|
||||
|
||||
# If expanded value is a list object, a list of lists error has been detected and processing is aborted.
|
||||
# Spec FIXME: Also look at each object if result is a list [from RDF.rb implementation]
|
||||
if Enum.any?(value, fn v -> Map.has_key?(v, "@list") end),
|
||||
do:
|
||||
raise(JSON.LD.ListOfListsError,
|
||||
message: "List of lists in #{inspect(value)}"
|
||||
)
|
||||
|
||||
value
|
||||
end
|
||||
|
||||
# 7.4.10)
|
||||
"@set" ->
|
||||
do_expand(active_context, active_property, value, options)
|
||||
|
||||
# 7.4.11)
|
||||
"@reverse" ->
|
||||
unless is_map(value),
|
||||
do:
|
||||
raise(JSON.LD.InvalidReverseValueError,
|
||||
message: "#{inspect(value)} is not a valid @reverse value"
|
||||
)
|
||||
|
||||
# 7.4.11.1)
|
||||
expanded_value = do_expand(active_context, "@reverse", value, options)
|
||||
|
||||
# 7.4.11.2) If expanded value contains an @reverse member, i.e., properties that are reversed twice, execute for each of its property and item the following steps:
|
||||
new_result =
|
||||
if Map.has_key?(expanded_value, "@reverse") do
|
||||
Enum.reduce(expanded_value["@reverse"], result, fn {property, item},
|
||||
new_result ->
|
||||
items =
|
||||
if is_list(item),
|
||||
do: item,
|
||||
else: [item]
|
||||
|
||||
Map.update(new_result, property, items, fn members ->
|
||||
members ++ items
|
||||
end)
|
||||
(_, reverse_map) -> reverse_map
|
||||
end)
|
||||
else
|
||||
result
|
||||
end
|
||||
Map.put(new_result, "@reverse", reverse_map)
|
||||
else
|
||||
new_result
|
||||
end
|
||||
{:skip, new_result}
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
# 7.4.12)
|
||||
case expanded_value do
|
||||
nil ->
|
||||
result
|
||||
{:skip, new_result} ->
|
||||
new_result
|
||||
expanded_value ->
|
||||
Map.put(result, expanded_property, expanded_value)
|
||||
end
|
||||
|
||||
else # expanded_property is not a keyword
|
||||
term_def = active_context.term_defs[key]
|
||||
expanded_value = cond do
|
||||
# 7.5) Otherwise, if key's container mapping in active context is @language and value is a JSON object then value is expanded from a language map as follows:
|
||||
is_map(value) && term_def && term_def.container_mapping == "@language" ->
|
||||
value
|
||||
|> Enum.sort_by(fn {language, _} -> language end)
|
||||
|> Enum.reduce([], fn ({language, language_value}, language_map_result) ->
|
||||
language_map_result ++ (
|
||||
if(is_list(language_value),
|
||||
do: language_value,
|
||||
else: [language_value])
|
||||
|> Enum.map(fn
|
||||
item when is_binary(item) ->
|
||||
%{
|
||||
"@value" => item,
|
||||
"@language" => String.downcase(language)
|
||||
}
|
||||
item ->
|
||||
raise JSON.LD.InvalidLanguageMapValueError,
|
||||
message: "#{inspect item} is not a valid language map value"
|
||||
# 7.4.11.3)
|
||||
new_result =
|
||||
if Map.keys(expanded_value) != ["@reverse"] do
|
||||
reverse_map =
|
||||
Enum.reduce(expanded_value, Map.get(new_result, "@reverse", %{}), fn
|
||||
{property, items}, reverse_map when property != "@reverse" ->
|
||||
Enum.each(items, fn item ->
|
||||
if Map.has_key?(item, "@value") or Map.has_key?(item, "@list"),
|
||||
do:
|
||||
raise(JSON.LD.InvalidReversePropertyValueError,
|
||||
message:
|
||||
"invalid value for a reverse property in #{inspect(item)}"
|
||||
)
|
||||
end)
|
||||
|
||||
Map.update(reverse_map, property, items, fn members ->
|
||||
members ++ items
|
||||
end)
|
||||
|
||||
_, reverse_map ->
|
||||
reverse_map
|
||||
end)
|
||||
)
|
||||
end)
|
||||
# 7.6)
|
||||
is_map(value) && term_def && term_def.container_mapping == "@index" ->
|
||||
value
|
||||
|> Enum.sort_by(fn {index, _} -> index end)
|
||||
|> Enum.reduce([], fn ({index, index_value}, index_map_result) ->
|
||||
index_map_result ++ (
|
||||
index_value = if(is_list(index_value),
|
||||
do: index_value,
|
||||
else: [index_value])
|
||||
index_value = do_expand(active_context, key, index_value, options)
|
||||
Enum.map(index_value, fn item ->
|
||||
Map.put_new(item, "@index", index)
|
||||
end)
|
||||
)
|
||||
end)
|
||||
# 7.7)
|
||||
true ->
|
||||
do_expand(active_context, key, value, options)
|
||||
end
|
||||
# 7.8)
|
||||
if is_nil(expanded_value) do
|
||||
result
|
||||
else
|
||||
# 7.9)
|
||||
expanded_value =
|
||||
if (term_def && term_def.container_mapping == "@list") &&
|
||||
!(is_map(expanded_value) && Map.has_key?(expanded_value, "@list")) do
|
||||
%{"@list" =>
|
||||
(if is_list(expanded_value),
|
||||
do: expanded_value,
|
||||
else: [expanded_value])}
|
||||
else
|
||||
expanded_value
|
||||
|
||||
Map.put(new_result, "@reverse", reverse_map)
|
||||
else
|
||||
new_result
|
||||
end
|
||||
|
||||
{:skip, new_result}
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
|
||||
# 7.4.12)
|
||||
case expanded_value do
|
||||
nil ->
|
||||
result
|
||||
|
||||
{:skip, new_result} ->
|
||||
new_result
|
||||
|
||||
expanded_value ->
|
||||
Map.put(result, expanded_property, expanded_value)
|
||||
end
|
||||
else
|
||||
term_def = active_context.term_defs[key]
|
||||
|
||||
expanded_value =
|
||||
cond do
|
||||
# 7.5) Otherwise, if key's container mapping in active context is @language and value is a JSON object then value is expanded from a language map as follows:
|
||||
is_map(value) && term_def && term_def.container_mapping == "@language" ->
|
||||
value
|
||||
|> Enum.sort_by(fn {language, _} -> language end)
|
||||
|> Enum.reduce([], fn {language, language_value}, language_map_result ->
|
||||
language_map_result ++
|
||||
(if(is_list(language_value),
|
||||
do: language_value,
|
||||
else: [language_value]
|
||||
)
|
||||
|> Enum.map(fn
|
||||
item when is_binary(item) ->
|
||||
%{
|
||||
"@value" => item,
|
||||
"@language" => String.downcase(language)
|
||||
}
|
||||
|
||||
item ->
|
||||
raise JSON.LD.InvalidLanguageMapValueError,
|
||||
message: "#{inspect(item)} is not a valid language map value"
|
||||
end))
|
||||
end)
|
||||
|
||||
# 7.6)
|
||||
is_map(value) && term_def && term_def.container_mapping == "@index" ->
|
||||
value
|
||||
|> Enum.sort_by(fn {index, _} -> index end)
|
||||
|> Enum.reduce([], fn {index, index_value}, index_map_result ->
|
||||
index_map_result ++
|
||||
(
|
||||
index_value =
|
||||
if(is_list(index_value),
|
||||
do: index_value,
|
||||
else: [index_value]
|
||||
)
|
||||
|
||||
index_value = do_expand(active_context, key, index_value, options)
|
||||
|
||||
Enum.map(index_value, fn item ->
|
||||
Map.put_new(item, "@index", index)
|
||||
end)
|
||||
)
|
||||
end)
|
||||
|
||||
# 7.7)
|
||||
true ->
|
||||
do_expand(active_context, key, value, options)
|
||||
end
|
||||
|
||||
# 7.8)
|
||||
if is_nil(expanded_value) do
|
||||
result
|
||||
else
|
||||
# 7.9)
|
||||
expanded_value =
|
||||
if term_def && term_def.container_mapping == "@list" &&
|
||||
!(is_map(expanded_value) && Map.has_key?(expanded_value, "@list")) do
|
||||
%{
|
||||
"@list" =>
|
||||
if(is_list(expanded_value),
|
||||
do: expanded_value,
|
||||
else: [expanded_value]
|
||||
)
|
||||
}
|
||||
else
|
||||
expanded_value
|
||||
end
|
||||
|
||||
# 7.10) Otherwise, if the term definition associated to key indicates that it is a reverse property
|
||||
# Spec FIXME: this is not an otherwise [from RDF.rb implementation]
|
||||
# 7.11)
|
||||
if term_def && term_def.reverse_property do
|
||||
reverse_map = Map.get(result, "@reverse", %{})
|
||||
|
||||
reverse_map =
|
||||
if(is_list(expanded_value),
|
||||
do: expanded_value,
|
||||
else: [expanded_value]
|
||||
)
|
||||
|> Enum.reduce(reverse_map, fn item, reverse_map ->
|
||||
if Map.has_key?(item, "@value") or Map.has_key?(item, "@list"),
|
||||
do:
|
||||
raise(JSON.LD.InvalidReversePropertyValueError,
|
||||
message: "invalid value for a reverse property in #{inspect(item)}"
|
||||
)
|
||||
|
||||
Map.update(reverse_map, expanded_property, [item], fn members ->
|
||||
members ++ [item]
|
||||
end)
|
||||
end)
|
||||
|
||||
Map.put(result, "@reverse", reverse_map)
|
||||
else
|
||||
expanded_value =
|
||||
if is_list(expanded_value),
|
||||
do: expanded_value,
|
||||
else: [expanded_value]
|
||||
|
||||
Map.update(result, expanded_property, expanded_value, fn values ->
|
||||
expanded_value ++ values
|
||||
end)
|
||||
end
|
||||
# 7.10) Otherwise, if the term definition associated to key indicates that it is a reverse property
|
||||
# Spec FIXME: this is not an otherwise [from RDF.rb implementation]
|
||||
if term_def && term_def.reverse_property do
|
||||
reverse_map = Map.get(result, "@reverse", %{})
|
||||
reverse_map =
|
||||
if(is_list(expanded_value),
|
||||
do: expanded_value,
|
||||
else: [expanded_value])
|
||||
|> Enum.reduce(reverse_map, fn (item, reverse_map) ->
|
||||
if Map.has_key?(item, "@value") or Map.has_key?(item, "@list"),
|
||||
do: raise JSON.LD.InvalidReversePropertyValueError,
|
||||
message: "invalid value for a reverse property in #{inspect item}"
|
||||
Map.update reverse_map, expanded_property, [item], fn members ->
|
||||
members ++ [item]
|
||||
end
|
||||
end)
|
||||
Map.put(result, "@reverse", reverse_map)
|
||||
else # 7.11)
|
||||
expanded_value = if is_list(expanded_value),
|
||||
do: expanded_value,
|
||||
else: [expanded_value]
|
||||
Map.update result, expanded_property, expanded_value,
|
||||
fn values -> expanded_value ++ values end
|
||||
end
|
||||
end
|
||||
else
|
||||
result
|
||||
end
|
||||
else
|
||||
result
|
||||
end
|
||||
else
|
||||
result
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
result = case result do
|
||||
# 8)
|
||||
%{"@value" => value} ->
|
||||
with keys = Map.keys(result) do # 8.1)
|
||||
if Enum.any?(keys, &(&1 not in ~w[@value @language @type @index])) ||
|
||||
("@language" in keys and "@type" in keys) do
|
||||
raise JSON.LD.InvalidValueObjectError,
|
||||
message: "value object with disallowed members"
|
||||
result =
|
||||
case result do
|
||||
# 8)
|
||||
%{"@value" => value} ->
|
||||
# 8.1)
|
||||
with keys = Map.keys(result) do
|
||||
if Enum.any?(keys, &(&1 not in ~w[@value @language @type @index])) ||
|
||||
("@language" in keys and "@type" in keys) do
|
||||
raise JSON.LD.InvalidValueObjectError,
|
||||
message: "value object with disallowed members"
|
||||
end
|
||||
end
|
||||
end
|
||||
cond do
|
||||
value == nil -> nil # 8.2)
|
||||
!is_binary(value) and Map.has_key?(result, "@language") -> # 8.3)
|
||||
raise JSON.LD.InvalidLanguageTaggedValueError,
|
||||
message: "@value '#{inspect value}' is tagged with a language"
|
||||
(type = result["@type"]) && !RDF.uri?(type) -> # 8.4)
|
||||
raise JSON.LD.InvalidTypedValueError,
|
||||
message: "@value '#{inspect value}' has invalid type #{inspect type}"
|
||||
true -> result
|
||||
end
|
||||
# 9)
|
||||
%{"@type" => type} when not is_list(type) ->
|
||||
Map.put(result, "@type", [type])
|
||||
# 10)
|
||||
%{"@set" => set} ->
|
||||
validate_set_or_list_object(result)
|
||||
set
|
||||
%{"@list" => _} ->
|
||||
validate_set_or_list_object(result)
|
||||
result
|
||||
_ -> result
|
||||
end
|
||||
|
||||
cond do
|
||||
# 8.2)
|
||||
value == nil ->
|
||||
nil
|
||||
|
||||
# 8.3)
|
||||
!is_binary(value) and Map.has_key?(result, "@language") ->
|
||||
raise JSON.LD.InvalidLanguageTaggedValueError,
|
||||
message: "@value '#{inspect(value)}' is tagged with a language"
|
||||
|
||||
# 8.4)
|
||||
(type = result["@type"]) && !RDF.uri?(type) ->
|
||||
raise JSON.LD.InvalidTypedValueError,
|
||||
message: "@value '#{inspect(value)}' has invalid type #{inspect(type)}"
|
||||
|
||||
true ->
|
||||
result
|
||||
end
|
||||
|
||||
# 9)
|
||||
%{"@type" => type} when not is_list(type) ->
|
||||
Map.put(result, "@type", [type])
|
||||
|
||||
# 10)
|
||||
%{"@set" => set} ->
|
||||
validate_set_or_list_object(result)
|
||||
set
|
||||
|
||||
%{"@list" => _} ->
|
||||
validate_set_or_list_object(result)
|
||||
result
|
||||
|
||||
_ ->
|
||||
result
|
||||
end
|
||||
|
||||
# 11) If result contains only the key @language, set result to null.
|
||||
result = if is_map(result) and map_size(result) == 1 and Map.has_key?(result, "@language"),
|
||||
do: nil, else: result
|
||||
result =
|
||||
if is_map(result) and map_size(result) == 1 and Map.has_key?(result, "@language"),
|
||||
do: nil,
|
||||
else: result
|
||||
|
||||
# 12) If active property is null or @graph, drop free-floating values as follows:
|
||||
# Spec FIXME: Due to case 10) we might land with a list here; other implementations deal with that, by just returning in step 10)
|
||||
result = if is_map(result) and active_property in [nil, "@graph"] and (
|
||||
Enum.empty?(result) or
|
||||
Map.has_key?(result, "@value") or Map.has_key?(result, "@list") or
|
||||
(map_size(result) == 1 and Map.has_key?(result, "@id"))),
|
||||
do: nil, else: result
|
||||
result =
|
||||
if is_map(result) and active_property in [nil, "@graph"] and
|
||||
(Enum.empty?(result) or
|
||||
Map.has_key?(result, "@value") or Map.has_key?(result, "@list") or
|
||||
(map_size(result) == 1 and Map.has_key?(result, "@id"))),
|
||||
do: nil,
|
||||
else: result
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
defp validate_set_or_list_object(object) when map_size(object) == 1, do: true
|
||||
|
||||
defp validate_set_or_list_object(object = %{"@index" => _})
|
||||
when map_size(object) == 2, do: true
|
||||
when map_size(object) == 2,
|
||||
do: true
|
||||
|
||||
defp validate_set_or_list_object(object) do
|
||||
raise JSON.LD.InvalidSetOrListObjectError,
|
||||
message: "set or list object with disallowed members: #{inspect object}"
|
||||
message: "set or list object with disallowed members: #{inspect(object)}"
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
Details at <http://json-ld.org/spec/latest/json-ld-api/#value-expansion>
|
||||
"""
|
||||
def expand_value(active_context, active_property, value) do
|
||||
with term_def = Map.get(active_context.term_defs, active_property,
|
||||
%JSON.LD.Context.TermDefinition{}) do
|
||||
with term_def =
|
||||
Map.get(active_context.term_defs, active_property, %JSON.LD.Context.TermDefinition{}) do
|
||||
cond do
|
||||
term_def.type_mapping == "@id" ->
|
||||
%{"@id" => expand_iri(value, active_context, true, false)}
|
||||
|
||||
term_def.type_mapping == "@vocab" ->
|
||||
%{"@id" => expand_iri(value, active_context, true, true)}
|
||||
|
||||
type_mapping = term_def.type_mapping ->
|
||||
%{"@value" => value, "@type" => type_mapping}
|
||||
|
||||
is_binary(value) ->
|
||||
language_mapping = term_def.language_mapping
|
||||
|
||||
cond do
|
||||
language_mapping ->
|
||||
language_mapping ->
|
||||
%{"@value" => value, "@language" => language_mapping}
|
||||
language_mapping == false && active_context.default_language ->
|
||||
|
||||
language_mapping == false && active_context.default_language ->
|
||||
%{"@value" => value, "@language" => active_context.default_language}
|
||||
true ->
|
||||
%{"@value" => value}
|
||||
|
||||
true ->
|
||||
%{"@value" => value}
|
||||
end
|
||||
|
||||
true ->
|
||||
%{"@value" => value}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -4,16 +4,16 @@ defmodule JSON.LD.Flattening do
|
|||
import JSON.LD.{NodeIdentifierMap, Utils}
|
||||
alias JSON.LD.NodeIdentifierMap
|
||||
|
||||
|
||||
def flatten(input, context \\ nil, options \\ %JSON.LD.Options{}) do
|
||||
with options = JSON.LD.Options.new(options),
|
||||
with options = JSON.LD.Options.new(options),
|
||||
expanded = JSON.LD.expand(input, options),
|
||||
node_map = node_map(expanded)
|
||||
do
|
||||
node_map = node_map(expanded) do
|
||||
default_graph =
|
||||
Enum.reduce node_map, node_map["@default"], fn
|
||||
({"@default", _}, default_graph) -> default_graph
|
||||
({graph_name, graph}, default_graph) ->
|
||||
Enum.reduce(node_map, node_map["@default"], fn
|
||||
{"@default", _}, default_graph ->
|
||||
default_graph
|
||||
|
||||
{graph_name, graph}, default_graph ->
|
||||
entry =
|
||||
if Map.has_key?(default_graph, graph_name) do
|
||||
default_graph[graph_name]
|
||||
|
@ -24,31 +24,32 @@ defmodule JSON.LD.Flattening do
|
|||
graph_entry =
|
||||
graph
|
||||
|> Stream.reject(fn {_, node} ->
|
||||
Map.has_key?(node, "@id") and map_size(node) == 1 end)
|
||||
Map.has_key?(node, "@id") and map_size(node) == 1
|
||||
end)
|
||||
|> Enum.sort_by(fn {id, _} -> id end)
|
||||
# TODO: Spec fixme: Spec doesn't handle the case, when a "@graph" member already exists
|
||||
|> Enum.reduce(Map.get(entry, "@graph", []), fn ({_, node}, graph_entry) ->
|
||||
[node | graph_entry]
|
||||
end)
|
||||
|> Enum.reverse
|
||||
|> Enum.reduce(Map.get(entry, "@graph", []), fn {_, node}, graph_entry ->
|
||||
[node | graph_entry]
|
||||
end)
|
||||
|> Enum.reverse()
|
||||
|
||||
Map.put(default_graph, graph_name,
|
||||
Map.put(entry, "@graph", graph_entry))
|
||||
end
|
||||
Map.put(default_graph, graph_name, Map.put(entry, "@graph", graph_entry))
|
||||
end)
|
||||
|
||||
flattened =
|
||||
default_graph
|
||||
|> Enum.sort_by(fn {id, _} -> id end)
|
||||
|> Enum.reduce([], fn ({_, node}, flattened) ->
|
||||
if not (Enum.count(node) == 1 and Map.has_key?(node, "@id")) do
|
||||
[node | flattened]
|
||||
else
|
||||
flattened
|
||||
end
|
||||
end)
|
||||
|> Enum.reverse
|
||||
|> Enum.reduce([], fn {_, node}, flattened ->
|
||||
if not (Enum.count(node) == 1 and Map.has_key?(node, "@id")) do
|
||||
[node | flattened]
|
||||
else
|
||||
flattened
|
||||
end
|
||||
end)
|
||||
|> Enum.reverse()
|
||||
|
||||
if context && !Enum.empty?(flattened) do # TODO: Spec fixme: !Enum.empty?(flattened) is not in the spec, but in other implementations (Ruby, Java, Go, ...)
|
||||
# TODO: Spec fixme: !Enum.empty?(flattened) is not in the spec, but in other implementations (Ruby, Java, Go, ...)
|
||||
if context && !Enum.empty?(flattened) do
|
||||
JSON.LD.compact(flattened, context, options)
|
||||
else
|
||||
flattened
|
||||
|
@ -59,7 +60,8 @@ defmodule JSON.LD.Flattening do
|
|||
def node_map(input, node_id_map \\ nil)
|
||||
|
||||
def node_map(input, nil) do
|
||||
{:ok, node_id_map} = NodeIdentifierMap.start_link
|
||||
{:ok, node_id_map} = NodeIdentifierMap.start_link()
|
||||
|
||||
try do
|
||||
node_map(input, node_id_map)
|
||||
after
|
||||
|
@ -76,30 +78,59 @@ defmodule JSON.LD.Flattening do
|
|||
|
||||
Details at <https://www.w3.org/TR/json-ld-api/#node-map-generation>
|
||||
"""
|
||||
def generate_node_map(element, node_map, node_id_map, active_graph \\ "@default",
|
||||
active_subject \\ nil, active_property \\ nil, list \\ nil)
|
||||
def generate_node_map(
|
||||
element,
|
||||
node_map,
|
||||
node_id_map,
|
||||
active_graph \\ "@default",
|
||||
active_subject \\ nil,
|
||||
active_property \\ nil,
|
||||
list \\ nil
|
||||
)
|
||||
|
||||
# 1)
|
||||
def generate_node_map(element, node_map, node_id_map, active_graph, active_subject,
|
||||
active_property, list) when is_list(element) do
|
||||
Enum.reduce element, node_map, fn (item, node_map) ->
|
||||
generate_node_map(item, node_map, node_id_map, active_graph, active_subject,
|
||||
active_property, list)
|
||||
end
|
||||
def generate_node_map(
|
||||
element,
|
||||
node_map,
|
||||
node_id_map,
|
||||
active_graph,
|
||||
active_subject,
|
||||
active_property,
|
||||
list
|
||||
)
|
||||
when is_list(element) do
|
||||
Enum.reduce(element, node_map, fn item, node_map ->
|
||||
generate_node_map(
|
||||
item,
|
||||
node_map,
|
||||
node_id_map,
|
||||
active_graph,
|
||||
active_subject,
|
||||
active_property,
|
||||
list
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
# 2)
|
||||
def generate_node_map(element, node_map, node_id_map, active_graph, active_subject,
|
||||
active_property, list) when is_map(element) do
|
||||
def generate_node_map(
|
||||
element,
|
||||
node_map,
|
||||
node_id_map,
|
||||
active_graph,
|
||||
active_subject,
|
||||
active_property,
|
||||
list
|
||||
)
|
||||
when is_map(element) do
|
||||
node_map = Map.put_new(node_map, active_graph, %{})
|
||||
node = node_map[active_graph][active_subject]
|
||||
|
||||
# 3)
|
||||
element =
|
||||
if old_types = Map.get(element, "@type") do
|
||||
new_types = Enum.reduce(List.wrap(old_types), [],
|
||||
fn (item, types) ->
|
||||
new_types =
|
||||
Enum.reduce(List.wrap(old_types), [], fn item, types ->
|
||||
if blank_node_id?(item) do
|
||||
identifier = generate_blank_node_id(node_id_map, item)
|
||||
types ++ [identifier]
|
||||
|
@ -107,24 +138,29 @@ defmodule JSON.LD.Flattening do
|
|||
types ++ [item]
|
||||
end
|
||||
end)
|
||||
Map.put(element, "@type",
|
||||
if(is_list(old_types), do: new_types, else: List.first(new_types)))
|
||||
|
||||
Map.put(
|
||||
element,
|
||||
"@type",
|
||||
if(is_list(old_types), do: new_types, else: List.first(new_types))
|
||||
)
|
||||
else
|
||||
element
|
||||
end
|
||||
|
||||
cond do
|
||||
|
||||
# 4)
|
||||
Map.has_key?(element, "@value") ->
|
||||
if is_nil(list) do
|
||||
if node do
|
||||
update_in(node_map, [active_graph, active_subject, active_property], fn
|
||||
nil -> [element]
|
||||
nil ->
|
||||
[element]
|
||||
|
||||
items ->
|
||||
unless element in items,
|
||||
do: items ++ [element],
|
||||
else: items
|
||||
else: items
|
||||
end)
|
||||
else
|
||||
node_map
|
||||
|
@ -137,19 +173,28 @@ defmodule JSON.LD.Flattening do
|
|||
# 5)
|
||||
Map.has_key?(element, "@list") ->
|
||||
{:ok, result_list} = new_list()
|
||||
|
||||
{node_map, result} =
|
||||
try do
|
||||
{
|
||||
generate_node_map(element["@list"], node_map, node_id_map,
|
||||
active_graph, active_subject, active_property, result_list),
|
||||
generate_node_map(
|
||||
element["@list"],
|
||||
node_map,
|
||||
node_id_map,
|
||||
active_graph,
|
||||
active_subject,
|
||||
active_property,
|
||||
result_list
|
||||
),
|
||||
get_list(result_list)
|
||||
}
|
||||
after
|
||||
terminate_list(result_list)
|
||||
end
|
||||
after
|
||||
terminate_list(result_list)
|
||||
end
|
||||
|
||||
if node do
|
||||
update_in(node_map, [active_graph, active_subject, active_property], fn
|
||||
nil -> [result]
|
||||
nil -> [result]
|
||||
items -> items ++ [result]
|
||||
end)
|
||||
else
|
||||
|
@ -160,6 +205,7 @@ defmodule JSON.LD.Flattening do
|
|||
true ->
|
||||
# 6.1)
|
||||
{id, element} = Map.pop(element, "@id")
|
||||
|
||||
id =
|
||||
if id do
|
||||
if blank_node_id?(id) do
|
||||
|
@ -167,7 +213,8 @@ defmodule JSON.LD.Flattening do
|
|||
else
|
||||
id
|
||||
end
|
||||
# 6.2)
|
||||
|
||||
# 6.2)
|
||||
else
|
||||
generate_blank_node_id(node_id_map)
|
||||
end
|
||||
|
@ -190,28 +237,35 @@ defmodule JSON.LD.Flattening do
|
|||
if is_map(active_subject) do
|
||||
unless Map.has_key?(node, active_property) do
|
||||
update_in(node_map, [active_graph, id, active_property], fn
|
||||
nil -> [active_subject]
|
||||
nil ->
|
||||
[active_subject]
|
||||
|
||||
items ->
|
||||
unless active_subject in items,
|
||||
do: items ++ [active_subject],
|
||||
else: items
|
||||
else: items
|
||||
end)
|
||||
else
|
||||
node_map
|
||||
end
|
||||
# 6.6)
|
||||
|
||||
# 6.6)
|
||||
else
|
||||
unless is_nil(active_property) do
|
||||
reference = %{"@id" => id}
|
||||
|
||||
if is_nil(list) do
|
||||
update_in(node_map, [active_graph, active_subject, active_property], fn
|
||||
nil -> [reference]
|
||||
nil ->
|
||||
[reference]
|
||||
|
||||
items ->
|
||||
unless reference in items,
|
||||
do: items ++ [reference],
|
||||
else: items
|
||||
else: items
|
||||
end)
|
||||
# 6.6.3) TODO: Spec fixme: specs says to add ELEMENT to @list member, should be REFERENCE
|
||||
|
||||
# 6.6.3) TODO: Spec fixme: specs says to add ELEMENT to @list member, should be REFERENCE
|
||||
else
|
||||
append_to_list(list, reference)
|
||||
node_map
|
||||
|
@ -225,15 +279,18 @@ defmodule JSON.LD.Flattening do
|
|||
{node_map, element} =
|
||||
if Map.has_key?(element, "@type") do
|
||||
node_map =
|
||||
Enum.reduce element["@type"], node_map, fn (type, node_map) ->
|
||||
Enum.reduce(element["@type"], node_map, fn type, node_map ->
|
||||
update_in(node_map, [active_graph, id, "@type"], fn
|
||||
nil -> [type]
|
||||
nil ->
|
||||
[type]
|
||||
|
||||
items ->
|
||||
unless type in items,
|
||||
do: items ++ [type],
|
||||
else: items
|
||||
else: items
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
element = Map.delete(element, "@type")
|
||||
{node_map, element}
|
||||
else
|
||||
|
@ -244,6 +301,7 @@ defmodule JSON.LD.Flattening do
|
|||
{node_map, element} =
|
||||
if Map.has_key?(element, "@index") do
|
||||
{element_index, element} = Map.pop(element, "@index")
|
||||
|
||||
node_map =
|
||||
if node_index = get_in(node_map, [active_graph, id, "@index"]) do
|
||||
if not deep_compare(node_index, element_index) do
|
||||
|
@ -251,10 +309,11 @@ defmodule JSON.LD.Flattening do
|
|||
message: "Multiple conflicting indexes have been found for the same node."
|
||||
end
|
||||
else
|
||||
update_in node_map, [active_graph, id], fn node ->
|
||||
update_in(node_map, [active_graph, id], fn node ->
|
||||
Map.put(node, "@index", element_index)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
{node_map, element}
|
||||
else
|
||||
{node_map, element}
|
||||
|
@ -265,12 +324,21 @@ defmodule JSON.LD.Flattening do
|
|||
if Map.has_key?(element, "@reverse") do
|
||||
referenced_node = %{"@id" => id}
|
||||
{reverse_map, element} = Map.pop(element, "@reverse")
|
||||
node_map = Enum.reduce reverse_map, node_map, fn ({property, values}, node_map) ->
|
||||
Enum.reduce values, node_map, fn (value, node_map) ->
|
||||
generate_node_map(value, node_map, node_id_map, active_graph,
|
||||
referenced_node, property)
|
||||
end
|
||||
end
|
||||
|
||||
node_map =
|
||||
Enum.reduce(reverse_map, node_map, fn {property, values}, node_map ->
|
||||
Enum.reduce(values, node_map, fn value, node_map ->
|
||||
generate_node_map(
|
||||
value,
|
||||
node_map,
|
||||
node_id_map,
|
||||
active_graph,
|
||||
referenced_node,
|
||||
property
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
{node_map, element}
|
||||
else
|
||||
{node_map, element}
|
||||
|
@ -288,23 +356,25 @@ defmodule JSON.LD.Flattening do
|
|||
# 6.11)
|
||||
element
|
||||
|> Enum.sort_by(fn {property, _} -> property end)
|
||||
|> Enum.reduce(node_map, fn ({property, value}, node_map) ->
|
||||
property =
|
||||
if blank_node_id?(property) do
|
||||
generate_blank_node_id(node_id_map, property)
|
||||
else
|
||||
property
|
||||
end
|
||||
node_map =
|
||||
unless Map.has_key?(node_map[active_graph][id], property) do
|
||||
update_in node_map, [active_graph, id], fn node ->
|
||||
Map.put(node, property, [])
|
||||
end
|
||||
else
|
||||
node_map
|
||||
end
|
||||
generate_node_map(value, node_map, node_id_map, active_graph, id, property)
|
||||
end)
|
||||
|> Enum.reduce(node_map, fn {property, value}, node_map ->
|
||||
property =
|
||||
if blank_node_id?(property) do
|
||||
generate_blank_node_id(node_id_map, property)
|
||||
else
|
||||
property
|
||||
end
|
||||
|
||||
node_map =
|
||||
unless Map.has_key?(node_map[active_graph][id], property) do
|
||||
update_in(node_map, [active_graph, id], fn node ->
|
||||
Map.put(node, property, [])
|
||||
end)
|
||||
else
|
||||
node_map
|
||||
end
|
||||
|
||||
generate_node_map(value, node_map, node_id_map, active_graph, id, property)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -314,29 +384,29 @@ defmodule JSON.LD.Flattening do
|
|||
Map.has_key?(v2, k) && deep_compare(v, v2[k])
|
||||
end)
|
||||
end
|
||||
|
||||
defp deep_compare(v1, v2) when is_list(v1) and is_list(v2) do
|
||||
Enum.count(v1) == Enum.count(v2) && MapSet.new(v1) == MapSet.new(v2)
|
||||
end
|
||||
|
||||
defp deep_compare(v, v), do: true
|
||||
defp deep_compare(_, _), do: false
|
||||
|
||||
|
||||
defp new_list do
|
||||
Agent.start_link fn -> %{"@list" => []} end
|
||||
Agent.start_link(fn -> %{"@list" => []} end)
|
||||
end
|
||||
|
||||
defp terminate_list(pid) do
|
||||
Agent.stop pid
|
||||
Agent.stop(pid)
|
||||
end
|
||||
|
||||
defp get_list(pid) do
|
||||
Agent.get pid, fn list_node -> list_node end
|
||||
Agent.get(pid, fn list_node -> list_node end)
|
||||
end
|
||||
|
||||
defp append_to_list(pid, element) do
|
||||
Agent.update pid, fn list_node ->
|
||||
Agent.update(pid, fn list_node ->
|
||||
Map.update(list_node, "@list", [element], fn list -> list ++ [element] end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
defmodule JSON.LD.IRIExpansion do
|
||||
|
||||
import JSON.LD.Utils
|
||||
|
||||
@keywords JSON.LD.keywords # to allow this to be used in function guard clauses, we redefine this here
|
||||
# to allow this to be used in function guard clauses, we redefine this here
|
||||
@keywords JSON.LD.keywords()
|
||||
|
||||
@doc """
|
||||
see http://json-ld.org/spec/latest/json-ld-api/#iri-expansion
|
||||
"""
|
||||
def expand_iri(value, active_context, doc_relative \\ false, vocab \\ false,
|
||||
local_context \\ nil, defined \\ nil)
|
||||
def expand_iri(
|
||||
value,
|
||||
active_context,
|
||||
doc_relative \\ false,
|
||||
vocab \\ false,
|
||||
local_context \\ nil,
|
||||
defined \\ nil
|
||||
)
|
||||
|
||||
# 1) If value is a keyword or null, return value as is.
|
||||
def expand_iri(value, active_context, _, _, local_context, defined)
|
||||
when is_nil(value) or value in @keywords do
|
||||
when is_nil(value) or value in @keywords do
|
||||
if local_context || defined do
|
||||
{value, active_context, defined}
|
||||
else
|
||||
|
@ -25,8 +31,14 @@ defmodule JSON.LD.IRIExpansion do
|
|||
{active_context, defined} =
|
||||
if local_context && local_context[value] && defined[value] != true do
|
||||
local_def = local_context[value]
|
||||
|
||||
JSON.LD.Context.create_term_definition(
|
||||
active_context, local_context, value, local_def, defined)
|
||||
active_context,
|
||||
local_context,
|
||||
value,
|
||||
local_def,
|
||||
defined
|
||||
)
|
||||
else
|
||||
{active_context, defined}
|
||||
end
|
||||
|
@ -37,6 +49,7 @@ defmodule JSON.LD.IRIExpansion do
|
|||
vocab && Map.has_key?(active_context.term_defs, value) ->
|
||||
result = (term_def = active_context.term_defs[value]) && term_def.iri_mapping
|
||||
{result, active_context, defined}
|
||||
|
||||
# 4) If value contains a colon (:), it is either an absolute IRI, a compact IRI, or a blank node identifier
|
||||
String.contains?(value, ":") ->
|
||||
case compact_iri_parts(value) do
|
||||
|
@ -45,33 +58,47 @@ defmodule JSON.LD.IRIExpansion do
|
|||
{active_context, defined} =
|
||||
if local_context && local_context[prefix] && defined[prefix] != true do
|
||||
local_def = local_context[prefix]
|
||||
|
||||
JSON.LD.Context.create_term_definition(
|
||||
active_context, local_context, prefix, local_def, defined)
|
||||
active_context,
|
||||
local_context,
|
||||
prefix,
|
||||
local_def,
|
||||
defined
|
||||
)
|
||||
else
|
||||
{active_context, defined}
|
||||
end
|
||||
|
||||
# 4.4)
|
||||
result =
|
||||
if prefix_def = active_context.term_defs[prefix] do
|
||||
prefix_def.iri_mapping <> suffix
|
||||
else
|
||||
value # 4.5)
|
||||
# 4.5)
|
||||
value
|
||||
end
|
||||
|
||||
{result, active_context, defined}
|
||||
|
||||
nil ->
|
||||
{value, active_context, defined} # 4.2)
|
||||
# 4.2)
|
||||
{value, active_context, defined}
|
||||
end
|
||||
|
||||
# 5) If vocab is true, and active context has a vocabulary mapping, return the result of concatenating the vocabulary mapping with value.
|
||||
vocab && active_context.vocab ->
|
||||
vocabulary_mapping = active_context.vocab
|
||||
{vocabulary_mapping <> value, active_context, defined}
|
||||
|
||||
# 6) Otherwise, if document relative is true, set value to the result of resolving value against the base IRI. Only the basic algorithm in section 5.2 of [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization are performed. Characters additionally allowed in IRI references are treated in the same way that unreserved characters are treated in URI references, per section 6.5 of [RFC3987].
|
||||
doc_relative ->
|
||||
{absolute_iri(value, JSON.LD.Context.base(active_context)), active_context, defined}
|
||||
# TODO: RDF.rb's implementation differs from the spec here, by checking if base_iri is actually present in the previous clause and adding the following additional clause. Another Spec error?
|
||||
# if local_context && RDF::URI(value).relative?
|
||||
# # If local context is not null and value is not an absolute IRI, an invalid IRI mapping error has been detected and processing is aborted.
|
||||
# raise JSON.LD.InvalidIRIMappingError, message: "not an absolute IRI: #{value}"
|
||||
|
||||
# TODO: RDF.rb's implementation differs from the spec here, by checking if base_iri is actually present in the previous clause and adding the following additional clause. Another Spec error?
|
||||
# if local_context && RDF::URI(value).relative?
|
||||
# # If local context is not null and value is not an absolute IRI, an invalid IRI mapping error has been detected and processing is aborted.
|
||||
# raise JSON.LD.InvalidIRIMappingError, message: "not an absolute IRI: #{value}"
|
||||
# 7) Return value as is.
|
||||
true ->
|
||||
{value, active_context, defined}
|
||||
|
@ -83,5 +110,4 @@ defmodule JSON.LD.IRIExpansion do
|
|||
result
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -22,7 +22,6 @@ defmodule JSON.LD.NodeIdentifierMap do
|
|||
GenServer.call(pid, {:generate_id, identifier})
|
||||
end
|
||||
|
||||
|
||||
# Server Callbacks
|
||||
|
||||
def init(:ok) do
|
||||
|
@ -34,16 +33,17 @@ defmodule JSON.LD.NodeIdentifierMap do
|
|||
{:reply, map[identifier], state}
|
||||
else
|
||||
blank_node_id = "_:b#{counter}"
|
||||
{:reply, blank_node_id, %{
|
||||
counter: counter + 1,
|
||||
map:
|
||||
if identifier do
|
||||
Map.put(map, identifier, blank_node_id)
|
||||
else
|
||||
map
|
||||
end
|
||||
}}
|
||||
|
||||
{:reply, blank_node_id,
|
||||
%{
|
||||
counter: counter + 1,
|
||||
map:
|
||||
if identifier do
|
||||
Map.put(map, identifier, blank_node_id)
|
||||
else
|
||||
map
|
||||
end
|
||||
}}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -17,5 +17,4 @@ defmodule JSON.LD.Options do
|
|||
def new(), do: %JSON.LD.Options{}
|
||||
def new(%JSON.LD.Options{} = options), do: options
|
||||
def new(options), do: struct(JSON.LD.Options, options)
|
||||
|
||||
end
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
defmodule JSON.LD.Utils do
|
||||
|
||||
alias RDF.IRI
|
||||
|
||||
@doc """
|
||||
|
@ -16,24 +15,23 @@ defmodule JSON.LD.Utils do
|
|||
|
||||
def absolute_iri(value, nil),
|
||||
do: value
|
||||
|
||||
def absolute_iri(value, base_iri),
|
||||
do: value |> RDF.IRI.absolute(base_iri) |> to_string
|
||||
|
||||
|
||||
def relative_iri?(value),
|
||||
do: not (JSON.LD.keyword?(value) or IRI.absolute?(value) or blank_node_id?(value))
|
||||
|
||||
def compact_iri_parts(compact_iri, exclude_bnode \\ true) do
|
||||
with [prefix, suffix] <- String.split(compact_iri, ":", parts: 2) do
|
||||
if not(String.starts_with?(suffix, "//")) and
|
||||
not(exclude_bnode and prefix == "_"),
|
||||
do: [prefix, suffix]
|
||||
if not String.starts_with?(suffix, "//") and
|
||||
not (exclude_bnode and prefix == "_"),
|
||||
do: [prefix, suffix]
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@doc """
|
||||
Checks if the given value is a blank node identifier.
|
||||
|
||||
|
@ -45,18 +43,19 @@ defmodule JSON.LD.Utils do
|
|||
see <https://www.w3.org/TR/json-ld-api/#dfn-blank-node-identifier>
|
||||
"""
|
||||
def blank_node_id?("_:" <> _), do: true
|
||||
def blank_node_id?(_), do: false
|
||||
def blank_node_id?(_), do: false
|
||||
|
||||
def scalar?(value)
|
||||
when is_binary(value) or is_number(value) or
|
||||
is_boolean(value),
|
||||
do: true
|
||||
|
||||
def scalar?(value) when is_binary(value) or is_number(value) or
|
||||
is_boolean(value), do: true
|
||||
def scalar?(_), do: false
|
||||
|
||||
def list?(%{"@list" => _}), do: true
|
||||
def list?(_), do: false
|
||||
def list?(%{"@list" => _}), do: true
|
||||
def list?(_), do: false
|
||||
def index?(%{"@index" => _}), do: true
|
||||
def index?(_), do: false
|
||||
def index?(_), do: false
|
||||
def value?(%{"@value" => _}), do: true
|
||||
def value?(_), do: false
|
||||
|
||||
def value?(_), do: false
|
||||
end
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
defmodule JSON.LD do
|
||||
|
||||
use RDF.Serialization.Format
|
||||
|
||||
import RDF.Sigils
|
||||
|
||||
@id ~I<http://www.w3.org/ns/formats/JSON-LD>
|
||||
@name :jsonld
|
||||
@extension "jsonld"
|
||||
@id ~I<http://www.w3.org/ns/formats/JSON-LD>
|
||||
@name :jsonld
|
||||
@extension "jsonld"
|
||||
@media_type "application/ld+json"
|
||||
|
||||
def options, do: JSON.LD.Options.new
|
||||
def options, do: JSON.LD.Options.new()
|
||||
|
||||
@keywords ~w[
|
||||
@base
|
||||
|
@ -42,7 +41,6 @@ defmodule JSON.LD do
|
|||
def keyword?(value) when is_binary(value) and value in @keywords, do: true
|
||||
def keyword?(_value), do: false
|
||||
|
||||
|
||||
@doc """
|
||||
Expands the given input according to the steps in the JSON-LD Expansion Algorithm.
|
||||
|
||||
|
@ -57,7 +55,6 @@ defmodule JSON.LD do
|
|||
defdelegate expand(input, options \\ %JSON.LD.Options{}),
|
||||
to: JSON.LD.Expansion
|
||||
|
||||
|
||||
@doc """
|
||||
Compacts the given input according to the steps in the JSON-LD Compaction Algorithm.
|
||||
|
||||
|
@ -74,7 +71,6 @@ defmodule JSON.LD do
|
|||
defdelegate compact(input, context, options \\ %JSON.LD.Options{}),
|
||||
to: JSON.LD.Compaction
|
||||
|
||||
|
||||
@doc """
|
||||
Flattens the given input according to the steps in the JSON-LD Flattening Algorithm.
|
||||
|
||||
|
@ -90,7 +86,6 @@ defmodule JSON.LD do
|
|||
defdelegate flatten(input, context \\ nil, options \\ %JSON.LD.Options{}),
|
||||
to: JSON.LD.Flattening
|
||||
|
||||
|
||||
@doc """
|
||||
Generator function for `JSON.LD.Context`s.
|
||||
|
||||
|
@ -105,11 +100,9 @@ defmodule JSON.LD do
|
|||
def context(context, options),
|
||||
do: JSON.LD.Context.create(%{"@context" => context}, options)
|
||||
|
||||
|
||||
@doc """
|
||||
Generator function for JSON-LD node maps.
|
||||
"""
|
||||
def node_map(input, node_id_map \\ nil),
|
||||
do: JSON.LD.Flattening.node_map(input, node_id_map)
|
||||
|
||||
end
|
||||
|
|
26
mix.exs
26
mix.exs
|
@ -3,15 +3,15 @@ defmodule JSON.LD.Mixfile do
|
|||
|
||||
@repo_url "https://github.com/rdf-elixir/jsonld-ex"
|
||||
|
||||
@version File.read!("VERSION") |> String.trim
|
||||
@version File.read!("VERSION") |> String.trim()
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :json_ld,
|
||||
version: @version,
|
||||
elixir: "~> 1.8",
|
||||
build_embedded: Mix.env == :prod,
|
||||
start_permanent: Mix.env == :prod,
|
||||
build_embedded: Mix.env() == :prod,
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
|
||||
|
@ -25,7 +25,7 @@ defmodule JSON.LD.Mixfile do
|
|||
main: "JSON.LD",
|
||||
source_url: @repo_url,
|
||||
source_ref: "v#{@version}",
|
||||
extras: ["README.md"],
|
||||
extras: ["README.md"]
|
||||
],
|
||||
|
||||
# ExCoveralls
|
||||
|
@ -35,7 +35,7 @@ defmodule JSON.LD.Mixfile do
|
|||
"coveralls.detail": :test,
|
||||
"coveralls.post": :test,
|
||||
"coveralls.html": :test
|
||||
],
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -63,16 +63,16 @@ defmodule JSON.LD.Mixfile do
|
|||
{:rdf, "~> 0.8"},
|
||||
{:jason, "~> 1.2"},
|
||||
{:httpoison, "~> 1.7"},
|
||||
|
||||
{:credo, "~> 1.4", only: [:dev, :test], runtime: false},
|
||||
{:dialyxir, "~> 1.0", only: :dev, runtime: false},
|
||||
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
||||
{:bypass, "~> 1.0", only: :test},
|
||||
{:plug_cowboy, "~> 1.0", only: :test}, # in order to run under OTP 21 we need to keep this dependency of bypass on 1.0
|
||||
{:excoveralls, "~> 0.13", only: :test}
|
||||
{:credo, "~> 1.4", only: [:dev, :test], runtime: false},
|
||||
{:dialyxir, "~> 1.0", only: :dev, runtime: false},
|
||||
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
||||
{:bypass, "~> 1.0", only: :test},
|
||||
# in order to run under OTP 21 we need to keep this dependency of bypass on 1.0
|
||||
{:plug_cowboy, "~> 1.0", only: :test},
|
||||
{:excoveralls, "~> 0.13", only: :test}
|
||||
]
|
||||
end
|
||||
|
||||
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
||||
defp elixirc_paths(_), do: ["lib"]
|
||||
defp elixirc_paths(_), do: ["lib"]
|
||||
end
|
||||
|
|
|
@ -9,13 +9,16 @@ defmodule JSON.LD.TestSuite.CompactTest do
|
|||
|
||||
test_cases("compact")
|
||||
|> Enum.each(fn %{"name" => name, "input" => input} = test_case ->
|
||||
@tag :test_suite
|
||||
@tag :compact_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => output, "context" => context} = test_case, base_iri: base_iri} do
|
||||
assert JSON.LD.compact(j(input), j(context), test_case_options(test_case, base_iri)) == j(output)
|
||||
end
|
||||
end)
|
||||
|
||||
@tag :test_suite
|
||||
@tag :compact_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{
|
||||
data: %{"input" => input, "expect" => output, "context" => context} = test_case,
|
||||
base_iri: base_iri
|
||||
} do
|
||||
assert JSON.LD.compact(j(input), j(context), test_case_options(test_case, base_iri)) ==
|
||||
j(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -9,23 +9,21 @@ defmodule JSON.LD.TestSuite.ErrorTest do
|
|||
|
||||
test_cases("error")
|
||||
|> Enum.each(fn %{"name" => name, "input" => input} = test_case ->
|
||||
@tag :test_suite
|
||||
@tag :flatten_test_suite
|
||||
@tag :error_test
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => error} = test_case, base_iri: base_iri} do
|
||||
context =
|
||||
case test_case["context"] do
|
||||
nil -> nil
|
||||
context -> j(context)
|
||||
end
|
||||
|
||||
assert_raise exception(error), fn ->
|
||||
JSON.LD.flatten(j(input), context, test_case_options(test_case, base_iri))
|
||||
@tag :test_suite
|
||||
@tag :flatten_test_suite
|
||||
@tag :error_test
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => error} = test_case, base_iri: base_iri} do
|
||||
context =
|
||||
case test_case["context"] do
|
||||
nil -> nil
|
||||
context -> j(context)
|
||||
end
|
||||
|
||||
assert_raise exception(error), fn ->
|
||||
JSON.LD.flatten(j(input), context, test_case_options(test_case, base_iri))
|
||||
end
|
||||
end)
|
||||
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -8,21 +8,21 @@ defmodule JSON.LD.TestSuite.ExpandTest do
|
|||
end
|
||||
|
||||
test_cases("expand")
|
||||
# TODO: Ordering problems
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0034] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0035] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0038] end)
|
||||
# TODO: Ordering problems
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0034] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0035] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0038] end)
|
||||
|> Enum.each(fn %{"name" => name, "input" => input} = test_case ->
|
||||
if input in ~w[expand-0034-in.jsonld expand-0035-in.jsonld expand-0038-in.jsonld] do
|
||||
@tag skip: "TODO: Actually correct values are expanded, but the ordering is different."
|
||||
end
|
||||
@tag :test_suite
|
||||
@tag :expand_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => output} = test_case, base_iri: base_iri} do
|
||||
assert JSON.LD.expand(j(input), test_case_options(test_case, base_iri)) == j(output)
|
||||
end
|
||||
end)
|
||||
if input in ~w[expand-0034-in.jsonld expand-0035-in.jsonld expand-0038-in.jsonld] do
|
||||
@tag skip: "TODO: Actually correct values are expanded, but the ordering is different."
|
||||
end
|
||||
|
||||
@tag :test_suite
|
||||
@tag :expand_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => output} = test_case, base_iri: base_iri} do
|
||||
assert JSON.LD.expand(j(input), test_case_options(test_case, base_iri)) == j(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -8,26 +8,28 @@ defmodule JSON.LD.TestSuite.FlattenTest do
|
|||
end
|
||||
|
||||
test_cases("flatten")
|
||||
# TODO: Ordering problems
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0034] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0035] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0038] end)
|
||||
# TODO: Ordering problems
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0034] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0035] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0038] end)
|
||||
|> Enum.each(fn %{"name" => name, "input" => input} = test_case ->
|
||||
if input in ~w[flatten-0034-in.jsonld flatten-0035-in.jsonld flatten-0038-in.jsonld] do
|
||||
@tag skip: "TODO: Actually correct values are expanded, but the ordering is different."
|
||||
end
|
||||
@tag :test_suite
|
||||
@tag :flatten_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => output} = test_case, base_iri: base_iri} do
|
||||
context =
|
||||
case test_case["context"] do
|
||||
nil -> nil
|
||||
context -> j(context)
|
||||
end
|
||||
assert JSON.LD.flatten(j(input), context, test_case_options(test_case, base_iri)) == j(output)
|
||||
end
|
||||
end)
|
||||
if input in ~w[flatten-0034-in.jsonld flatten-0035-in.jsonld flatten-0038-in.jsonld] do
|
||||
@tag skip: "TODO: Actually correct values are expanded, but the ordering is different."
|
||||
end
|
||||
|
||||
@tag :test_suite
|
||||
@tag :flatten_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => output} = test_case, base_iri: base_iri} do
|
||||
context =
|
||||
case test_case["context"] do
|
||||
nil -> nil
|
||||
context -> j(context)
|
||||
end
|
||||
|
||||
assert JSON.LD.flatten(j(input), context, test_case_options(test_case, base_iri)) ==
|
||||
j(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -3,52 +3,53 @@ defmodule JSON.LD.TestSuite.FromRdfTest do
|
|||
|
||||
import JSON.LD.TestSuite
|
||||
|
||||
|
||||
setup_all do
|
||||
[base_iri: manifest("fromRdf")["baseIri"]]
|
||||
end
|
||||
|
||||
test_cases("fromRdf")
|
||||
# TODO: https://github.com/json-ld/json-ld.org/issues/357
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0020] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0021] end)
|
||||
# TODO: Ordering problems
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0001] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0002] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0017] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0018] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0019] end)
|
||||
# TODO: https://github.com/json-ld/json-ld.org/issues/357
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0020] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0021] end)
|
||||
# TODO: Ordering problems
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0001] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0002] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0017] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0018] end)
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0019] end)
|
||||
|
||||
|> Enum.each(fn %{"name" => name, "input" => input} = test_case ->
|
||||
if input in ~w[fromRdf-0001-in.nq fromRdf-0002-in.nq fromRdf-0017-in.nq fromRdf-0018-in.nq fromRdf-0019-in.nq] do
|
||||
@tag skip: """
|
||||
The values are correct, but the order not, because Elixirs maps with the input graphs have no order.
|
||||
So, fixing that would require a different representation of graphs in general.
|
||||
"""
|
||||
end
|
||||
if input in ~w[fromRdf-0020-in.nq fromRdf-0021-in.nq] do
|
||||
@tag skip: "https://github.com/json-ld/json-ld.org/issues/357"
|
||||
end
|
||||
@tag :test_suite
|
||||
@tag :from_rdf_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => output} = test_case, base_iri: base_iri} do
|
||||
assert serialize(input, test_case_options(test_case, base_iri)) == json(output)
|
||||
end
|
||||
end)
|
||||
if input in ~w[fromRdf-0001-in.nq fromRdf-0002-in.nq fromRdf-0017-in.nq fromRdf-0018-in.nq fromRdf-0019-in.nq] do
|
||||
@tag skip: """
|
||||
The values are correct, but the order not, because Elixirs maps with the input graphs have no order.
|
||||
So, fixing that would require a different representation of graphs in general.
|
||||
"""
|
||||
end
|
||||
|
||||
if input in ~w[fromRdf-0020-in.nq fromRdf-0021-in.nq] do
|
||||
@tag skip: "https://github.com/json-ld/json-ld.org/issues/357"
|
||||
end
|
||||
|
||||
@tag :test_suite
|
||||
@tag :from_rdf_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => output} = test_case, base_iri: base_iri} do
|
||||
assert serialize(input, test_case_options(test_case, base_iri)) == json(output)
|
||||
end
|
||||
end)
|
||||
|
||||
def serialize(filename, options) do
|
||||
filename
|
||||
|> file
|
||||
|> RDF.NQuads.read_file!
|
||||
|> RDF.NQuads.read_file!()
|
||||
|> JSON.LD.Encoder.from_rdf!(options)
|
||||
end
|
||||
|
||||
def json(filename) do
|
||||
filename
|
||||
|> file
|
||||
|> File.read!
|
||||
|> Jason.decode!
|
||||
|> File.read!()
|
||||
|> Jason.decode!()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# TODO: These tests are disabled as we don't support remote document loading yet.
|
||||
|
||||
#defmodule JSON.LD.TestSuite.RemoteDocTest do
|
||||
# defmodule JSON.LD.TestSuite.RemoteDocTest do
|
||||
# use ExUnit.Case, async: false
|
||||
#
|
||||
# import JSON.LD.TestSuite
|
||||
|
@ -54,4 +54,4 @@
|
|||
# end
|
||||
# end
|
||||
# end)
|
||||
#end
|
||||
# end
|
||||
|
|
|
@ -9,28 +9,30 @@ defmodule JSON.LD.TestSuite.ToRdfTest do
|
|||
end
|
||||
|
||||
test_cases("toRdf")
|
||||
# TODO: Ordering problems
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0118] end)
|
||||
# TODO: Ordering problems
|
||||
# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0118] end)
|
||||
|> Enum.each(fn %{"name" => name, "input" => input} = test_case ->
|
||||
if input in ~w[toRdf-0118-in.jsonld] do
|
||||
@tag skip: """
|
||||
Actually an isomorphic graph is generated, but due to different ordering
|
||||
during expansion the generated blank nodes are named different.
|
||||
"""
|
||||
end
|
||||
@tag :test_suite
|
||||
@tag :to_rdf_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => output} = test_case, base_iri: base_iri} do
|
||||
# This requires a special handling, since the N-Quad ouput file is not valid, by using blank nodes as predicates
|
||||
dataset = if input == "toRdf-0118-in.jsonld",
|
||||
do: toRdf_0118_dataset(),
|
||||
else: RDF.NQuads.read_file!(file(output))
|
||||
if input in ~w[toRdf-0118-in.jsonld] do
|
||||
@tag skip: """
|
||||
Actually an isomorphic graph is generated, but due to different ordering
|
||||
during expansion the generated blank nodes are named different.
|
||||
"""
|
||||
end
|
||||
|
||||
assert JSON.LD.read_file!(file(input), test_case_options(test_case, base_iri)) == dataset
|
||||
end
|
||||
end)
|
||||
@tag :test_suite
|
||||
@tag :to_rdf_test_suite
|
||||
@tag data: test_case
|
||||
test "#{input}: #{name}",
|
||||
%{data: %{"input" => input, "expect" => output} = test_case, base_iri: base_iri} do
|
||||
# This requires a special handling, since the N-Quad ouput file is not valid, by using blank nodes as predicates
|
||||
dataset =
|
||||
if input == "toRdf-0118-in.jsonld",
|
||||
do: toRdf_0118_dataset(),
|
||||
else: RDF.NQuads.read_file!(file(output))
|
||||
|
||||
assert JSON.LD.read_file!(file(input), test_case_options(test_case, base_iri)) == dataset
|
||||
end
|
||||
end)
|
||||
|
||||
def toRdf_0118_dataset do
|
||||
RDF.Dataset.new([
|
||||
|
@ -42,7 +44,7 @@ defmodule JSON.LD.TestSuite.ToRdfTest do
|
|||
{RDF.bnode("b0"), RDF.bnode("b0"), RDF.bnode("b2")},
|
||||
{RDF.bnode("b0"), RDF.bnode("b0"), RDF.bnode("b3")},
|
||||
{RDF.bnode("b1"), RDF.bnode("b0"), "term"},
|
||||
{RDF.bnode("b2"), RDF.bnode("b0"), "termId"},
|
||||
{RDF.bnode("b2"), RDF.bnode("b0"), "termId"}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
defmodule JSON.LD.TestData do
|
||||
|
||||
@dir Path.join(File.cwd!, "test/data/")
|
||||
@dir Path.join(File.cwd!(), "test/data/")
|
||||
def dir, do: @dir
|
||||
|
||||
def file(name) do
|
||||
|
@ -10,5 +9,4 @@ defmodule JSON.LD.TestData do
|
|||
raise "Test data file '#{name}' not found"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
defmodule JSON.LD.TestSuite do
|
||||
|
||||
@test_suite_dir "json-ld.org-test-suite"
|
||||
def test_suite_dir, do: @test_suite_dir
|
||||
|
||||
|
@ -7,7 +6,7 @@ defmodule JSON.LD.TestSuite do
|
|||
|
||||
def parse_json_file!(file) do
|
||||
case File.read(file(file)) do
|
||||
{:ok, content} -> Jason.decode!(content)
|
||||
{:ok, content} -> Jason.decode!(content)
|
||||
{:error, reason} -> raise File.Error, path: file, action: "read", reason: reason
|
||||
end
|
||||
end
|
||||
|
@ -29,8 +28,8 @@ defmodule JSON.LD.TestSuite do
|
|||
cond do
|
||||
"jld:PositiveEvaluationTest" in type -> :positive_evaluation_test
|
||||
"jld:NegativeEvaluationTest" in type -> :negative_evaluation_test
|
||||
"jld:PositiveSyntaxTest" in type -> :positive_syntax_test
|
||||
"jld:NegativeSyntaxTest" in type -> :negative_syntax_test
|
||||
"jld:PositiveSyntaxTest" in type -> :positive_syntax_test
|
||||
"jld:NegativeSyntaxTest" in type -> :negative_syntax_test
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
@ -43,22 +42,23 @@ defmodule JSON.LD.TestSuite do
|
|||
|> Map.get("option", %{})
|
||||
|> Map.put_new("base", base_iri <> test_case["input"])
|
||||
|> Enum.map(fn {key, value} ->
|
||||
{key |> Macro.underscore |> String.to_atom, value}
|
||||
end)
|
||||
{key |> Macro.underscore() |> String.to_atom(), value}
|
||||
end)
|
||||
|> Enum.map(fn
|
||||
{:expand_context, file} -> {:expand_context, j(file)}
|
||||
option -> option
|
||||
end)
|
||||
{:expand_context, file} -> {:expand_context, j(file)}
|
||||
option -> option
|
||||
end)
|
||||
end
|
||||
|
||||
def exception(error) do
|
||||
error = error
|
||||
|> String.replace(" ", "_")
|
||||
|> String.replace("-", "_")
|
||||
|> String.replace("@", "_")
|
||||
|> Macro.camelize
|
||||
|> String.replace("_", "")
|
||||
error =
|
||||
error
|
||||
|> String.replace(" ", "_")
|
||||
|> String.replace("-", "_")
|
||||
|> String.replace("@", "_")
|
||||
|> Macro.camelize()
|
||||
|> String.replace("_", "")
|
||||
|
||||
String.to_existing_atom("Elixir.JSON.LD.#{error}Error")
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -4,7 +4,8 @@ defmodule JSON.LD.CompactionTest do
|
|||
alias RDF.NS.{RDFS, XSD}
|
||||
|
||||
test "Flattened form of a JSON-LD document (EXAMPLES 57-59 of https://www.w3.org/TR/json-ld/#compacted-document-form)" do
|
||||
input = Jason.decode! """
|
||||
input =
|
||||
Jason.decode!("""
|
||||
[
|
||||
{
|
||||
"http://xmlns.com/foaf/0.1/name": [ "Manu Sporny" ],
|
||||
|
@ -15,8 +16,10 @@ defmodule JSON.LD.CompactionTest do
|
|||
]
|
||||
}
|
||||
]
|
||||
"""
|
||||
context = Jason.decode! """
|
||||
""")
|
||||
|
||||
context =
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
|
@ -26,22 +29,23 @@ defmodule JSON.LD.CompactionTest do
|
|||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
assert JSON.LD.compact(input, context) == Jason.decode! """
|
||||
{
|
||||
"@context": {
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
"homepage": {
|
||||
"@id": "http://xmlns.com/foaf/0.1/homepage",
|
||||
"@type": "@id"
|
||||
}
|
||||
},
|
||||
"name": "Manu Sporny",
|
||||
"homepage": "http://manu.sporny.org/"
|
||||
}
|
||||
"""
|
||||
end
|
||||
""")
|
||||
|
||||
assert JSON.LD.compact(input, context) ==
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
"homepage": {
|
||||
"@id": "http://xmlns.com/foaf/0.1/homepage",
|
||||
"@type": "@id"
|
||||
}
|
||||
},
|
||||
"name": "Manu Sporny",
|
||||
"homepage": "http://manu.sporny.org/"
|
||||
}
|
||||
""")
|
||||
end
|
||||
|
||||
%{
|
||||
"prefix" => %{
|
||||
|
@ -111,15 +115,15 @@ defmodule JSON.LD.CompactionTest do
|
|||
},
|
||||
"xsd:date coercion" => %{
|
||||
input: %{
|
||||
"http://example.com/b" => %{"@value" => "2012-01-04", "@type" => to_string(XSD.date)}
|
||||
"http://example.com/b" => %{"@value" => "2012-01-04", "@type" => to_string(XSD.date())}
|
||||
},
|
||||
context: %{
|
||||
"xsd" => XSD.__base_iri__,
|
||||
"xsd" => XSD.__base_iri__(),
|
||||
"b" => %{"@id" => "http://example.com/b", "@type" => "xsd:date"}
|
||||
},
|
||||
output: %{
|
||||
"@context" => %{
|
||||
"xsd" => XSD.__base_iri__,
|
||||
"xsd" => XSD.__base_iri__(),
|
||||
"b" => %{"@id" => "http://example.com/b", "@type" => "xsd:date"}
|
||||
},
|
||||
"b" => "2012-01-04"
|
||||
|
@ -138,7 +142,7 @@ defmodule JSON.LD.CompactionTest do
|
|||
"@list coercion (integer)" => %{
|
||||
input: %{
|
||||
"http://example.com/term" => [
|
||||
%{"@list" => [1]},
|
||||
%{"@list" => [1]}
|
||||
]
|
||||
},
|
||||
context: %{
|
||||
|
@ -150,7 +154,7 @@ defmodule JSON.LD.CompactionTest do
|
|||
"term4" => %{"@id" => "http://example.com/term", "@container" => "@list"},
|
||||
"@language" => "de"
|
||||
},
|
||||
"term4" => [1],
|
||||
"term4" => [1]
|
||||
}
|
||||
},
|
||||
"@set coercion" => %{
|
||||
|
@ -176,24 +180,24 @@ defmodule JSON.LD.CompactionTest do
|
|||
"@type with string @id" => %{
|
||||
input: %{
|
||||
"@id" => "http://example.com/",
|
||||
"@type" => (RDFS.Resource |> RDF.uri |> to_string)
|
||||
"@type" => RDFS.Resource |> RDF.uri() |> to_string
|
||||
},
|
||||
context: %{},
|
||||
output: %{
|
||||
"@id" => "http://example.com/",
|
||||
"@type" => (RDFS.Resource |> RDF.uri |> to_string)
|
||||
},
|
||||
"@type" => RDFS.Resource |> RDF.uri() |> to_string
|
||||
}
|
||||
},
|
||||
"@type with array @id" => %{
|
||||
input: %{
|
||||
"@id" => "http://example.com/",
|
||||
"@type" => (RDFS.Resource |> RDF.uri |> to_string)
|
||||
"@type" => RDFS.Resource |> RDF.uri() |> to_string
|
||||
},
|
||||
context: %{},
|
||||
output: %{
|
||||
"@id" => "http://example.com/",
|
||||
"@type" => (RDFS.Resource |> RDF.uri |> to_string)
|
||||
},
|
||||
"@type" => RDFS.Resource |> RDF.uri() |> to_string
|
||||
}
|
||||
},
|
||||
"default language" => %{
|
||||
input: %{
|
||||
|
@ -211,40 +215,40 @@ defmodule JSON.LD.CompactionTest do
|
|||
"term5" => %{"@id" => "http://example.com/term", "@language" => nil},
|
||||
"@language" => "de"
|
||||
},
|
||||
"term5" => [ "v5", "plain literal" ]
|
||||
"term5" => ["v5", "plain literal"]
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
|
||||
describe "keyword aliasing" do
|
||||
%{
|
||||
"@id" => %{
|
||||
input: %{
|
||||
"@id" => "",
|
||||
"@type" => (RDFS.Resource |> RDF.uri |> to_string)
|
||||
"@type" => RDFS.Resource |> RDF.uri() |> to_string
|
||||
},
|
||||
context: %{"id" => "@id"},
|
||||
output: %{
|
||||
"@context" => %{"id" => "@id"},
|
||||
"id" => "",
|
||||
"@type" => (RDFS.Resource |> RDF.uri |> to_string)
|
||||
"@type" => RDFS.Resource |> RDF.uri() |> to_string
|
||||
}
|
||||
},
|
||||
"@type" => %{
|
||||
input: %{
|
||||
"@type" => (RDFS.Resource |> RDF.uri |> to_string),
|
||||
"@type" => RDFS.Resource |> RDF.uri() |> to_string,
|
||||
"http://example.org/foo" => %{"@value" => "bar", "@type" => "http://example.com/type"}
|
||||
},
|
||||
context: %{"type" => "@type"},
|
||||
output: %{
|
||||
"@context" => %{"type" => "@type"},
|
||||
"type" => (RDFS.Resource |> RDF.uri |> to_string),
|
||||
"type" => RDFS.Resource |> RDF.uri() |> to_string,
|
||||
"http://example.org/foo" => %{"@value" => "bar", "type" => "http://example.com/type"}
|
||||
}
|
||||
},
|
||||
|
@ -277,39 +281,43 @@ defmodule JSON.LD.CompactionTest do
|
|||
"@context" => %{"list" => "@list"},
|
||||
"http://example.org/foo" => %{"list" => ["bar"]}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "term selection" do
|
||||
%{
|
||||
"Uses term with nil language when two terms conflict on language" => %{
|
||||
input: [%{
|
||||
"http://example.com/term" => %{"@value" => "v1"}
|
||||
}],
|
||||
input: [
|
||||
%{
|
||||
"http://example.com/term" => %{"@value" => "v1"}
|
||||
}
|
||||
],
|
||||
context: %{
|
||||
"term5" => %{"@id" => "http://example.com/term","@language" => nil},
|
||||
"term5" => %{"@id" => "http://example.com/term", "@language" => nil},
|
||||
"@language" => "de"
|
||||
},
|
||||
output: %{
|
||||
"@context" => %{
|
||||
"term5" => %{"@id" => "http://example.com/term","@language" => nil},
|
||||
"term5" => %{"@id" => "http://example.com/term", "@language" => nil},
|
||||
"@language" => "de"
|
||||
},
|
||||
"term5" => "v1",
|
||||
"term5" => "v1"
|
||||
}
|
||||
},
|
||||
"Uses subject alias" => %{
|
||||
input: [%{
|
||||
"@id" => "http://example.com/id1",
|
||||
"http://example.com/id1" => %{"@value" => "foo", "@language" => "de"}
|
||||
}],
|
||||
input: [
|
||||
%{
|
||||
"@id" => "http://example.com/id1",
|
||||
"http://example.com/id1" => %{"@value" => "foo", "@language" => "de"}
|
||||
}
|
||||
],
|
||||
context: %{
|
||||
"id1" => "http://example.com/id1",
|
||||
"@language" => "de"
|
||||
|
@ -324,83 +332,89 @@ defmodule JSON.LD.CompactionTest do
|
|||
}
|
||||
},
|
||||
"compact-0007" => %{
|
||||
input: Jason.decode!("""
|
||||
{"http://example.org/vocab#contains": "this-is-not-an-IRI"}
|
||||
"""),
|
||||
context: Jason.decode!("""
|
||||
{
|
||||
"ex": "http://example.org/vocab#",
|
||||
"ex:contains": {"@type": "@id"}
|
||||
}
|
||||
"""),
|
||||
output: Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"ex": "http://example.org/vocab#",
|
||||
"ex:contains": {"@type": "@id"}
|
||||
},
|
||||
"http://example.org/vocab#contains": "this-is-not-an-IRI"
|
||||
}
|
||||
"""),
|
||||
input:
|
||||
Jason.decode!("""
|
||||
{"http://example.org/vocab#contains": "this-is-not-an-IRI"}
|
||||
"""),
|
||||
context:
|
||||
Jason.decode!("""
|
||||
{
|
||||
"ex": "http://example.org/vocab#",
|
||||
"ex:contains": {"@type": "@id"}
|
||||
}
|
||||
"""),
|
||||
output:
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"ex": "http://example.org/vocab#",
|
||||
"ex:contains": {"@type": "@id"}
|
||||
},
|
||||
"http://example.org/vocab#contains": "this-is-not-an-IRI"
|
||||
}
|
||||
""")
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "@reverse" do
|
||||
%{
|
||||
"compact-0033" => %{
|
||||
input: Jason.decode!("""
|
||||
[
|
||||
{
|
||||
"@id": "http://example.com/people/markus",
|
||||
"@reverse": {
|
||||
"http://xmlns.com/foaf/0.1/knows": [
|
||||
{
|
||||
"@id": "http://example.com/people/dave",
|
||||
"http://xmlns.com/foaf/0.1/name": [ { "@value": "Dave Longley" } ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"http://xmlns.com/foaf/0.1/name": [ { "@value": "Markus Lanthaler" } ]
|
||||
}
|
||||
]
|
||||
"""),
|
||||
context: Jason.decode!("""
|
||||
{
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
"isKnownBy": { "@reverse": "http://xmlns.com/foaf/0.1/knows" }
|
||||
}
|
||||
"""),
|
||||
output: Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
"isKnownBy": {
|
||||
"@reverse": "http://xmlns.com/foaf/0.1/knows"
|
||||
input:
|
||||
Jason.decode!("""
|
||||
[
|
||||
{
|
||||
"@id": "http://example.com/people/markus",
|
||||
"@reverse": {
|
||||
"http://xmlns.com/foaf/0.1/knows": [
|
||||
{
|
||||
"@id": "http://example.com/people/dave",
|
||||
"http://xmlns.com/foaf/0.1/name": [ { "@value": "Dave Longley" } ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"http://xmlns.com/foaf/0.1/name": [ { "@value": "Markus Lanthaler" } ]
|
||||
}
|
||||
},
|
||||
"@id": "http://example.com/people/markus",
|
||||
"name": "Markus Lanthaler",
|
||||
"isKnownBy": {
|
||||
"@id": "http://example.com/people/dave",
|
||||
"name": "Dave Longley"
|
||||
]
|
||||
"""),
|
||||
context:
|
||||
Jason.decode!("""
|
||||
{
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
"isKnownBy": { "@reverse": "http://xmlns.com/foaf/0.1/knows" }
|
||||
}
|
||||
}
|
||||
""")
|
||||
"""),
|
||||
output:
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
"isKnownBy": {
|
||||
"@reverse": "http://xmlns.com/foaf/0.1/knows"
|
||||
}
|
||||
},
|
||||
"@id": "http://example.com/people/markus",
|
||||
"name": "Markus Lanthaler",
|
||||
"isKnownBy": {
|
||||
"@id": "http://example.com/people/dave",
|
||||
"name": "Dave Longley"
|
||||
}
|
||||
}
|
||||
""")
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "context as value" do
|
||||
|
@ -408,67 +422,88 @@ defmodule JSON.LD.CompactionTest do
|
|||
context = %{
|
||||
"foo" => "http://example.com/"
|
||||
}
|
||||
|
||||
input = %{
|
||||
"http://example.com/" => "bar"
|
||||
}
|
||||
|
||||
expected = %{
|
||||
"@context" => %{
|
||||
"foo" => "http://example.com/"
|
||||
},
|
||||
"foo" => "bar"
|
||||
}
|
||||
|
||||
assert JSON.LD.compact(input, context) == expected
|
||||
end
|
||||
end
|
||||
|
||||
# TODO:
|
||||
# describe "context as reference" do
|
||||
# let(:remote_doc) do
|
||||
# JSON::LD::API::RemoteDocument.new("http://example.com/context", %q({"@context": {"b": "http://example.com/b"}}))
|
||||
# end
|
||||
# test "uses referenced context" do
|
||||
# input = %{
|
||||
# "http://example.com/b" => "c"
|
||||
# }
|
||||
# expected = %{
|
||||
# "@context" => "http://example.com/context",
|
||||
# "b" => "c"
|
||||
# }
|
||||
# allow(JSON::LD::API).to receive(:documentLoader).with("http://example.com/context", anything).and_yield(remote_doc)
|
||||
# jld = JSON::LD::API.compact(input, "http://example.com/context", logger: logger, validate: true)
|
||||
# expect(jld).to produce(expected, logger)
|
||||
# end
|
||||
# end
|
||||
# TODO:
|
||||
# describe "context as reference" do
|
||||
# let(:remote_doc) do
|
||||
# JSON::LD::API::RemoteDocument.new("http://example.com/context", %q({"@context": {"b": "http://example.com/b"}}))
|
||||
# end
|
||||
# test "uses referenced context" do
|
||||
# input = %{
|
||||
# "http://example.com/b" => "c"
|
||||
# }
|
||||
# expected = %{
|
||||
# "@context" => "http://example.com/context",
|
||||
# "b" => "c"
|
||||
# }
|
||||
# allow(JSON::LD::API).to receive(:documentLoader).with("http://example.com/context", anything).and_yield(remote_doc)
|
||||
# jld = JSON::LD::API.compact(input, "http://example.com/context", logger: logger, validate: true)
|
||||
# expect(jld).to produce(expected, logger)
|
||||
# end
|
||||
# end
|
||||
|
||||
describe "@list" do
|
||||
%{
|
||||
"1 term 2 lists 2 languages" => %{
|
||||
input: [%{
|
||||
"http://example.com/foo" => [
|
||||
%{"@list" => [%{"@value" => "en", "@language" => "en"}]},
|
||||
%{"@list" => [%{"@value" => "de", "@language" => "de"}]}
|
||||
]
|
||||
}],
|
||||
input: [
|
||||
%{
|
||||
"http://example.com/foo" => [
|
||||
%{"@list" => [%{"@value" => "en", "@language" => "en"}]},
|
||||
%{"@list" => [%{"@value" => "de", "@language" => "de"}]}
|
||||
]
|
||||
}
|
||||
],
|
||||
context: %{
|
||||
"foo_en" => %{"@id" => "http://example.com/foo", "@container" => "@list", "@language" => "en"},
|
||||
"foo_de" => %{"@id" => "http://example.com/foo", "@container" => "@list", "@language" => "de"}
|
||||
"foo_en" => %{
|
||||
"@id" => "http://example.com/foo",
|
||||
"@container" => "@list",
|
||||
"@language" => "en"
|
||||
},
|
||||
"foo_de" => %{
|
||||
"@id" => "http://example.com/foo",
|
||||
"@container" => "@list",
|
||||
"@language" => "de"
|
||||
}
|
||||
},
|
||||
output: %{
|
||||
"@context" => %{
|
||||
"foo_en" => %{"@id" => "http://example.com/foo", "@container" => "@list", "@language" => "en"},
|
||||
"foo_de" => %{"@id" => "http://example.com/foo", "@container" => "@list", "@language" => "de"}
|
||||
"foo_en" => %{
|
||||
"@id" => "http://example.com/foo",
|
||||
"@container" => "@list",
|
||||
"@language" => "en"
|
||||
},
|
||||
"foo_de" => %{
|
||||
"@id" => "http://example.com/foo",
|
||||
"@container" => "@list",
|
||||
"@language" => "de"
|
||||
}
|
||||
},
|
||||
"foo_en" => ["en"],
|
||||
"foo_de" => ["de"]
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "language maps" do
|
||||
|
@ -499,14 +534,14 @@ defmodule JSON.LD.CompactionTest do
|
|||
"de" => ["Die Königin", "Ihre Majestät"]
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "@graph" do
|
||||
|
@ -520,18 +555,18 @@ defmodule JSON.LD.CompactionTest do
|
|||
output: %{
|
||||
"@context" => %{"ex" => "http://example.com/"},
|
||||
"@graph" => [
|
||||
%{"ex:foo" => "foo"},
|
||||
%{"ex:foo" => "foo"},
|
||||
%{"ex:bar" => "bar"}
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.compact(data.input, data.context) == data.output
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "exceptions" do
|
||||
|
@ -548,14 +583,13 @@ defmodule JSON.LD.CompactionTest do
|
|||
"http://example.org/foo" => [%{"@list" => ["baz"]}]
|
||||
},
|
||||
exception: JSON.LD.ListOfListsError
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert_raise data.exception, fn -> JSON.LD.compact(data.input, %{}) end
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert_raise data.exception, fn -> JSON.LD.compact(data.input, %{}) end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@ defmodule JSON.LD.ContextTest do
|
|||
|
||||
test "extracts @vocab" do
|
||||
assert JSON.LD.context(%{"@vocab" => "http://schema.org/"}).vocab ==
|
||||
"http://schema.org/"
|
||||
"http://schema.org/"
|
||||
end
|
||||
|
||||
test "maps term with IRI value" do
|
||||
|
@ -32,40 +32,39 @@ defmodule JSON.LD.ContextTest do
|
|||
end
|
||||
|
||||
test "associates @list container mapping with predicate" do
|
||||
c = JSON.LD.context(%{"foo" =>
|
||||
%{"@id" => "http://example.com/", "@container" => "@list"}})
|
||||
c = JSON.LD.context(%{"foo" => %{"@id" => "http://example.com/", "@container" => "@list"}})
|
||||
assert c.term_defs["foo"]
|
||||
assert c.term_defs["foo"].iri_mapping == "http://example.com/"
|
||||
assert c.term_defs["foo"].container_mapping == "@list"
|
||||
end
|
||||
|
||||
test "associates @set container mapping with predicate" do
|
||||
c = JSON.LD.context(%{"foo" =>
|
||||
%{"@id" => "http://example.com/", "@container" => "@set"}})
|
||||
c = JSON.LD.context(%{"foo" => %{"@id" => "http://example.com/", "@container" => "@set"}})
|
||||
assert c.term_defs["foo"]
|
||||
assert c.term_defs["foo"].iri_mapping == "http://example.com/"
|
||||
assert c.term_defs["foo"].container_mapping == "@set"
|
||||
end
|
||||
|
||||
test "associates @id container mapping with predicate" do
|
||||
c = JSON.LD.context(%{"foo" =>
|
||||
%{"@id" => "http://example.com/", "@type" => "@id"}})
|
||||
c = JSON.LD.context(%{"foo" => %{"@id" => "http://example.com/", "@type" => "@id"}})
|
||||
assert c.term_defs["foo"]
|
||||
assert c.term_defs["foo"].iri_mapping == "http://example.com/"
|
||||
assert c.term_defs["foo"].type_mapping == "@id"
|
||||
end
|
||||
|
||||
test "associates type mapping with predicate" do
|
||||
c = JSON.LD.context(%{"foo" =>
|
||||
%{"@id" => "http://example.com/", "@type" => to_string(XSD.string)}})
|
||||
c =
|
||||
JSON.LD.context(%{
|
||||
"foo" => %{"@id" => "http://example.com/", "@type" => to_string(XSD.string())}
|
||||
})
|
||||
|
||||
assert c.term_defs["foo"]
|
||||
assert c.term_defs["foo"].iri_mapping == "http://example.com/"
|
||||
assert c.term_defs["foo"].type_mapping == to_string(XSD.string)
|
||||
assert c.term_defs["foo"].type_mapping == to_string(XSD.string())
|
||||
end
|
||||
|
||||
test "associates language mapping with predicate" do
|
||||
c = JSON.LD.context(%{"foo" =>
|
||||
%{"@id" => "http://example.com/", "@language" => "en"}})
|
||||
c = JSON.LD.context(%{"foo" => %{"@id" => "http://example.com/", "@language" => "en"}})
|
||||
assert c.term_defs["foo"]
|
||||
assert c.term_defs["foo"].iri_mapping == "http://example.com/"
|
||||
assert c.term_defs["foo"].language_mapping == "en"
|
||||
|
@ -73,20 +72,24 @@ defmodule JSON.LD.ContextTest do
|
|||
|
||||
test "expands chains of term definition/use with string values" do
|
||||
assert JSON.LD.context(%{
|
||||
"foo" => "bar",
|
||||
"bar" => "baz",
|
||||
"baz" => "http://example.com/"
|
||||
}) |> iri_mappings == %{
|
||||
"foo" => "http://example.com/",
|
||||
"bar" => "http://example.com/",
|
||||
"baz" => "http://example.com/"
|
||||
}
|
||||
"foo" => "bar",
|
||||
"bar" => "baz",
|
||||
"baz" => "http://example.com/"
|
||||
})
|
||||
|> iri_mappings == %{
|
||||
"foo" => "http://example.com/",
|
||||
"bar" => "http://example.com/",
|
||||
"baz" => "http://example.com/"
|
||||
}
|
||||
end
|
||||
|
||||
test "expands terms using @vocab" do
|
||||
c = JSON.LD.context(%{
|
||||
"foo" => "bar",
|
||||
"@vocab" => "http://example.com/"})
|
||||
c =
|
||||
JSON.LD.context(%{
|
||||
"foo" => "bar",
|
||||
"@vocab" => "http://example.com/"
|
||||
})
|
||||
|
||||
assert c.term_defs["foo"]
|
||||
assert c.term_defs["foo"].iri_mapping == "http://example.com/bar"
|
||||
end
|
||||
|
@ -95,37 +98,41 @@ defmodule JSON.LD.ContextTest do
|
|||
describe "create from Array/List" do
|
||||
test "merges definitions from each context" do
|
||||
assert JSON.LD.context([
|
||||
%{"foo" => "http://example.com/foo"},
|
||||
%{"bar" => "foo"}
|
||||
]) |> iri_mappings == %{
|
||||
"foo" => "http://example.com/foo",
|
||||
"bar" => "http://example.com/foo"
|
||||
}
|
||||
%{"foo" => "http://example.com/foo"},
|
||||
%{"bar" => "foo"}
|
||||
])
|
||||
|> iri_mappings == %{
|
||||
"foo" => "http://example.com/foo",
|
||||
"bar" => "http://example.com/foo"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "term definitions with null values" do
|
||||
test "removes @language if set to null" do
|
||||
assert JSON.LD.context([
|
||||
%{ "@language" => "en" },
|
||||
%{ "@language" => nil }
|
||||
]).default_language == nil
|
||||
%{"@language" => "en"},
|
||||
%{"@language" => nil}
|
||||
]).default_language == nil
|
||||
end
|
||||
|
||||
test "removes @vocab if set to null" do
|
||||
assert JSON.LD.context([
|
||||
%{ "@vocab" => "http://schema.org/" },
|
||||
%{ "@vocab" => nil }
|
||||
]).vocab == nil
|
||||
%{"@vocab" => "http://schema.org/"},
|
||||
%{"@vocab" => nil}
|
||||
]).vocab == nil
|
||||
end
|
||||
|
||||
test "removes term if set to null with @vocab" do
|
||||
assert JSON.LD.context([%{
|
||||
"@vocab" => "http://schema.org/",
|
||||
"term" => nil
|
||||
}]) |> iri_mappings == %{
|
||||
"term" => nil
|
||||
}
|
||||
assert JSON.LD.context([
|
||||
%{
|
||||
"@vocab" => "http://schema.org/",
|
||||
"term" => nil
|
||||
}
|
||||
])
|
||||
|> iri_mappings == %{
|
||||
"term" => nil
|
||||
}
|
||||
end
|
||||
|
||||
test "removes a term definition" do
|
||||
|
@ -133,7 +140,7 @@ defmodule JSON.LD.ContextTest do
|
|||
end
|
||||
|
||||
test "loads initial context" do
|
||||
init_ec = JSON.LD.Context.new
|
||||
init_ec = JSON.LD.Context.new()
|
||||
nil_ec = JSON.LD.context(nil)
|
||||
assert nil_ec.default_language == init_ec.default_language
|
||||
assert nil_ec |> coercions == init_ec |> coercions
|
||||
|
@ -146,87 +153,87 @@ defmodule JSON.LD.ContextTest do
|
|||
describe "errors" do
|
||||
%{
|
||||
"no @id, @type, or @container" => %{
|
||||
input: %{"foo" => %{}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
input: %{"foo" => %{}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
"value as array" => %{
|
||||
input: %{"foo" => []},
|
||||
exception: JSON.LD.InvalidTermDefinitionError
|
||||
},
|
||||
input: %{"foo" => []},
|
||||
exception: JSON.LD.InvalidTermDefinitionError
|
||||
},
|
||||
"@id as object" => %{
|
||||
input: %{"foo" => %{"@id" => %{}}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@id" => %{}}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
"@id as array of object" => %{
|
||||
input: %{"foo" => %{"@id" => [{}]}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@id" => [{}]}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
"@id as array of null" => %{
|
||||
input: %{"foo" => %{"@id" => [nil]}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@id" => [nil]}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
"@type as object" => %{
|
||||
input: %{"foo" => %{"@type" => %{}}},
|
||||
exception: JSON.LD.InvalidTypeMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@type" => %{}}},
|
||||
exception: JSON.LD.InvalidTypeMappingError
|
||||
},
|
||||
"@type as array" => %{
|
||||
input: %{"foo" => %{"@type" => []}},
|
||||
exception: JSON.LD.InvalidTypeMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@type" => []}},
|
||||
exception: JSON.LD.InvalidTypeMappingError
|
||||
},
|
||||
"@type as @list" => %{
|
||||
input: %{"foo" => %{"@type" => "@list"}},
|
||||
exception: JSON.LD.InvalidTypeMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@type" => "@list"}},
|
||||
exception: JSON.LD.InvalidTypeMappingError
|
||||
},
|
||||
"@type as @set" => %{
|
||||
input: %{"foo" => %{"@type" => "@set"}},
|
||||
exception: JSON.LD.InvalidTypeMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@type" => "@set"}},
|
||||
exception: JSON.LD.InvalidTypeMappingError
|
||||
},
|
||||
"@container as object" => %{
|
||||
input: %{"foo" => %{"@container" => %{}}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@container" => %{}}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
"@container as array" => %{
|
||||
input: %{"foo" => %{"@container" => []}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@container" => []}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
"@container as string" => %{
|
||||
input: %{"foo" => %{"@container" => "true"}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
input: %{"foo" => %{"@container" => "true"}},
|
||||
exception: JSON.LD.InvalidIRIMappingError
|
||||
},
|
||||
"@language as @id" => %{
|
||||
input: %{"@language" => %{"@id" => "http://example.com/"}},
|
||||
exception: JSON.LD.InvalidDefaultLanguageError
|
||||
},
|
||||
input: %{"@language" => %{"@id" => "http://example.com/"}},
|
||||
exception: JSON.LD.InvalidDefaultLanguageError
|
||||
},
|
||||
"@vocab as @id" => %{
|
||||
input: %{"@vocab" => %{"@id" => "http://example.com/"}},
|
||||
exception: JSON.LD.InvalidVocabMappingError
|
||||
},
|
||||
input: %{"@vocab" => %{"@id" => "http://example.com/"}},
|
||||
exception: JSON.LD.InvalidVocabMappingError
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert_raise data.exception, fn ->
|
||||
JSON.LD.context(data.input)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert_raise data.exception, fn ->
|
||||
JSON.LD.context(data.input)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
(JSON.LD.keywords -- ~w[@base @language @vocab])
|
||||
(JSON.LD.keywords() -- ~w[@base @language @vocab])
|
||||
|> Enum.each(fn keyword ->
|
||||
@tag keyword: keyword
|
||||
test "does not redefine #{keyword} as a string", %{keyword: keyword} do
|
||||
assert_raise JSON.LD.KeywordRedefinitionError, fn ->
|
||||
JSON.LD.context(%{"@context" => %{keyword => "http://example.com/"}})
|
||||
end
|
||||
end
|
||||
@tag keyword: keyword
|
||||
test "does not redefine #{keyword} as a string", %{keyword: keyword} do
|
||||
assert_raise JSON.LD.KeywordRedefinitionError, fn ->
|
||||
JSON.LD.context(%{"@context" => %{keyword => "http://example.com/"}})
|
||||
end
|
||||
end
|
||||
|
||||
@tag keyword: keyword
|
||||
test "does not redefine #{keyword} with an @id", %{keyword: keyword} do
|
||||
assert_raise JSON.LD.KeywordRedefinitionError, fn ->
|
||||
JSON.LD.context(%{"@context" => %{keyword => %{"@id" => "http://example.com/"}}})
|
||||
end
|
||||
end
|
||||
end)
|
||||
@tag keyword: keyword
|
||||
test "does not redefine #{keyword} with an @id", %{keyword: keyword} do
|
||||
assert_raise JSON.LD.KeywordRedefinitionError, fn ->
|
||||
JSON.LD.context(%{"@context" => %{keyword => %{"@id" => "http://example.com/"}}})
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
# TODO: "Furthermore, the term must not be an empty string ("") as not all programming languages are able to handle empty JSON keys." -- https://www.w3.org/TR/json-ld/#terms
|
||||
|
@ -237,29 +244,27 @@ defmodule JSON.LD.ContextTest do
|
|||
@tag :skip
|
||||
test "warn on terms starting with a @"
|
||||
|
||||
|
||||
def iri_mappings(%JSON.LD.Context{term_defs: term_defs}) do
|
||||
Enum.reduce term_defs, %{}, fn ({term, term_def}, iri_mappings) ->
|
||||
Map.put iri_mappings, term, (term_def && term_def.iri_mapping) || nil
|
||||
end
|
||||
Enum.reduce(term_defs, %{}, fn {term, term_def}, iri_mappings ->
|
||||
Map.put(iri_mappings, term, (term_def && term_def.iri_mapping) || nil)
|
||||
end)
|
||||
end
|
||||
|
||||
def languages(%JSON.LD.Context{term_defs: term_defs}) do
|
||||
Enum.reduce term_defs, %{}, fn ({term, term_def}, language_mappings) ->
|
||||
Map.put language_mappings, term, term_def.language_mapping
|
||||
end
|
||||
Enum.reduce(term_defs, %{}, fn {term, term_def}, language_mappings ->
|
||||
Map.put(language_mappings, term, term_def.language_mapping)
|
||||
end)
|
||||
end
|
||||
|
||||
def coercions(%JSON.LD.Context{term_defs: term_defs}) do
|
||||
Enum.reduce term_defs, %{}, fn ({term, term_def}, type_mappings) ->
|
||||
Map.put type_mappings, term, term_def.type_mapping
|
||||
end
|
||||
Enum.reduce(term_defs, %{}, fn {term, term_def}, type_mappings ->
|
||||
Map.put(type_mappings, term, term_def.type_mapping)
|
||||
end)
|
||||
end
|
||||
|
||||
def containers(%JSON.LD.Context{term_defs: term_defs}) do
|
||||
Enum.reduce term_defs, %{}, fn ({term, term_def}, type_mappings) ->
|
||||
Map.put type_mappings, term, term_def.container_mapping
|
||||
end
|
||||
Enum.reduce(term_defs, %{}, fn {term, term_def}, type_mappings ->
|
||||
Map.put(type_mappings, term, term_def.container_mapping)
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -12,14 +12,13 @@ defmodule JSON.LD.DecoderTest do
|
|||
defmodule TestNS do
|
||||
use RDF.Vocabulary.Namespace
|
||||
defvocab EX, base_iri: "http://example.org/#", terms: [], strict: false
|
||||
defvocab S, base_iri: "http://schema.org/", terms: [], strict: false
|
||||
defvocab S, base_iri: "http://schema.org/", terms: [], strict: false
|
||||
end
|
||||
|
||||
alias TestNS.{EX, S}
|
||||
|
||||
|
||||
test "an empty JSON document is deserialized to an empty graph" do
|
||||
assert JSON.LD.Decoder.decode!("{}") == Dataset.new
|
||||
assert JSON.LD.Decoder.decode!("{}") == Dataset.new()
|
||||
end
|
||||
|
||||
describe "unnamed nodes" do
|
||||
|
@ -43,14 +42,14 @@ defmodule JSON.LD.DecoderTest do
|
|||
"http://example.com/foo": {"@id": "_:a"}
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, RDF.bnode("b0")}
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "nodes with @id" do
|
||||
|
@ -61,14 +60,14 @@ defmodule JSON.LD.DecoderTest do
|
|||
"http://example.com/foo": "bar"
|
||||
}),
|
||||
{~I<http://example.com/a>, ~I<http://example.com/foo>, RDF.literal("bar")}
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|
||||
%{
|
||||
"base" => {
|
||||
|
@ -76,30 +75,30 @@ defmodule JSON.LD.DecoderTest do
|
|||
"@id": "",
|
||||
"@type": "#{RDF.uri(RDFS.Resource)}"
|
||||
}),
|
||||
{~I<http://example.org/>, NS.RDF.type, RDF.uri(RDFS.Resource)}
|
||||
{~I<http://example.org/>, NS.RDF.type(), RDF.uri(RDFS.Resource)}
|
||||
},
|
||||
"relative" => {
|
||||
"relative" => {
|
||||
~s({
|
||||
"@id": "a/b",
|
||||
"@type": "#{RDF.uri(RDFS.Resource)}"
|
||||
}),
|
||||
{~I<http://example.org/a/b>, NS.RDF.type, RDF.uri(RDFS.Resource)}
|
||||
{~I<http://example.org/a/b>, NS.RDF.type(), RDF.uri(RDFS.Resource)}
|
||||
},
|
||||
"hash" => {
|
||||
~s({
|
||||
"@id": "#a",
|
||||
"@type": "#{RDF.uri(RDFS.Resource)}"
|
||||
}),
|
||||
{~I<http://example.org/#a>, NS.RDF.type, RDF.uri(RDFS.Resource)}
|
||||
},
|
||||
{~I<http://example.org/#a>, NS.RDF.type(), RDF.uri(RDFS.Resource)}
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test "when relative IRIs #{title}", %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input, base: "http://example.org/") ==
|
||||
RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test "when relative IRIs #{title}", %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input, base: "http://example.org/") ==
|
||||
RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "typed nodes" do
|
||||
|
@ -108,30 +107,30 @@ defmodule JSON.LD.DecoderTest do
|
|||
~s({
|
||||
"@type": "http://example.com/foo"
|
||||
}),
|
||||
{RDF.bnode("b0"), NS.RDF.type, ~I<http://example.com/foo>}
|
||||
{RDF.bnode("b0"), NS.RDF.type(), ~I<http://example.com/foo>}
|
||||
},
|
||||
"two types" => {
|
||||
~s({
|
||||
"@type": ["http://example.com/foo", "http://example.com/baz"]
|
||||
}),
|
||||
[
|
||||
{RDF.bnode("b0"), NS.RDF.type, ~I<http://example.com/foo>},
|
||||
{RDF.bnode("b0"), NS.RDF.type, ~I<http://example.com/baz>},
|
||||
{RDF.bnode("b0"), NS.RDF.type(), ~I<http://example.com/foo>},
|
||||
{RDF.bnode("b0"), NS.RDF.type(), ~I<http://example.com/baz>}
|
||||
]
|
||||
},
|
||||
"blank node type" => {
|
||||
~s({
|
||||
"@type": "_:foo"
|
||||
}),
|
||||
{RDF.bnode("b1"), NS.RDF.type, RDF.bnode("b0")}
|
||||
{RDF.bnode("b1"), NS.RDF.type(), RDF.bnode("b0")}
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "key/value" do
|
||||
|
@ -148,7 +147,7 @@ defmodule JSON.LD.DecoderTest do
|
|||
}),
|
||||
[
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, RDF.literal("bar")},
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, RDF.literal("baz")},
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, RDF.literal("baz")}
|
||||
]
|
||||
},
|
||||
"IRI" => {
|
||||
|
@ -163,37 +162,35 @@ defmodule JSON.LD.DecoderTest do
|
|||
}),
|
||||
[
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, ~I<http://example.com/bar>},
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, ~I<http://example.com/baz>},
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, ~I<http://example.com/baz>}
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "literals" do
|
||||
%{
|
||||
"plain literal" =>
|
||||
{
|
||||
"plain literal" => {
|
||||
~s({"@id": "http://greggkellogg.net/foaf#me", "http://xmlns.com/foaf/0.1/name": "Gregg Kellogg"}),
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/name>, RDF.literal("Gregg Kellogg")},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/name>,
|
||||
RDF.literal("Gregg Kellogg")}
|
||||
},
|
||||
"explicit plain literal" =>
|
||||
{
|
||||
"explicit plain literal" => {
|
||||
~s({"http://xmlns.com/foaf/0.1/name": {"@value": "Gregg Kellogg"}}),
|
||||
{RDF.bnode("b0"), ~I<http://xmlns.com/foaf/0.1/name>, RDF.literal("Gregg Kellogg")}
|
||||
},
|
||||
"language tagged literal" =>
|
||||
{
|
||||
"language tagged literal" => {
|
||||
~s({"http://www.w3.org/2000/01/rdf-schema#label": {"@value": "A plain literal with a lang tag.", "@language": "en-us"}}),
|
||||
{RDF.bnode("b0"), RDFS.label, RDF.literal("A plain literal with a lang tag.", language: "en-us")}
|
||||
{RDF.bnode("b0"), RDFS.label(),
|
||||
RDF.literal("A plain literal with a lang tag.", language: "en-us")}
|
||||
},
|
||||
"I18N literal with language" =>
|
||||
{
|
||||
"I18N literal with language" => {
|
||||
~s([{
|
||||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
"http://xmlns.com/foaf/0.1/knows": {"@id": "http://www.ivan-herman.net/foaf#me"}
|
||||
|
@ -202,50 +199,52 @@ defmodule JSON.LD.DecoderTest do
|
|||
"http://xmlns.com/foaf/0.1/name": {"@value": "Herman Iván", "@language": "hu"}
|
||||
}]),
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, ~I<http://www.ivan-herman.net/foaf#me>},
|
||||
{~I<http://www.ivan-herman.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/name>, RDF.literal("Herman Iv\u00E1n", language: "hu")},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>,
|
||||
~I<http://www.ivan-herman.net/foaf#me>},
|
||||
{~I<http://www.ivan-herman.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/name>,
|
||||
RDF.literal("Herman Iv\u00E1n", language: "hu")}
|
||||
]
|
||||
},
|
||||
"explicit datatyped literal" =>
|
||||
{
|
||||
"explicit datatyped literal" => {
|
||||
~s({
|
||||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
"http://purl.org/dc/terms/created": {"@value": "1957-02-27", "@type": "http://www.w3.org/2001/XMLSchema#date"}
|
||||
}),
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://purl.org/dc/terms/created>, RDF.literal("1957-02-27", datatype: XSD.date)},
|
||||
},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://purl.org/dc/terms/created>,
|
||||
RDF.literal("1957-02-27", datatype: XSD.date())}
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "prefixes" do
|
||||
%{
|
||||
"empty prefix" => {
|
||||
~s({"@context": {"": "http://example.com/default#"}, ":foo": "bar"}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/default#foo>, RDF.literal("bar")}
|
||||
},
|
||||
# TODO:
|
||||
"empty suffix" => {
|
||||
~s({"@context": {"prefix": "http://example.com/default#"}, "prefix:": "bar"}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/default#>, RDF.literal("bar")}
|
||||
},
|
||||
"prefix:suffix" => {
|
||||
~s({"@context": {"prefix": "http://example.com/default#"}, "prefix:foo": "bar"}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/default#foo>, RDF.literal("bar")}
|
||||
}
|
||||
"empty prefix" => {
|
||||
~s({"@context": {"": "http://example.com/default#"}, ":foo": "bar"}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/default#foo>, RDF.literal("bar")}
|
||||
},
|
||||
# TODO:
|
||||
"empty suffix" => {
|
||||
~s({"@context": {"prefix": "http://example.com/default#"}, "prefix:": "bar"}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/default#>, RDF.literal("bar")}
|
||||
},
|
||||
"prefix:suffix" => {
|
||||
~s({"@context": {"prefix": "http://example.com/default#"}, "prefix:foo": "bar"}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/default#foo>, RDF.literal("bar")}
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
if title == "empty suffix", do: @tag :skip
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
if title == "empty suffix", do: @tag(:skip)
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "overriding keywords" do
|
||||
|
@ -258,23 +257,23 @@ defmodule JSON.LD.DecoderTest do
|
|||
"name": "Gregg Kellogg"
|
||||
}),
|
||||
[
|
||||
{~I<http://example.com/about#gregg>, NS.RDF.type, ~I<http://schema.org/Person>},
|
||||
{~I<http://example.com/about#gregg>, ~I<http://schema.org/name>, RDF.literal("Gregg Kellogg")},
|
||||
{~I<http://example.com/about#gregg>, NS.RDF.type(), ~I<http://schema.org/Person>},
|
||||
{~I<http://example.com/about#gregg>, ~I<http://schema.org/name>,
|
||||
RDF.literal("Gregg Kellogg")}
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "chaining" do
|
||||
%{
|
||||
"explicit subject" =>
|
||||
{
|
||||
"explicit subject" => {
|
||||
~s({
|
||||
"@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
|
||||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
|
@ -284,12 +283,13 @@ defmodule JSON.LD.DecoderTest do
|
|||
}
|
||||
}),
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, ~I<http://www.ivan-herman.net/foaf#me>},
|
||||
{~I<http://www.ivan-herman.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/name>, RDF.literal("Ivan Herman")},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>,
|
||||
~I<http://www.ivan-herman.net/foaf#me>},
|
||||
{~I<http://www.ivan-herman.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/name>,
|
||||
RDF.literal("Ivan Herman")}
|
||||
]
|
||||
},
|
||||
"implicit subject" =>
|
||||
{
|
||||
"implicit subject" => {
|
||||
~s({
|
||||
"@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
|
||||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
|
@ -298,40 +298,42 @@ defmodule JSON.LD.DecoderTest do
|
|||
}
|
||||
}),
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, RDF.bnode("b0")},
|
||||
{RDF.bnode("b0"), ~I<http://xmlns.com/foaf/0.1/name>, RDF.literal("Manu Sporny")},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>,
|
||||
RDF.bnode("b0")},
|
||||
{RDF.bnode("b0"), ~I<http://xmlns.com/foaf/0.1/name>, RDF.literal("Manu Sporny")}
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "multiple values" do
|
||||
%{
|
||||
"literals" =>
|
||||
{
|
||||
"literals" => {
|
||||
~s({
|
||||
"@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
|
||||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
"foaf:knows": ["Manu Sporny", "Ivan Herman"]
|
||||
}),
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, RDF.literal("Manu Sporny")},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, RDF.literal("Ivan Herman")},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>,
|
||||
RDF.literal("Manu Sporny")},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>,
|
||||
RDF.literal("Ivan Herman")}
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "lists" do
|
||||
|
@ -342,22 +344,23 @@ defmodule JSON.LD.DecoderTest do
|
|||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
"foaf:knows": {"@list": []}
|
||||
}),
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, NS.RDF.nil}
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, NS.RDF.nil()}
|
||||
},
|
||||
"single value" => {
|
||||
~s({
|
||||
"single value" => {
|
||||
~s({
|
||||
"@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
|
||||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
"foaf:knows": {"@list": ["Manu Sporny"]}
|
||||
}),
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, RDF.bnode("b0")},
|
||||
{RDF.bnode("b0"), NS.RDF.first, RDF.literal("Manu Sporny")},
|
||||
{RDF.bnode("b0"), NS.RDF.rest, NS.RDF.nil},
|
||||
]
|
||||
},
|
||||
"single value (with coercion)" => {
|
||||
~s({
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>,
|
||||
RDF.bnode("b0")},
|
||||
{RDF.bnode("b0"), NS.RDF.first(), RDF.literal("Manu Sporny")},
|
||||
{RDF.bnode("b0"), NS.RDF.rest(), NS.RDF.nil()}
|
||||
]
|
||||
},
|
||||
"single value (with coercion)" => {
|
||||
~s({
|
||||
"@context": {
|
||||
"foaf": "http://xmlns.com/foaf/0.1/",
|
||||
"foaf:knows": { "@container": "@list"}
|
||||
|
@ -365,39 +368,40 @@ defmodule JSON.LD.DecoderTest do
|
|||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
"foaf:knows": ["Manu Sporny"]
|
||||
}),
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, RDF.bnode("b0")},
|
||||
{RDF.bnode("b0"), NS.RDF.first, RDF.literal("Manu Sporny")},
|
||||
{RDF.bnode("b0"), NS.RDF.rest, NS.RDF.nil},
|
||||
]
|
||||
},
|
||||
"multiple values" => {
|
||||
~s({
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>,
|
||||
RDF.bnode("b0")},
|
||||
{RDF.bnode("b0"), NS.RDF.first(), RDF.literal("Manu Sporny")},
|
||||
{RDF.bnode("b0"), NS.RDF.rest(), NS.RDF.nil()}
|
||||
]
|
||||
},
|
||||
"multiple values" => {
|
||||
~s({
|
||||
"@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
|
||||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
"foaf:knows": {"@list": ["Manu Sporny", "Dave Longley"]}
|
||||
}),
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, RDF.bnode("b0")},
|
||||
{RDF.bnode("b0"), NS.RDF.first, RDF.literal("Manu Sporny")},
|
||||
{RDF.bnode("b0"), NS.RDF.rest, RDF.bnode("b1")},
|
||||
{RDF.bnode("b1"), NS.RDF.first, RDF.literal("Dave Longley")},
|
||||
{RDF.bnode("b1"), NS.RDF.rest, NS.RDF.nil},
|
||||
]
|
||||
},
|
||||
[
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>,
|
||||
RDF.bnode("b0")},
|
||||
{RDF.bnode("b0"), NS.RDF.first(), RDF.literal("Manu Sporny")},
|
||||
{RDF.bnode("b0"), NS.RDF.rest(), RDF.bnode("b1")},
|
||||
{RDF.bnode("b1"), NS.RDF.first(), RDF.literal("Dave Longley")},
|
||||
{RDF.bnode("b1"), NS.RDF.rest(), NS.RDF.nil()}
|
||||
]
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "context" do
|
||||
%{
|
||||
"@id coersion" =>
|
||||
{
|
||||
"@id coersion" => {
|
||||
~s({
|
||||
"@context": {
|
||||
"knows": {"@id": "http://xmlns.com/foaf/0.1/knows", "@type": "@id"}
|
||||
|
@ -405,10 +409,10 @@ defmodule JSON.LD.DecoderTest do
|
|||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
"knows": "http://www.ivan-herman.net/foaf#me"
|
||||
}),
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>, ~I<http://www.ivan-herman.net/foaf#me>},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://xmlns.com/foaf/0.1/knows>,
|
||||
~I<http://www.ivan-herman.net/foaf#me>}
|
||||
},
|
||||
"datatype coersion" =>
|
||||
{
|
||||
"datatype coersion" => {
|
||||
~s({
|
||||
"@context": {
|
||||
"dcterms": "http://purl.org/dc/terms/",
|
||||
|
@ -418,7 +422,8 @@ defmodule JSON.LD.DecoderTest do
|
|||
"@id": "http://greggkellogg.net/foaf#me",
|
||||
"created": "1957-02-27"
|
||||
}),
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://purl.org/dc/terms/created>, RDF.literal("1957-02-27", datatype: XSD.date)},
|
||||
{~I<http://greggkellogg.net/foaf#me>, ~I<http://purl.org/dc/terms/created>,
|
||||
RDF.literal("1957-02-27", datatype: XSD.date())}
|
||||
},
|
||||
"sub-objects with context" => {
|
||||
~s({
|
||||
|
@ -428,10 +433,10 @@ defmodule JSON.LD.DecoderTest do
|
|||
"foo": "bar"
|
||||
}
|
||||
}),
|
||||
[
|
||||
[
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, RDF.bnode("b1")},
|
||||
{RDF.bnode("b1"), ~I<http://example.org/foo>, RDF.literal("bar")},
|
||||
]
|
||||
{RDF.bnode("b1"), ~I<http://example.org/foo>, RDF.literal("bar")}
|
||||
]
|
||||
},
|
||||
"contexts with a list processed in order" => {
|
||||
~s({
|
||||
|
@ -441,7 +446,7 @@ defmodule JSON.LD.DecoderTest do
|
|||
],
|
||||
"foo": "bar"
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://example.org/foo>, RDF.literal("bar")},
|
||||
{RDF.bnode("b0"), ~I<http://example.org/foo>, RDF.literal("bar")}
|
||||
},
|
||||
"term definition resolves term as IRI" => {
|
||||
~s({
|
||||
|
@ -451,7 +456,7 @@ defmodule JSON.LD.DecoderTest do
|
|||
],
|
||||
"bar": "bar"
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, RDF.literal("bar")},
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo>, RDF.literal("bar")}
|
||||
},
|
||||
"term definition resolves prefix as IRI" => {
|
||||
~s({
|
||||
|
@ -461,7 +466,7 @@ defmodule JSON.LD.DecoderTest do
|
|||
],
|
||||
"bar": "bar"
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo#bar>, RDF.literal("bar")},
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo#bar>, RDF.literal("bar")}
|
||||
},
|
||||
"@language" => {
|
||||
~s({
|
||||
|
@ -471,7 +476,7 @@ defmodule JSON.LD.DecoderTest do
|
|||
},
|
||||
"foo:bar": "baz"
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo#bar>, RDF.literal("baz", language: "en")},
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo#bar>, RDF.literal("baz", language: "en")}
|
||||
},
|
||||
"@language with override" => {
|
||||
~s({
|
||||
|
@ -481,7 +486,7 @@ defmodule JSON.LD.DecoderTest do
|
|||
},
|
||||
"foo:bar": {"@value": "baz", "@language": "fr"}
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo#bar>, RDF.literal("baz", language: "fr")},
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo#bar>, RDF.literal("baz", language: "fr")}
|
||||
},
|
||||
"@language with plain" => {
|
||||
~s({
|
||||
|
@ -491,15 +496,15 @@ defmodule JSON.LD.DecoderTest do
|
|||
},
|
||||
"foo:bar": {"@value": "baz"}
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo#bar>, RDF.literal("baz")},
|
||||
},
|
||||
{RDF.bnode("b0"), ~I<http://example.com/foo#bar>, RDF.literal("baz")}
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|
||||
%{
|
||||
"dt with term" => {
|
||||
|
@ -510,7 +515,7 @@ defmodule JSON.LD.DecoderTest do
|
|||
],
|
||||
"foo": "bar"
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://example.org/foo#>, RDF.literal("bar", datatype: XSD.date)},
|
||||
{RDF.bnode("b0"), ~I<http://example.org/foo#>, RDF.literal("bar", datatype: XSD.date())}
|
||||
},
|
||||
"@id with term" => {
|
||||
~s({
|
||||
|
@ -519,7 +524,7 @@ defmodule JSON.LD.DecoderTest do
|
|||
],
|
||||
"foo": "http://example.org/foo#bar"
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://example.org/foo#bar>, ~I<http://example.org/foo#bar>},
|
||||
{RDF.bnode("b0"), ~I<http://example.org/foo#bar>, ~I<http://example.org/foo#bar>}
|
||||
},
|
||||
"coercion without term definition" => {
|
||||
~s({
|
||||
|
@ -534,15 +539,16 @@ defmodule JSON.LD.DecoderTest do
|
|||
],
|
||||
"dc:date": "2011-11-23"
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://purl.org/dc/terms/date>, RDF.literal("2011-11-23", datatype: XSD.date)},
|
||||
},
|
||||
{RDF.bnode("b0"), ~I<http://purl.org/dc/terms/date>,
|
||||
RDF.literal("2011-11-23", datatype: XSD.date())}
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test "term def with @id + @type coercion: #{title}", %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test "term def with @id + @type coercion: #{title}", %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|
||||
%{
|
||||
"dt with term" => {
|
||||
|
@ -555,8 +561,8 @@ defmodule JSON.LD.DecoderTest do
|
|||
}),
|
||||
[
|
||||
{RDF.bnode("b0"), ~I<http://example.org/foo#>, RDF.bnode("b1")},
|
||||
{RDF.bnode("b1"), NS.RDF.first, RDF.literal("bar", datatype: XSD.date)},
|
||||
{RDF.bnode("b1"), NS.RDF.rest, NS.RDF.nil},
|
||||
{RDF.bnode("b1"), NS.RDF.first(), RDF.literal("bar", datatype: XSD.date())},
|
||||
{RDF.bnode("b1"), NS.RDF.rest(), NS.RDF.nil()}
|
||||
]
|
||||
},
|
||||
"@id with term" => {
|
||||
|
@ -568,17 +574,17 @@ defmodule JSON.LD.DecoderTest do
|
|||
}),
|
||||
[
|
||||
{RDF.bnode("b0"), ~I<http://example.org/foo#bar>, RDF.bnode("b1")},
|
||||
{RDF.bnode("b1"), NS.RDF.first, ~I<http://example.org/foo#bar>},
|
||||
{RDF.bnode("b1"), NS.RDF.rest, NS.RDF.nil},
|
||||
{RDF.bnode("b1"), NS.RDF.first(), ~I<http://example.org/foo#bar>},
|
||||
{RDF.bnode("b1"), NS.RDF.rest(), NS.RDF.nil()}
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test "term def with @id + @type + @container list: #{title}", %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test "term def with @id + @type + @container list: #{title}", %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "blank node predicates" do
|
||||
|
@ -587,82 +593,79 @@ defmodule JSON.LD.DecoderTest do
|
|||
end
|
||||
|
||||
test "outputs statements with blank node predicates if :produceGeneralizedRdf is true",
|
||||
%{input: input} do
|
||||
dataset = JSON.LD.Decoder.decode!(input, produce_generalized_rdf: true)
|
||||
assert RDF.Dataset.statement_count(dataset) == 1
|
||||
%{input: input} do
|
||||
dataset = JSON.LD.Decoder.decode!(input, produce_generalized_rdf: true)
|
||||
assert RDF.Dataset.statement_count(dataset) == 1
|
||||
end
|
||||
|
||||
test "rejects statements with blank node predicates if :produceGeneralizedRdf is false",
|
||||
%{input: input} do
|
||||
dataset = JSON.LD.Decoder.decode!(input, produce_generalized_rdf: false)
|
||||
assert RDF.Dataset.statement_count(dataset) == 0
|
||||
%{input: input} do
|
||||
dataset = JSON.LD.Decoder.decode!(input, produce_generalized_rdf: false)
|
||||
assert RDF.Dataset.statement_count(dataset) == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "advanced features" do
|
||||
%{
|
||||
"number syntax (decimal)" =>
|
||||
{
|
||||
"number syntax (decimal)" => {
|
||||
~s({"@context": { "measure": "http://example/measure#"}, "measure:cups": 5.3}),
|
||||
{RDF.bnode("b0"), ~I<http://example/measure#cups>, RDF.literal("5.3E0", datatype: XSD.double)}
|
||||
{RDF.bnode("b0"), ~I<http://example/measure#cups>,
|
||||
RDF.literal("5.3E0", datatype: XSD.double())}
|
||||
},
|
||||
"number syntax (double)" =>
|
||||
{
|
||||
"number syntax (double)" => {
|
||||
~s({"@context": { "measure": "http://example/measure#"}, "measure:cups": 5.3e0}),
|
||||
{RDF.bnode("b0"), ~I<http://example/measure#cups>, RDF.literal("5.3E0", datatype: XSD.double)}
|
||||
{RDF.bnode("b0"), ~I<http://example/measure#cups>,
|
||||
RDF.literal("5.3E0", datatype: XSD.double())}
|
||||
},
|
||||
"number syntax (integer)" =>
|
||||
{
|
||||
~s({"@context": { "chem": "http://example/chem#"}, "chem:protons": 12}),
|
||||
{RDF.bnode("b0"), ~I<http://example/chem#protons>, RDF.literal("12", datatype: XSD.integer)}
|
||||
},
|
||||
"boolan syntax" =>
|
||||
{
|
||||
~s({"@context": { "sensor": "http://example/sensor#"}, "sensor:active": true}),
|
||||
{RDF.bnode("b0"), ~I<http://example/sensor#active>, RDF.literal("true", datatype: XSD.boolean)}
|
||||
},
|
||||
"Array top element" =>
|
||||
{
|
||||
~s([
|
||||
"number syntax (integer)" => {
|
||||
~s({"@context": { "chem": "http://example/chem#"}, "chem:protons": 12}),
|
||||
{RDF.bnode("b0"), ~I<http://example/chem#protons>,
|
||||
RDF.literal("12", datatype: XSD.integer())}
|
||||
},
|
||||
"boolan syntax" => {
|
||||
~s({"@context": { "sensor": "http://example/sensor#"}, "sensor:active": true}),
|
||||
{RDF.bnode("b0"), ~I<http://example/sensor#active>,
|
||||
RDF.literal("true", datatype: XSD.boolean())}
|
||||
},
|
||||
"Array top element" => {
|
||||
~s([
|
||||
{"@id": "http://example.com/#me", "@type": "http://xmlns.com/foaf/0.1/Person"},
|
||||
{"@id": "http://example.com/#you", "@type": "http://xmlns.com/foaf/0.1/Person"}
|
||||
]),
|
||||
[
|
||||
{~I<http://example.com/#me>, NS.RDF.type, ~I<http://xmlns.com/foaf/0.1/Person>},
|
||||
{~I<http://example.com/#you>, NS.RDF.type, ~I<http://xmlns.com/foaf/0.1/Person>}
|
||||
]
|
||||
},
|
||||
"@graph with array of objects value" =>
|
||||
{
|
||||
~s({
|
||||
[
|
||||
{~I<http://example.com/#me>, NS.RDF.type(), ~I<http://xmlns.com/foaf/0.1/Person>},
|
||||
{~I<http://example.com/#you>, NS.RDF.type(), ~I<http://xmlns.com/foaf/0.1/Person>}
|
||||
]
|
||||
},
|
||||
"@graph with array of objects value" => {
|
||||
~s({
|
||||
"@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
|
||||
"@graph": [
|
||||
{"@id": "http://example.com/#me", "@type": "foaf:Person"},
|
||||
{"@id": "http://example.com/#you", "@type": "foaf:Person"}
|
||||
]
|
||||
}),
|
||||
[
|
||||
{~I<http://example.com/#me>, NS.RDF.type, ~I<http://xmlns.com/foaf/0.1/Person>},
|
||||
{~I<http://example.com/#you>, NS.RDF.type, ~I<http://xmlns.com/foaf/0.1/Person>}
|
||||
]
|
||||
},
|
||||
"XMLLiteral" =>
|
||||
{
|
||||
~s({
|
||||
[
|
||||
{~I<http://example.com/#me>, NS.RDF.type(), ~I<http://xmlns.com/foaf/0.1/Person>},
|
||||
{~I<http://example.com/#you>, NS.RDF.type(), ~I<http://xmlns.com/foaf/0.1/Person>}
|
||||
]
|
||||
},
|
||||
"XMLLiteral" => {
|
||||
~s({
|
||||
"http://rdfs.org/sioc/ns#content": {
|
||||
"@value": "foo",
|
||||
"@type": "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral"
|
||||
}
|
||||
}),
|
||||
{RDF.bnode("b0"), ~I<http://rdfs.org/sioc/ns#content>, RDF.literal("foo", datatype: "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral")}
|
||||
}
|
||||
{RDF.bnode("b0"), ~I<http://rdfs.org/sioc/ns#content>,
|
||||
RDF.literal("foo", datatype: "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral")}
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
assert JSON.LD.Decoder.decode!(input) == RDF.Dataset.new(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ defmodule JSON.LD.EncoderTest do
|
|||
defmodule TestNS do
|
||||
use RDF.Vocabulary.Namespace
|
||||
defvocab EX, base_iri: "http://example.com/", terms: [], strict: false
|
||||
defvocab S, base_iri: "http://schema.org/", terms: [], strict: false
|
||||
defvocab S, base_iri: "http://schema.org/", terms: [], strict: false
|
||||
end
|
||||
|
||||
alias TestNS.{EX, S}
|
||||
|
@ -19,210 +19,304 @@ defmodule JSON.LD.EncoderTest do
|
|||
@compile {:no_warn_undefined, JSON.LD.EncoderTest.TestNS.EX}
|
||||
@compile {:no_warn_undefined, JSON.LD.EncoderTest.TestNS.S}
|
||||
|
||||
|
||||
def gets_serialized_to(input, output, opts \\ []) do
|
||||
data_structs = Keyword.get(opts, :data_structs, [Dataset, Graph])
|
||||
Enum.each data_structs, fn data_struct ->
|
||||
|
||||
Enum.each(data_structs, fn data_struct ->
|
||||
assert JSON.LD.Encoder.from_rdf!(data_struct.new(input), opts) == output
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
test "pretty printing" do
|
||||
dataset = Dataset.new {~I<http://a/b>, ~I<http://a/c>, ~I<http://a/d>}
|
||||
dataset = Dataset.new({~I<http://a/b>, ~I<http://a/c>, ~I<http://a/d>})
|
||||
|
||||
assert JSON.LD.Encoder.encode!(dataset) ==
|
||||
"[{\"@id\":\"http://a/b\",\"http://a/c\":[{\"@id\":\"http://a/d\"}]}]"
|
||||
|
||||
assert JSON.LD.Encoder.encode!(dataset, pretty: true) ==
|
||||
"""
|
||||
[
|
||||
{
|
||||
"@id": "http://a/b",
|
||||
"http://a/c": [
|
||||
{
|
||||
"@id": "http://a/d"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
""" |> String.trim()
|
||||
"""
|
||||
[
|
||||
{
|
||||
"@id": "http://a/b",
|
||||
"http://a/c": [
|
||||
{
|
||||
"@id": "http://a/d"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"""
|
||||
|> String.trim()
|
||||
end
|
||||
|
||||
test "an empty RDF.Dataset is serialized to an JSON array string" do
|
||||
assert JSON.LD.Encoder.encode!(Dataset.new) == "[]"
|
||||
assert JSON.LD.Encoder.encode!(Dataset.new()) == "[]"
|
||||
end
|
||||
|
||||
describe "simple tests" do
|
||||
test "One subject IRI object" do
|
||||
{~I<http://a/b>, ~I<http://a/c>, ~I<http://a/d>}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://a/b",
|
||||
"http://a/c" => [%{"@id" => "http://a/d"}]
|
||||
}], data_structs: [Dataset, Graph, Description])
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://a/b",
|
||||
"http://a/c" => [%{"@id" => "http://a/d"}]
|
||||
}
|
||||
],
|
||||
data_structs: [Dataset, Graph, Description]
|
||||
)
|
||||
end
|
||||
|
||||
test "should generate object list" do
|
||||
[{EX.b, EX.c, EX.d}, {EX.b, EX.c, EX.e}]
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/b",
|
||||
[{EX.b(), EX.c(), EX.d()}, {EX.b(), EX.c(), EX.e()}]
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/b",
|
||||
"http://example.com/c" => [
|
||||
%{"@id" => "http://example.com/d"},
|
||||
%{"@id" => "http://example.com/e"}
|
||||
]
|
||||
}], data_structs: [Dataset, Graph, Description])
|
||||
}
|
||||
],
|
||||
data_structs: [Dataset, Graph, Description]
|
||||
)
|
||||
end
|
||||
|
||||
test "should generate property list" do
|
||||
[{EX.b, EX.c, EX.d}, {EX.b, EX.e, EX.f}]
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/b",
|
||||
"http://example.com/c" => [%{"@id" => "http://example.com/d"}],
|
||||
"http://example.com/e" => [%{"@id" => "http://example.com/f"}]
|
||||
}], data_structs: [Dataset, Graph, Description])
|
||||
[{EX.b(), EX.c(), EX.d()}, {EX.b(), EX.e(), EX.f()}]
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/b",
|
||||
"http://example.com/c" => [%{"@id" => "http://example.com/d"}],
|
||||
"http://example.com/e" => [%{"@id" => "http://example.com/f"}]
|
||||
}
|
||||
],
|
||||
data_structs: [Dataset, Graph, Description]
|
||||
)
|
||||
end
|
||||
|
||||
test "serializes multiple subjects" do
|
||||
[
|
||||
{~I<http://test-cases/0001>, NS.RDF.type, ~I<http://www.w3.org/2006/03/test-description#TestCase>},
|
||||
{~I<http://test-cases/0002>, NS.RDF.type, ~I<http://www.w3.org/2006/03/test-description#TestCase>}
|
||||
{~I<http://test-cases/0001>, NS.RDF.type(),
|
||||
~I<http://www.w3.org/2006/03/test-description#TestCase>},
|
||||
{~I<http://test-cases/0002>, NS.RDF.type(),
|
||||
~I<http://www.w3.org/2006/03/test-description#TestCase>}
|
||||
]
|
||||
|> gets_serialized_to([
|
||||
%{"@id" => "http://test-cases/0001", "@type" => ["http://www.w3.org/2006/03/test-description#TestCase"]},
|
||||
%{"@id" => "http://test-cases/0002", "@type" => ["http://www.w3.org/2006/03/test-description#TestCase"]},
|
||||
])
|
||||
%{
|
||||
"@id" => "http://test-cases/0001",
|
||||
"@type" => ["http://www.w3.org/2006/03/test-description#TestCase"]
|
||||
},
|
||||
%{
|
||||
"@id" => "http://test-cases/0002",
|
||||
"@type" => ["http://www.w3.org/2006/03/test-description#TestCase"]
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
describe "literal coercion" do
|
||||
test "typed literal" do
|
||||
{EX.a, EX.b, RDF.literal("foo", datatype: EX.d)}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "foo", "@type" => "http://example.com/d"}]
|
||||
}], data_structs: [Dataset, Graph, Description])
|
||||
{EX.a(), EX.b(), RDF.literal("foo", datatype: EX.d())}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "foo", "@type" => "http://example.com/d"}]
|
||||
}
|
||||
],
|
||||
data_structs: [Dataset, Graph, Description]
|
||||
)
|
||||
end
|
||||
|
||||
test "integer" do
|
||||
{EX.a, EX.b, RDF.literal(1)}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => 1}]
|
||||
}], use_native_types: true)
|
||||
{EX.a(), EX.b(), RDF.literal(1)}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => 1}]
|
||||
}
|
||||
],
|
||||
use_native_types: true
|
||||
)
|
||||
end
|
||||
|
||||
test "integer (non-native)" do
|
||||
{EX.a, EX.b, RDF.literal(1)}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "1","@type" => "http://www.w3.org/2001/XMLSchema#integer"}]
|
||||
}], use_native_types: false)
|
||||
{EX.a(), EX.b(), RDF.literal(1)}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [
|
||||
%{"@value" => "1", "@type" => "http://www.w3.org/2001/XMLSchema#integer"}
|
||||
]
|
||||
}
|
||||
],
|
||||
use_native_types: false
|
||||
)
|
||||
end
|
||||
|
||||
test "boolean" do
|
||||
{EX.a, EX.b, RDF.literal(true)}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => true}]
|
||||
}], use_native_types: true)
|
||||
{EX.a(), EX.b(), RDF.literal(true)}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => true}]
|
||||
}
|
||||
],
|
||||
use_native_types: true
|
||||
)
|
||||
end
|
||||
|
||||
test "boolean (non-native)" do
|
||||
{EX.a, EX.b, RDF.literal(true)}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "true","@type" => "http://www.w3.org/2001/XMLSchema#boolean"}]
|
||||
}], use_native_types: false)
|
||||
{EX.a(), EX.b(), RDF.literal(true)}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [
|
||||
%{"@value" => "true", "@type" => "http://www.w3.org/2001/XMLSchema#boolean"}
|
||||
]
|
||||
}
|
||||
],
|
||||
use_native_types: false
|
||||
)
|
||||
end
|
||||
|
||||
@tag skip: "TODO: Is this spec conformant or RDF.rb specific? RDF.rb doesn't use the specified RDF to Object Conversion algorithm but reuses a generalized expand_value algorithm"
|
||||
@tag skip:
|
||||
"TODO: Is this spec conformant or RDF.rb specific? RDF.rb doesn't use the specified RDF to Object Conversion algorithm but reuses a generalized expand_value algorithm"
|
||||
test "decimal" do
|
||||
{EX.a, EX.b, RDF.literal(1.0)}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "1.0", "@type" => "http://www.w3.org/2001/XMLSchema#decimal"}]
|
||||
}], use_native_types: true)
|
||||
{EX.a(), EX.b(), RDF.literal(1.0)}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [
|
||||
%{"@value" => "1.0", "@type" => "http://www.w3.org/2001/XMLSchema#decimal"}
|
||||
]
|
||||
}
|
||||
],
|
||||
use_native_types: true
|
||||
)
|
||||
end
|
||||
|
||||
test "double" do
|
||||
{EX.a, EX.b, RDF.literal(1.0e0)}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => 1.0E0}]
|
||||
}], use_native_types: true)
|
||||
{EX.a(), EX.b(), RDF.literal(1.0e0)}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => 1.0e0}]
|
||||
}
|
||||
],
|
||||
use_native_types: true
|
||||
)
|
||||
end
|
||||
|
||||
@tag skip: "TODO: Is this spec conformant or RDF.rb specific? RDF.rb doesn't use the specified RDF to Object Conversion algorithm but reuses a generalized expand_value algorithm"
|
||||
@tag skip:
|
||||
"TODO: Is this spec conformant or RDF.rb specific? RDF.rb doesn't use the specified RDF to Object Conversion algorithm but reuses a generalized expand_value algorithm"
|
||||
test "double (non-native)" do
|
||||
{EX.a, EX.b, RDF.literal(1.0e0)}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "1.0E0", "@type" => "http://www.w3.org/2001/XMLSchema#double"}]
|
||||
}], use_native_types: false)
|
||||
{EX.a(), EX.b(), RDF.literal(1.0e0)}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [
|
||||
%{"@value" => "1.0E0", "@type" => "http://www.w3.org/2001/XMLSchema#double"}
|
||||
]
|
||||
}
|
||||
],
|
||||
use_native_types: false
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "datatyped (non-native) literals" do
|
||||
%{
|
||||
integer: 1,
|
||||
unsignedInt: 1,
|
||||
integer: 1,
|
||||
unsignedInt: 1,
|
||||
nonNegativeInteger: 1,
|
||||
float: 1.0,
|
||||
float: 1.0,
|
||||
nonPositiveInteger: -1,
|
||||
negativeInteger: -1,
|
||||
negativeInteger: -1
|
||||
}
|
||||
|> Enum.each(fn ({type, _} = data) ->
|
||||
@tag data: data
|
||||
test "#{type}", %{data: {type, value}} do
|
||||
{EX.a, EX.b, RDF.literal(value, datatype: apply(NS.XSD, type, []))}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "#{value}", "@type" => "http://www.w3.org/2001/XMLSchema##{type}"}]
|
||||
}], use_native_types: false)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {type, _} = data ->
|
||||
@tag data: data
|
||||
test "#{type}", %{data: {type, value}} do
|
||||
{EX.a(), EX.b(), RDF.literal(value, datatype: apply(NS.XSD, type, []))}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [
|
||||
%{"@value" => "#{value}", "@type" => "http://www.w3.org/2001/XMLSchema##{type}"}
|
||||
]
|
||||
}
|
||||
],
|
||||
use_native_types: false
|
||||
)
|
||||
end
|
||||
end)
|
||||
|
||||
test "when useNativeTypes" do
|
||||
{EX.a, EX.b, RDF.literal("foo", datatype: EX.customType)}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "foo", "@type" => to_string(EX.customType)}]
|
||||
}], use_native_types: true)
|
||||
{EX.a(), EX.b(), RDF.literal("foo", datatype: EX.customType())}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [
|
||||
%{"@value" => "foo", "@type" => to_string(EX.customType())}
|
||||
]
|
||||
}
|
||||
],
|
||||
use_native_types: true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
test "encodes language literal" do
|
||||
{EX.a, EX.b, RDF.literal("foo", language: "en-us")}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "foo", "@language" => "en-us"}]
|
||||
}])
|
||||
{EX.a(), EX.b(), RDF.literal("foo", language: "en-us")}
|
||||
|> gets_serialized_to([
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@value" => "foo", "@language" => "en-us"}]
|
||||
}
|
||||
])
|
||||
end
|
||||
|
||||
|
||||
describe "blank nodes" do
|
||||
test "should generate blank nodes" do
|
||||
{RDF.bnode(:a), EX.a, EX.b}
|
||||
|> gets_serialized_to([%{
|
||||
"@id" => "_:a",
|
||||
"http://example.com/a" => [%{"@id" => "http://example.com/b"}]
|
||||
}], data_structs: [Dataset, Graph, Description])
|
||||
{RDF.bnode(:a), EX.a(), EX.b()}
|
||||
|> gets_serialized_to(
|
||||
[
|
||||
%{
|
||||
"@id" => "_:a",
|
||||
"http://example.com/a" => [%{"@id" => "http://example.com/b"}]
|
||||
}
|
||||
],
|
||||
data_structs: [Dataset, Graph, Description]
|
||||
)
|
||||
end
|
||||
|
||||
test "should generate blank nodes as object" do
|
||||
[
|
||||
{EX.a, EX.b, RDF.bnode(:a)},
|
||||
{RDF.bnode(:a), EX.c, EX.d}
|
||||
{EX.a(), EX.b(), RDF.bnode(:a)},
|
||||
{RDF.bnode(:a), EX.c(), EX.d()}
|
||||
]
|
||||
|> gets_serialized_to([
|
||||
%{
|
||||
"@id" => "_:a",
|
||||
"http://example.com/c" => [%{"@id" => "http://example.com/d"}]
|
||||
},
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@id" => "_:a"}]
|
||||
}
|
||||
])
|
||||
%{
|
||||
"@id" => "_:a",
|
||||
"http://example.com/c" => [%{"@id" => "http://example.com/d"}]
|
||||
},
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@id" => "_:a"}]
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -230,177 +324,160 @@ defmodule JSON.LD.EncoderTest do
|
|||
%{
|
||||
"literal list" => {
|
||||
[
|
||||
{EX.a, EX.b, RDF.bnode(:e1) },
|
||||
{RDF.bnode(:e1), NS.RDF.first, ~L"apple"},
|
||||
{RDF.bnode(:e1), NS.RDF.rest, RDF.bnode(:e2)},
|
||||
{RDF.bnode(:e2), NS.RDF.first, ~L"banana"},
|
||||
{RDF.bnode(:e2), NS.RDF.rest, NS.RDF.nil},
|
||||
{EX.a(), EX.b(), RDF.bnode(:e1)},
|
||||
{RDF.bnode(:e1), NS.RDF.first(), ~L"apple"},
|
||||
{RDF.bnode(:e1), NS.RDF.rest(), RDF.bnode(:e2)},
|
||||
{RDF.bnode(:e2), NS.RDF.first(), ~L"banana"},
|
||||
{RDF.bnode(:e2), NS.RDF.rest(), NS.RDF.nil()}
|
||||
],
|
||||
[%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{
|
||||
"@list" => [
|
||||
%{"@value" => "apple"},
|
||||
%{"@value" => "banana"}
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [
|
||||
%{
|
||||
"@list" => [
|
||||
%{"@value" => "apple"},
|
||||
%{"@value" => "banana"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"iri list" => {
|
||||
[
|
||||
{EX.a, EX.b, RDF.bnode(:list)},
|
||||
{RDF.bnode(:list), NS.RDF.first, EX.c},
|
||||
{RDF.bnode(:list), NS.RDF.rest, NS.RDF.nil},
|
||||
{EX.a(), EX.b(), RDF.bnode(:list)},
|
||||
{RDF.bnode(:list), NS.RDF.first(), EX.c()},
|
||||
{RDF.bnode(:list), NS.RDF.rest(), NS.RDF.nil()}
|
||||
],
|
||||
[%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{
|
||||
"@list" => [
|
||||
%{"@id" => "http://example.com/c"}
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [
|
||||
%{
|
||||
"@list" => [
|
||||
%{"@id" => "http://example.com/c"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"empty list" => {
|
||||
[
|
||||
{EX.a, EX.b, NS.RDF.nil},
|
||||
{EX.a(), EX.b(), NS.RDF.nil()}
|
||||
],
|
||||
[%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@list" => []}]
|
||||
}]
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@list" => []}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"single element list" => {
|
||||
[
|
||||
{EX.a, EX.b, RDF.bnode(:list)},
|
||||
{RDF.bnode(:list), NS.RDF.first, ~L"apple"},
|
||||
{RDF.bnode(:list), NS.RDF.rest, NS.RDF.nil},
|
||||
{EX.a(), EX.b(), RDF.bnode(:list)},
|
||||
{RDF.bnode(:list), NS.RDF.first(), ~L"apple"},
|
||||
{RDF.bnode(:list), NS.RDF.rest(), NS.RDF.nil()}
|
||||
],
|
||||
[%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@list" => [%{"@value" => "apple"}]}]
|
||||
}]
|
||||
[
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@list" => [%{"@value" => "apple"}]}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"single element list without @type" => {
|
||||
[
|
||||
{EX.a, EX.b, RDF.bnode(:list)},
|
||||
{RDF.bnode(:list), NS.RDF.first, RDF.bnode(:a)},
|
||||
{RDF.bnode(:list), NS.RDF.rest, NS.RDF.nil},
|
||||
{RDF.bnode(:a), EX.b, ~L"foo"},
|
||||
{EX.a(), EX.b(), RDF.bnode(:list)},
|
||||
{RDF.bnode(:list), NS.RDF.first(), RDF.bnode(:a)},
|
||||
{RDF.bnode(:list), NS.RDF.rest(), NS.RDF.nil()},
|
||||
{RDF.bnode(:a), EX.b(), ~L"foo"}
|
||||
],
|
||||
[
|
||||
%{
|
||||
"@id" => "_:a",
|
||||
"http://example.com/b" => [%{"@value" => "foo"}]
|
||||
"@id" => "_:a",
|
||||
"http://example.com/b" => [%{"@value" => "foo"}]
|
||||
},
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@list" => [%{"@id" => "_:a"}]}]
|
||||
},
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@list" => [%{"@id" => "_:a"}]}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"multiple graphs with shared BNode" => {
|
||||
[
|
||||
{EX.z, EX.q, RDF.bnode(:z0), EX.G},
|
||||
{RDF.bnode(:z0), NS.RDF.first, ~L"cell-A", EX.G},
|
||||
{RDF.bnode(:z0), NS.RDF.rest, RDF.bnode(:z1), EX.G},
|
||||
{RDF.bnode(:z1), NS.RDF.first, ~L"cell-B", EX.G},
|
||||
{RDF.bnode(:z1), NS.RDF.rest, NS.RDF.nil, EX.G},
|
||||
{EX.x, EX.p, RDF.bnode(:z1), EX.G1},
|
||||
{EX.z(), EX.q(), RDF.bnode(:z0), EX.G},
|
||||
{RDF.bnode(:z0), NS.RDF.first(), ~L"cell-A", EX.G},
|
||||
{RDF.bnode(:z0), NS.RDF.rest(), RDF.bnode(:z1), EX.G},
|
||||
{RDF.bnode(:z1), NS.RDF.first(), ~L"cell-B", EX.G},
|
||||
{RDF.bnode(:z1), NS.RDF.rest(), NS.RDF.nil(), EX.G},
|
||||
{EX.x(), EX.p(), RDF.bnode(:z1), EX.G1}
|
||||
],
|
||||
[%{
|
||||
"@id" => "http://www.example.com/G",
|
||||
"@graph" => [%{
|
||||
"@id" => "_:z0",
|
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#first" => [%{"@value" => "cell-A"}],
|
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#rest" => [%{"@id" => "_:z1"}]
|
||||
}, %{
|
||||
"@id" => "_:z1",
|
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#first" => [%{"@value" => "cell-B"}],
|
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#rest" => [%{"@list" => []}]
|
||||
}, %{
|
||||
"@id" => "http://www.example.com/z",
|
||||
"http://www.example.com/q" => [%{"@id" => "_:z0"}]
|
||||
}]
|
||||
},
|
||||
%{
|
||||
"@id" => "http://www.example.com/G1",
|
||||
"@graph" => [%{
|
||||
"@id" => "http://www.example.com/x",
|
||||
"http://www.example.com/p" => [%{"@id" => "_:z1"}]
|
||||
}]
|
||||
}]
|
||||
},
|
||||
[
|
||||
%{
|
||||
"@id" => "http://www.example.com/G",
|
||||
"@graph" => [
|
||||
%{
|
||||
"@id" => "_:z0",
|
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#first" => [%{"@value" => "cell-A"}],
|
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#rest" => [%{"@id" => "_:z1"}]
|
||||
},
|
||||
%{
|
||||
"@id" => "_:z1",
|
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#first" => [%{"@value" => "cell-B"}],
|
||||
"http://www.w3.org/1999/02/22-rdf-syntax-ns#rest" => [%{"@list" => []}]
|
||||
},
|
||||
%{
|
||||
"@id" => "http://www.example.com/z",
|
||||
"http://www.example.com/q" => [%{"@id" => "_:z0"}]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
"@id" => "http://www.example.com/G1",
|
||||
"@graph" => [
|
||||
%{
|
||||
"@id" => "http://www.example.com/x",
|
||||
"http://www.example.com/p" => [%{"@id" => "_:z1"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
if title == "multiple graphs with shared BNode" do
|
||||
@tag skip: "TODO: https://github.com/json-ld/json-ld.org/issues/357"
|
||||
end
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
input |> gets_serialized_to(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
if title == "multiple graphs with shared BNode" do
|
||||
@tag skip: "TODO: https://github.com/json-ld/json-ld.org/issues/357"
|
||||
end
|
||||
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
input |> gets_serialized_to(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "quads" do
|
||||
%{
|
||||
"simple named graph" => %{
|
||||
input: {EX.a, EX.b, EX.c, EX.U},
|
||||
input: {EX.a(), EX.b(), EX.c(), EX.U},
|
||||
output: [
|
||||
%{
|
||||
"@id" => "http://example.com/U",
|
||||
"@graph" => [%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@id" => "http://example.com/c"}]
|
||||
}]
|
||||
},
|
||||
"@graph" => [
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@id" => "http://example.com/c"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"with properties" => %{
|
||||
input: [
|
||||
{EX.a, EX.b, EX.c, EX.U},
|
||||
{EX.U, EX.d, EX.e},
|
||||
],
|
||||
output: [
|
||||
%{
|
||||
"@id" => "http://example.com/U",
|
||||
"@graph" => [%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@id" => "http://example.com/c"}]
|
||||
}],
|
||||
"http://example.com/d" => [%{"@id" => "http://example.com/e"}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"with lists" => %{
|
||||
input: [
|
||||
{EX.a, EX.b, RDF.bnode(:a), EX.U},
|
||||
{RDF.bnode(:a), NS.RDF.first, EX.c, EX.U},
|
||||
{RDF.bnode(:a), NS.RDF.rest, NS.RDF.nil, EX.U},
|
||||
{EX.U, EX.d, RDF.bnode(:b)},
|
||||
{RDF.bnode(:b), NS.RDF.first, EX.e},
|
||||
{RDF.bnode(:b), NS.RDF.rest, NS.RDF.nil},
|
||||
],
|
||||
output: [
|
||||
%{
|
||||
"@id" => "http://example.com/U",
|
||||
"@graph" => [%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@list" => [%{"@id" => "http://example.com/c"}]}]
|
||||
}],
|
||||
"http://example.com/d" => [%{"@list" => [%{"@id" => "http://example.com/e"}]}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Two Graphs with same subject and lists" => %{
|
||||
input: [
|
||||
{EX.a, EX.b, RDF.bnode(:a), EX.U},
|
||||
{RDF.bnode(:a), NS.RDF.first, EX.c, EX.U},
|
||||
{RDF.bnode(:a), NS.RDF.rest, NS.RDF.nil, EX.U},
|
||||
{EX.a, EX.b, RDF.bnode(:b), EX.V},
|
||||
{RDF.bnode(:b), NS.RDF.first, EX.e, EX.V},
|
||||
{RDF.bnode(:b), NS.RDF.rest, NS.RDF.nil, EX.V},
|
||||
{EX.a(), EX.b(), EX.c(), EX.U},
|
||||
{EX.U, EX.d(), EX.e()}
|
||||
],
|
||||
output: [
|
||||
%{
|
||||
|
@ -408,9 +485,55 @@ defmodule JSON.LD.EncoderTest do
|
|||
"@graph" => [
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{
|
||||
"@list" => [%{"@id" => "http://example.com/c"}]
|
||||
}]
|
||||
"http://example.com/b" => [%{"@id" => "http://example.com/c"}]
|
||||
}
|
||||
],
|
||||
"http://example.com/d" => [%{"@id" => "http://example.com/e"}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"with lists" => %{
|
||||
input: [
|
||||
{EX.a(), EX.b(), RDF.bnode(:a), EX.U},
|
||||
{RDF.bnode(:a), NS.RDF.first(), EX.c(), EX.U},
|
||||
{RDF.bnode(:a), NS.RDF.rest(), NS.RDF.nil(), EX.U},
|
||||
{EX.U, EX.d(), RDF.bnode(:b)},
|
||||
{RDF.bnode(:b), NS.RDF.first(), EX.e()},
|
||||
{RDF.bnode(:b), NS.RDF.rest(), NS.RDF.nil()}
|
||||
],
|
||||
output: [
|
||||
%{
|
||||
"@id" => "http://example.com/U",
|
||||
"@graph" => [
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{"@list" => [%{"@id" => "http://example.com/c"}]}]
|
||||
}
|
||||
],
|
||||
"http://example.com/d" => [%{"@list" => [%{"@id" => "http://example.com/e"}]}]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Two Graphs with same subject and lists" => %{
|
||||
input: [
|
||||
{EX.a(), EX.b(), RDF.bnode(:a), EX.U},
|
||||
{RDF.bnode(:a), NS.RDF.first(), EX.c(), EX.U},
|
||||
{RDF.bnode(:a), NS.RDF.rest(), NS.RDF.nil(), EX.U},
|
||||
{EX.a(), EX.b(), RDF.bnode(:b), EX.V},
|
||||
{RDF.bnode(:b), NS.RDF.first(), EX.e(), EX.V},
|
||||
{RDF.bnode(:b), NS.RDF.rest(), NS.RDF.nil(), EX.V}
|
||||
],
|
||||
output: [
|
||||
%{
|
||||
"@id" => "http://example.com/U",
|
||||
"@graph" => [
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [
|
||||
%{
|
||||
"@list" => [%{"@id" => "http://example.com/c"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -419,41 +542,44 @@ defmodule JSON.LD.EncoderTest do
|
|||
"@graph" => [
|
||||
%{
|
||||
"@id" => "http://example.com/a",
|
||||
"http://example.com/b" => [%{
|
||||
"@list" => [%{"@id" => "http://example.com/e"}]
|
||||
}]
|
||||
"http://example.com/b" => [
|
||||
%{
|
||||
"@list" => [%{"@id" => "http://example.com/e"}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: %{input: input, output: output}} do
|
||||
input |> gets_serialized_to(output, data_structs: [Dataset])
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: %{input: input, output: output}} do
|
||||
input |> gets_serialized_to(output, data_structs: [Dataset])
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "problems" do
|
||||
%{
|
||||
"xsd:boolean as value" => {
|
||||
{~I<http://data.wikia.com/terms#playable>, NS.RDFS.range, NS.XSD.boolean},
|
||||
[%{
|
||||
"@id" => "http://data.wikia.com/terms#playable",
|
||||
"http://www.w3.org/2000/01/rdf-schema#range" => [
|
||||
%{ "@id" => "http://www.w3.org/2001/XMLSchema#boolean" }
|
||||
]
|
||||
}]
|
||||
},
|
||||
{~I<http://data.wikia.com/terms#playable>, NS.RDFS.range(), NS.XSD.boolean()},
|
||||
[
|
||||
%{
|
||||
"@id" => "http://data.wikia.com/terms#playable",
|
||||
"http://www.w3.org/2000/01/rdf-schema#range" => [
|
||||
%{"@id" => "http://www.w3.org/2001/XMLSchema#boolean"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
input |> gets_serialized_to(output)
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: {input, output}} do
|
||||
input |> gets_serialized_to(output)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,8 @@ defmodule JSON.LD.FlatteningTest do
|
|||
alias RDF.NS.RDFS
|
||||
|
||||
test "Flattened form of a JSON-LD document (EXAMPLE 60 and 61 of https://www.w3.org/TR/json-ld/#flattened-document-form)" do
|
||||
input = Jason.decode! """
|
||||
input =
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
|
@ -22,35 +23,36 @@ defmodule JSON.LD.FlatteningTest do
|
|||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
assert JSON.LD.flatten(input, input) == Jason.decode! """
|
||||
{
|
||||
"@context": {
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
"knows": "http://xmlns.com/foaf/0.1/knows"
|
||||
},
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "_:b0",
|
||||
"name": "Dave Longley"
|
||||
},
|
||||
{
|
||||
"@id": "http://manu.sporny.org/about#manu",
|
||||
"name": "Manu Sporny"
|
||||
},
|
||||
{
|
||||
"@id": "http://me.markus-lanthaler.com/",
|
||||
"name": "Markus Lanthaler",
|
||||
"knows": [
|
||||
{ "@id": "http://manu.sporny.org/about#manu" },
|
||||
{ "@id": "_:b0" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
end
|
||||
""")
|
||||
|
||||
assert JSON.LD.flatten(input, input) ==
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
"knows": "http://xmlns.com/foaf/0.1/knows"
|
||||
},
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "_:b0",
|
||||
"name": "Dave Longley"
|
||||
},
|
||||
{
|
||||
"@id": "http://manu.sporny.org/about#manu",
|
||||
"name": "Manu Sporny"
|
||||
},
|
||||
{
|
||||
"@id": "http://me.markus-lanthaler.com/",
|
||||
"name": "Markus Lanthaler",
|
||||
"knows": [
|
||||
{ "@id": "http://manu.sporny.org/about#manu" },
|
||||
{ "@id": "_:b0" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""")
|
||||
end
|
||||
|
||||
%{
|
||||
"single object" => %{
|
||||
|
@ -66,16 +68,20 @@ defmodule JSON.LD.FlatteningTest do
|
|||
},
|
||||
"@id" => "http://greggkellogg.net/foaf",
|
||||
"@type" => ["foaf:PersonalProfileDocument"],
|
||||
"foaf:primaryTopic" => [%{
|
||||
"@id" => "http://greggkellogg.net/foaf#me",
|
||||
"@type" => ["foaf:Person"]
|
||||
}]
|
||||
"foaf:primaryTopic" => [
|
||||
%{
|
||||
"@id" => "http://greggkellogg.net/foaf#me",
|
||||
"@type" => ["foaf:Person"]
|
||||
}
|
||||
]
|
||||
},
|
||||
output: [
|
||||
%{
|
||||
"@id" => "http://greggkellogg.net/foaf",
|
||||
"@type" => ["http://xmlns.com/foaf/0.1/PersonalProfileDocument"],
|
||||
"http://xmlns.com/foaf/0.1/primaryTopic" => [%{"@id" => "http://greggkellogg.net/foaf#me"}]
|
||||
"http://xmlns.com/foaf/0.1/primaryTopic" => [
|
||||
%{"@id" => "http://greggkellogg.net/foaf#me"}
|
||||
]
|
||||
},
|
||||
%{
|
||||
"@id" => "http://greggkellogg.net/foaf#me",
|
||||
|
@ -107,170 +113,177 @@ defmodule JSON.LD.FlatteningTest do
|
|||
]
|
||||
},
|
||||
"reverse properties" => %{
|
||||
input: Jason.decode!("""
|
||||
[
|
||||
{
|
||||
"@id": "http://example.com/people/markus",
|
||||
"@reverse": {
|
||||
input:
|
||||
Jason.decode!("""
|
||||
[
|
||||
{
|
||||
"@id": "http://example.com/people/markus",
|
||||
"@reverse": {
|
||||
"http://xmlns.com/foaf/0.1/knows": [
|
||||
{
|
||||
"@id": "http://example.com/people/dave"
|
||||
},
|
||||
{
|
||||
"@id": "http://example.com/people/gregg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"http://xmlns.com/foaf/0.1/name": [ { "@value": "Markus Lanthaler" } ]
|
||||
}
|
||||
]
|
||||
"""),
|
||||
output:
|
||||
Jason.decode!("""
|
||||
[
|
||||
{
|
||||
"@id": "http://example.com/people/dave",
|
||||
"http://xmlns.com/foaf/0.1/knows": [
|
||||
{
|
||||
"@id": "http://example.com/people/dave"
|
||||
},
|
||||
{
|
||||
"@id": "http://example.com/people/gregg"
|
||||
"@id": "http://example.com/people/markus"
|
||||
}
|
||||
]
|
||||
},
|
||||
"http://xmlns.com/foaf/0.1/name": [ { "@value": "Markus Lanthaler" } ]
|
||||
}
|
||||
]
|
||||
"""),
|
||||
output: Jason.decode!("""
|
||||
[
|
||||
{
|
||||
"@id": "http://example.com/people/dave",
|
||||
"http://xmlns.com/foaf/0.1/knows": [
|
||||
{
|
||||
"@id": "http://example.com/people/markus"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"@id": "http://example.com/people/gregg",
|
||||
"http://xmlns.com/foaf/0.1/knows": [
|
||||
{
|
||||
"@id": "http://example.com/people/markus"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"@id": "http://example.com/people/markus",
|
||||
"http://xmlns.com/foaf/0.1/name": [
|
||||
{
|
||||
"@value": "Markus Lanthaler"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
""")
|
||||
{
|
||||
"@id": "http://example.com/people/gregg",
|
||||
"http://xmlns.com/foaf/0.1/knows": [
|
||||
{
|
||||
"@id": "http://example.com/people/markus"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"@id": "http://example.com/people/markus",
|
||||
"http://xmlns.com/foaf/0.1/name": [
|
||||
{
|
||||
"@value": "Markus Lanthaler"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
""")
|
||||
},
|
||||
"Simple named graph (Wikidata)" => %{
|
||||
input: Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
||||
"ex": "http://example.org/",
|
||||
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
||||
"ex:locatedIn": {"@type": "@id"},
|
||||
"ex:hasPopulaton": {"@type": "xsd:integer"},
|
||||
"ex:hasReference": {"@type": "@id"}
|
||||
},
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "http://example.org/ParisFact1",
|
||||
"@type": "rdf:Graph",
|
||||
"@graph": {
|
||||
"@id": "http://example.org/location/Paris#this",
|
||||
"ex:locatedIn": "http://example.org/location/France#this"
|
||||
},
|
||||
"ex:hasReference": ["http://www.britannica.com/", "http://www.wikipedia.org/", "http://www.brockhaus.de/"]
|
||||
input:
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
||||
"ex": "http://example.org/",
|
||||
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
||||
"ex:locatedIn": {"@type": "@id"},
|
||||
"ex:hasPopulaton": {"@type": "xsd:integer"},
|
||||
"ex:hasReference": {"@type": "@id"}
|
||||
},
|
||||
{
|
||||
"@id": "http://example.org/ParisFact2",
|
||||
"@type": "rdf:Graph",
|
||||
"@graph": {
|
||||
"@id": "http://example.org/location/Paris#this",
|
||||
"ex:hasPopulation": 7000000
|
||||
"@graph": [
|
||||
{
|
||||
"@id": "http://example.org/ParisFact1",
|
||||
"@type": "rdf:Graph",
|
||||
"@graph": {
|
||||
"@id": "http://example.org/location/Paris#this",
|
||||
"ex:locatedIn": "http://example.org/location/France#this"
|
||||
},
|
||||
"ex:hasReference": ["http://www.britannica.com/", "http://www.wikipedia.org/", "http://www.brockhaus.de/"]
|
||||
},
|
||||
"ex:hasReference": "http://www.wikipedia.org/"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""),
|
||||
output: Jason.decode!("""
|
||||
[{
|
||||
"@id": "http://example.org/ParisFact1",
|
||||
"@type": ["http://www.w3.org/1999/02/22-rdf-syntax-ns#Graph"],
|
||||
"http://example.org/hasReference": [
|
||||
{"@id": "http://www.britannica.com/"},
|
||||
{"@id": "http://www.wikipedia.org/"},
|
||||
{"@id": "http://www.brockhaus.de/"}
|
||||
],
|
||||
"@graph": [{
|
||||
"@id": "http://example.org/location/Paris#this",
|
||||
"http://example.org/locatedIn": [{"@id": "http://example.org/location/France#this"}]
|
||||
}]
|
||||
}, {
|
||||
"@id": "http://example.org/ParisFact2",
|
||||
{
|
||||
"@id": "http://example.org/ParisFact2",
|
||||
"@type": "rdf:Graph",
|
||||
"@graph": {
|
||||
"@id": "http://example.org/location/Paris#this",
|
||||
"ex:hasPopulation": 7000000
|
||||
},
|
||||
"ex:hasReference": "http://www.wikipedia.org/"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""),
|
||||
output:
|
||||
Jason.decode!("""
|
||||
[{
|
||||
"@id": "http://example.org/ParisFact1",
|
||||
"@type": ["http://www.w3.org/1999/02/22-rdf-syntax-ns#Graph"],
|
||||
"http://example.org/hasReference": [{"@id": "http://www.wikipedia.org/"}],
|
||||
"http://example.org/hasReference": [
|
||||
{"@id": "http://www.britannica.com/"},
|
||||
{"@id": "http://www.wikipedia.org/"},
|
||||
{"@id": "http://www.brockhaus.de/"}
|
||||
],
|
||||
"@graph": [{
|
||||
"@id": "http://example.org/location/Paris#this",
|
||||
"http://example.org/hasPopulation": [{"@value": 7000000}]
|
||||
"@id": "http://example.org/location/Paris#this",
|
||||
"http://example.org/locatedIn": [{"@id": "http://example.org/location/France#this"}]
|
||||
}]
|
||||
}, {
|
||||
"@id": "http://example.org/ParisFact2",
|
||||
"@type": ["http://www.w3.org/1999/02/22-rdf-syntax-ns#Graph"],
|
||||
"http://example.org/hasReference": [{"@id": "http://www.wikipedia.org/"}],
|
||||
"@graph": [{
|
||||
"@id": "http://example.org/location/Paris#this",
|
||||
"http://example.org/hasPopulation": [{"@value": 7000000}]
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
""")
|
||||
""")
|
||||
},
|
||||
"Test Manifest (shortened)" => %{
|
||||
input: Jason.decode!("""
|
||||
{
|
||||
"@id": "",
|
||||
"http://example/sequence": {"@list": [
|
||||
{
|
||||
"@id": "#t0001",
|
||||
"http://example/name": "Keywords cannot be aliased to other keywords",
|
||||
"http://example/input": {"@id": "error-expand-0001-in.jsonld"}
|
||||
}
|
||||
]}
|
||||
}
|
||||
"""),
|
||||
output: Jason.decode!("""
|
||||
[{
|
||||
"@id": "",
|
||||
"http://example/sequence": [{"@list": [{"@id": "#t0001"}]}]
|
||||
}, {
|
||||
"@id": "#t0001",
|
||||
"http://example/input": [{"@id": "error-expand-0001-in.jsonld"}],
|
||||
"http://example/name": [{"@value": "Keywords cannot be aliased to other keywords"}]
|
||||
}]
|
||||
"""),
|
||||
input:
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@id": "",
|
||||
"http://example/sequence": {"@list": [
|
||||
{
|
||||
"@id": "#t0001",
|
||||
"http://example/name": "Keywords cannot be aliased to other keywords",
|
||||
"http://example/input": {"@id": "error-expand-0001-in.jsonld"}
|
||||
}
|
||||
]}
|
||||
}
|
||||
"""),
|
||||
output:
|
||||
Jason.decode!("""
|
||||
[{
|
||||
"@id": "",
|
||||
"http://example/sequence": [{"@list": [{"@id": "#t0001"}]}]
|
||||
}, {
|
||||
"@id": "#t0001",
|
||||
"http://example/input": [{"@id": "error-expand-0001-in.jsonld"}],
|
||||
"http://example/name": [{"@value": "Keywords cannot be aliased to other keywords"}]
|
||||
}]
|
||||
"""),
|
||||
options: %{}
|
||||
},
|
||||
"@reverse bnode issue (0045)" => %{
|
||||
input: Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"foo": "http://example.org/foo",
|
||||
"bar": { "@reverse": "http://example.org/bar", "@type": "@id" }
|
||||
},
|
||||
"foo": "Foo",
|
||||
"bar": [ "http://example.org/origin", "_:b0" ]
|
||||
}
|
||||
"""),
|
||||
output: Jason.decode!("""
|
||||
[
|
||||
input:
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@id": "_:b0",
|
||||
"http://example.org/foo": [ { "@value": "Foo" } ]
|
||||
},
|
||||
{
|
||||
"@id": "_:b1",
|
||||
"http://example.org/bar": [ { "@id": "_:b0" } ]
|
||||
},
|
||||
{
|
||||
"@id": "http://example.org/origin",
|
||||
"http://example.org/bar": [ { "@id": "_:b0" } ]
|
||||
"@context": {
|
||||
"foo": "http://example.org/foo",
|
||||
"bar": { "@reverse": "http://example.org/bar", "@type": "@id" }
|
||||
},
|
||||
"foo": "Foo",
|
||||
"bar": [ "http://example.org/origin", "_:b0" ]
|
||||
}
|
||||
]
|
||||
"""),
|
||||
"""),
|
||||
output:
|
||||
Jason.decode!("""
|
||||
[
|
||||
{
|
||||
"@id": "_:b0",
|
||||
"http://example.org/foo": [ { "@value": "Foo" } ]
|
||||
},
|
||||
{
|
||||
"@id": "_:b1",
|
||||
"http://example.org/bar": [ { "@id": "_:b0" } ]
|
||||
},
|
||||
{
|
||||
"@id": "http://example.org/origin",
|
||||
"http://example.org/bar": [ { "@id": "_:b0" } ]
|
||||
}
|
||||
]
|
||||
"""),
|
||||
options: %{}
|
||||
}
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.flatten(data.input) == data.output
|
||||
end
|
||||
end)
|
||||
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: data} do
|
||||
assert JSON.LD.flatten(data.input) == data.output
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -6,58 +6,67 @@ defmodule JSON.LD.IRICompactionTest do
|
|||
alias RDF.NS.{XSD}
|
||||
|
||||
setup do
|
||||
context = JSON.LD.context(%{
|
||||
"@base" => "http://base/",
|
||||
"xsd" => "http://www.w3.org/2001/XMLSchema#",
|
||||
"ex" => "http://example.org/",
|
||||
"" => "http://empty/", # TODO: "Invalid JSON-LD syntax; a term cannot be an empty string."
|
||||
"_" => "http://underscore/",
|
||||
"rex" => %{"@reverse" => "ex"},
|
||||
"lex" => %{"@id" => "ex", "@language" => "en"},
|
||||
"tex" => %{"@id" => "ex", "@type" => "xsd:string"},
|
||||
"exp" => %{"@id" => "ex:pert"},
|
||||
"experts" => %{"@id" => "ex:perts"}
|
||||
})
|
||||
context =
|
||||
JSON.LD.context(%{
|
||||
"@base" => "http://base/",
|
||||
"xsd" => "http://www.w3.org/2001/XMLSchema#",
|
||||
"ex" => "http://example.org/",
|
||||
# TODO: "Invalid JSON-LD syntax; a term cannot be an empty string."
|
||||
"" => "http://empty/",
|
||||
"_" => "http://underscore/",
|
||||
"rex" => %{"@reverse" => "ex"},
|
||||
"lex" => %{"@id" => "ex", "@language" => "en"},
|
||||
"tex" => %{"@id" => "ex", "@type" => "xsd:string"},
|
||||
"exp" => %{"@id" => "ex:pert"},
|
||||
"experts" => %{"@id" => "ex:perts"}
|
||||
})
|
||||
|
||||
%{example_context: context, inverse_context: JSON.LD.Context.inverse(context)}
|
||||
end
|
||||
|
||||
%{
|
||||
"nil" => [nil, nil],
|
||||
"absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "foo"],
|
||||
"bnode" => ["_:a", "_:a"],
|
||||
"relative" => ["foo/bar", "http://base/foo/bar"],
|
||||
"odd CURIE" => ["exp:s", "http://example.org/perts"]
|
||||
"nil" => [nil, nil],
|
||||
"absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "foo"],
|
||||
"bnode" => ["_:a", "_:a"],
|
||||
"relative" => ["foo/bar", "http://base/foo/bar"],
|
||||
"odd CURIE" => ["exp:s", "http://example.org/perts"]
|
||||
}
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: [result, input], example_context: context,
|
||||
inverse_context: inverse_context} do
|
||||
assert compact_iri(input, context, inverse_context) == result
|
||||
end
|
||||
end)
|
||||
@tag data: data
|
||||
test title, %{
|
||||
data: [result, input],
|
||||
example_context: context,
|
||||
inverse_context: inverse_context
|
||||
} do
|
||||
assert compact_iri(input, context, inverse_context) == result
|
||||
end
|
||||
end)
|
||||
|
||||
describe "with :vocab option" do
|
||||
%{
|
||||
"absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "foo"],
|
||||
"bnode" => ["_:a", "_:a"],
|
||||
"relative" => ["http://base/foo/bar", "http://base/foo/bar"],
|
||||
"odd CURIE" => ["experts", "http://example.org/perts"]
|
||||
"absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "foo"],
|
||||
"bnode" => ["_:a", "_:a"],
|
||||
"relative" => ["http://base/foo/bar", "http://base/foo/bar"],
|
||||
"odd CURIE" => ["experts", "http://example.org/perts"]
|
||||
}
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: [result, input], example_context: context,
|
||||
inverse_context: inverse_context} do
|
||||
assert compact_iri(input, context, inverse_context, nil, true) == result
|
||||
end
|
||||
end)
|
||||
@tag data: data
|
||||
test title, %{
|
||||
data: [result, input],
|
||||
example_context: context,
|
||||
inverse_context: inverse_context
|
||||
} do
|
||||
assert compact_iri(input, context, inverse_context, nil, true) == result
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "with @vocab" do
|
||||
|
@ -67,188 +76,249 @@ defmodule JSON.LD.IRICompactionTest do
|
|||
end
|
||||
|
||||
%{
|
||||
"absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
"prefix:suffix" => ["suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "foo"],
|
||||
"bnode" => ["_:a", "_:a"],
|
||||
"relative" => ["http://base/foo/bar", "http://base/foo/bar"],
|
||||
"odd CURIE" => ["experts", "http://example.org/perts"]
|
||||
"absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
"prefix:suffix" => ["suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "foo"],
|
||||
"bnode" => ["_:a", "_:a"],
|
||||
"relative" => ["http://base/foo/bar", "http://base/foo/bar"],
|
||||
"odd CURIE" => ["experts", "http://example.org/perts"]
|
||||
}
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: [result, input], example_context: context,
|
||||
inverse_context: inverse_context} do
|
||||
assert compact_iri(input, context, inverse_context, nil, true) == result
|
||||
end
|
||||
end)
|
||||
@tag data: data
|
||||
test title, %{
|
||||
data: [result, input],
|
||||
example_context: context,
|
||||
inverse_context: inverse_context
|
||||
} do
|
||||
assert compact_iri(input, context, inverse_context, nil, true) == result
|
||||
end
|
||||
end)
|
||||
|
||||
# TODO: we don't support 'position: :predicate'"
|
||||
# test "does not use @vocab if it would collide with a term" do
|
||||
# subject.set_mapping("name", "http://xmlns.com/foaf/0.1/name")
|
||||
# subject.set_mapping("ex", nil)
|
||||
# expect(subject.compact_iri("http://example.org/name", position: :predicate)).
|
||||
# to produce("lex:name", logger)
|
||||
# end
|
||||
# TODO: we don't support 'position: :predicate'"
|
||||
# test "does not use @vocab if it would collide with a term" do
|
||||
# subject.set_mapping("name", "http://xmlns.com/foaf/0.1/name")
|
||||
# subject.set_mapping("ex", nil)
|
||||
# expect(subject.compact_iri("http://example.org/name", position: :predicate)).
|
||||
# to produce("lex:name", logger)
|
||||
# end
|
||||
end
|
||||
|
||||
describe "with value" do
|
||||
setup do
|
||||
context = JSON.LD.context(%{
|
||||
"xsd" => XSD.__base_iri__,
|
||||
"plain" => "http://example.com/plain",
|
||||
"lang" => %{"@id" => "http://example.com/lang", "@language" => "en"},
|
||||
"bool" => %{"@id" => "http://example.com/bool", "@type" => "xsd:boolean"},
|
||||
"integer" => %{"@id" => "http://example.com/integer", "@type" => "xsd:integer"},
|
||||
"double" => %{"@id" => "http://example.com/double", "@type" => "xsd:double"},
|
||||
"date" => %{"@id" => "http://example.com/date", "@type" => "xsd:date"},
|
||||
"id" => %{"@id" => "http://example.com/id", "@type" => "@id"},
|
||||
"listplain" => %{"@id" => "http://example.com/plain", "@container" => "@list"},
|
||||
"listlang" => %{"@id" => "http://example.com/lang", "@language" => "en", "@container" => "@list"},
|
||||
"listbool" => %{"@id" => "http://example.com/bool", "@type" => "xsd:boolean", "@container" => "@list"},
|
||||
"listinteger" => %{"@id" => "http://example.com/integer", "@type" => "xsd:integer", "@container" => "@list"},
|
||||
"listdouble" => %{"@id" => "http://example.com/double", "@type" => "xsd:double", "@container" => "@list"},
|
||||
"listdate" => %{"@id" => "http://example.com/date", "@type" => "xsd:date", "@container" => "@list"},
|
||||
"listid" => %{"@id" => "http://example.com/id", "@type" => "@id", "@container" => "@list"},
|
||||
"setplain" => %{"@id" => "http://example.com/plain", "@container" => "@set"},
|
||||
"setlang" => %{"@id" => "http://example.com/lang", "@language" => "en", "@container" => "@set"},
|
||||
"setbool" => %{"@id" => "http://example.com/bool", "@type" => "xsd:boolean", "@container" => "@set"},
|
||||
"setinteger" => %{"@id" => "http://example.com/integer", "@type" => "xsd:integer", "@container" => "@set"},
|
||||
"setdouble" => %{"@id" => "http://example.com/double", "@type" => "xsd:double", "@container" => "@set"},
|
||||
"setdate" => %{"@id" => "http://example.com/date", "@type" => "xsd:date", "@container" => "@set"},
|
||||
"setid" => %{"@id" => "http://example.com/id", "@type" => "@id", "@container" => "@set"},
|
||||
"langmap" => %{"@id" => "http://example.com/langmap", "@container" => "@language"},
|
||||
})
|
||||
context =
|
||||
JSON.LD.context(%{
|
||||
"xsd" => XSD.__base_iri__(),
|
||||
"plain" => "http://example.com/plain",
|
||||
"lang" => %{"@id" => "http://example.com/lang", "@language" => "en"},
|
||||
"bool" => %{"@id" => "http://example.com/bool", "@type" => "xsd:boolean"},
|
||||
"integer" => %{"@id" => "http://example.com/integer", "@type" => "xsd:integer"},
|
||||
"double" => %{"@id" => "http://example.com/double", "@type" => "xsd:double"},
|
||||
"date" => %{"@id" => "http://example.com/date", "@type" => "xsd:date"},
|
||||
"id" => %{"@id" => "http://example.com/id", "@type" => "@id"},
|
||||
"listplain" => %{"@id" => "http://example.com/plain", "@container" => "@list"},
|
||||
"listlang" => %{
|
||||
"@id" => "http://example.com/lang",
|
||||
"@language" => "en",
|
||||
"@container" => "@list"
|
||||
},
|
||||
"listbool" => %{
|
||||
"@id" => "http://example.com/bool",
|
||||
"@type" => "xsd:boolean",
|
||||
"@container" => "@list"
|
||||
},
|
||||
"listinteger" => %{
|
||||
"@id" => "http://example.com/integer",
|
||||
"@type" => "xsd:integer",
|
||||
"@container" => "@list"
|
||||
},
|
||||
"listdouble" => %{
|
||||
"@id" => "http://example.com/double",
|
||||
"@type" => "xsd:double",
|
||||
"@container" => "@list"
|
||||
},
|
||||
"listdate" => %{
|
||||
"@id" => "http://example.com/date",
|
||||
"@type" => "xsd:date",
|
||||
"@container" => "@list"
|
||||
},
|
||||
"listid" => %{
|
||||
"@id" => "http://example.com/id",
|
||||
"@type" => "@id",
|
||||
"@container" => "@list"
|
||||
},
|
||||
"setplain" => %{"@id" => "http://example.com/plain", "@container" => "@set"},
|
||||
"setlang" => %{
|
||||
"@id" => "http://example.com/lang",
|
||||
"@language" => "en",
|
||||
"@container" => "@set"
|
||||
},
|
||||
"setbool" => %{
|
||||
"@id" => "http://example.com/bool",
|
||||
"@type" => "xsd:boolean",
|
||||
"@container" => "@set"
|
||||
},
|
||||
"setinteger" => %{
|
||||
"@id" => "http://example.com/integer",
|
||||
"@type" => "xsd:integer",
|
||||
"@container" => "@set"
|
||||
},
|
||||
"setdouble" => %{
|
||||
"@id" => "http://example.com/double",
|
||||
"@type" => "xsd:double",
|
||||
"@container" => "@set"
|
||||
},
|
||||
"setdate" => %{
|
||||
"@id" => "http://example.com/date",
|
||||
"@type" => "xsd:date",
|
||||
"@container" => "@set"
|
||||
},
|
||||
"setid" => %{"@id" => "http://example.com/id", "@type" => "@id", "@container" => "@set"},
|
||||
"langmap" => %{"@id" => "http://example.com/langmap", "@container" => "@language"}
|
||||
})
|
||||
|
||||
%{example_context: context, inverse_context: JSON.LD.Context.inverse(context)}
|
||||
end
|
||||
|
||||
%{
|
||||
"langmap" => %{"@value" => "en", "@language" => "en"},
|
||||
#"plain" => %{"@value" => "foo"},
|
||||
# "plain" => %{"@value" => "foo"},
|
||||
"setplain" => %{"@value" => "foo", "@language" => "pl"}
|
||||
}
|
||||
|> Enum.each(fn {prop, value} ->
|
||||
@tag data: {prop, value}
|
||||
test "uses #{prop} for #{inspect value}",
|
||||
%{data: {prop, value}, example_context: context,
|
||||
inverse_context: inverse_context} do
|
||||
assert compact_iri("http://example.com/#{String.replace(prop, "set", "")}",
|
||||
context, inverse_context, value, true) == prop
|
||||
end
|
||||
end)
|
||||
@tag data: {prop, value}
|
||||
test "uses #{prop} for #{inspect(value)}",
|
||||
%{data: {prop, value}, example_context: context, inverse_context: inverse_context} do
|
||||
assert compact_iri(
|
||||
"http://example.com/#{String.replace(prop, "set", "")}",
|
||||
context,
|
||||
inverse_context,
|
||||
value,
|
||||
true
|
||||
) == prop
|
||||
end
|
||||
end)
|
||||
|
||||
%{
|
||||
"listplain" => [
|
||||
[%{"@value" => "foo"}],
|
||||
[%{"@value" => "foo"}, %{"@value" => "bar"}, %{"@value" => "baz"}],
|
||||
[%{"@value" => "foo"}, %{"@value" => "bar"}, %{"@value" => 1}],
|
||||
[%{"@value" => "foo"}, %{"@value" => "bar"}, %{"@value" => 1.1}],
|
||||
[%{"@value" => "foo"}, %{"@value" => "bar"}, %{"@value" => true}],
|
||||
[%{"@value" => "de", "@language" => "de"}, %{"@value" => "jp", "@language" => "jp"}],
|
||||
[%{"@value" => true}],
|
||||
[%{"@value" => false}],
|
||||
[%{"@value" => 1}], [%{"@value" => 1.1}],
|
||||
],
|
||||
"listlang" => [[%{"@value" => "en", "@language" => "en"}]],
|
||||
"listbool" => [[%{"@value" => "true", "@type" => to_string(XSD.boolean)}]],
|
||||
"listinteger" => [[%{"@value" => "1", "@type" => to_string(XSD.integer)}]],
|
||||
"listdouble" => [[%{"@value" => "1", "@type" => to_string(XSD.double)}]],
|
||||
"listdate" => [[%{"@value" => "2012-04-17", "@type" => to_string(XSD.date)}]],
|
||||
}
|
||||
%{
|
||||
"listplain" => [
|
||||
[%{"@value" => "foo"}],
|
||||
[%{"@value" => "foo"}, %{"@value" => "bar"}, %{"@value" => "baz"}],
|
||||
[%{"@value" => "foo"}, %{"@value" => "bar"}, %{"@value" => 1}],
|
||||
[%{"@value" => "foo"}, %{"@value" => "bar"}, %{"@value" => 1.1}],
|
||||
[%{"@value" => "foo"}, %{"@value" => "bar"}, %{"@value" => true}],
|
||||
[%{"@value" => "de", "@language" => "de"}, %{"@value" => "jp", "@language" => "jp"}],
|
||||
[%{"@value" => true}],
|
||||
[%{"@value" => false}],
|
||||
[%{"@value" => 1}],
|
||||
[%{"@value" => 1.1}]
|
||||
],
|
||||
"listlang" => [[%{"@value" => "en", "@language" => "en"}]],
|
||||
"listbool" => [[%{"@value" => "true", "@type" => to_string(XSD.boolean())}]],
|
||||
"listinteger" => [[%{"@value" => "1", "@type" => to_string(XSD.integer())}]],
|
||||
"listdouble" => [[%{"@value" => "1", "@type" => to_string(XSD.double())}]],
|
||||
"listdate" => [[%{"@value" => "2012-04-17", "@type" => to_string(XSD.date())}]]
|
||||
}
|
||||
|> Enum.each(fn {prop, values} ->
|
||||
Enum.each values, fn value ->
|
||||
@tag data: {prop, value}
|
||||
test "for @list uses #{prop} for #{inspect %{"@list" => value}}",
|
||||
%{data: {prop, value}, example_context: context,
|
||||
inverse_context: inverse_context} do
|
||||
assert compact_iri("http://example.com/#{String.replace(prop, "list", "")}",
|
||||
context, inverse_context, %{"@list" => value}, true) == prop
|
||||
end
|
||||
end
|
||||
end)
|
||||
Enum.each(values, fn value ->
|
||||
@tag data: {prop, value}
|
||||
test "for @list uses #{prop} for #{inspect(%{"@list" => value})}",
|
||||
%{data: {prop, value}, example_context: context, inverse_context: inverse_context} do
|
||||
assert compact_iri(
|
||||
"http://example.com/#{String.replace(prop, "list", "")}",
|
||||
context,
|
||||
inverse_context,
|
||||
%{"@list" => value},
|
||||
true
|
||||
) == prop
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
# describe "with :simple_compact_iris" do
|
||||
# before(:each) { subject.instance_variable_get(:@options)[:simple_compact_iris] = true}
|
||||
#
|
||||
# %{
|
||||
# "nil" => [nil, nil],
|
||||
# "absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
# "prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", "http://empty/suffix"],
|
||||
# "unmapped" => ["foo", "foo"],
|
||||
# "bnode" => ["_:a", RDF::Node("a")],
|
||||
# "relative" => ["foo/bar", "http://base/foo/bar"],
|
||||
# "odd CURIE" => ["ex:perts", "http://example.org/perts"]
|
||||
# }.each do |title, (result, input)|
|
||||
# test title do
|
||||
# expect(subject.compact_iri(input)).to produce(result, logger)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# describe "and @vocab" do
|
||||
# before(:each) { subject.vocab = "http://example.org/"}
|
||||
#
|
||||
# %{
|
||||
# "absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
# "prefix:suffix" => ["suffix", "http://example.org/suffix"],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", "http://empty/suffix"],
|
||||
# "unmapped" => ["foo", "foo"],
|
||||
# "bnode" => ["_:a", RDF::Node("a")],
|
||||
# "relative" => ["http://base/foo/bar", "http://base/foo/bar"],
|
||||
# "odd CURIE" => ["experts", "http://example.org/perts"]
|
||||
# }.each do |title, (result, input)|
|
||||
# test title do
|
||||
# expect(subject.compact_iri(input, vocab: true)).to produce(result, logger)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# describe "with :simple_compact_iris" do
|
||||
# before(:each) { subject.instance_variable_get(:@options)[:simple_compact_iris] = true}
|
||||
#
|
||||
# %{
|
||||
# "nil" => [nil, nil],
|
||||
# "absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
# "prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", "http://empty/suffix"],
|
||||
# "unmapped" => ["foo", "foo"],
|
||||
# "bnode" => ["_:a", RDF::Node("a")],
|
||||
# "relative" => ["foo/bar", "http://base/foo/bar"],
|
||||
# "odd CURIE" => ["ex:perts", "http://example.org/perts"]
|
||||
# }.each do |title, (result, input)|
|
||||
# test title do
|
||||
# expect(subject.compact_iri(input)).to produce(result, logger)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# describe "and @vocab" do
|
||||
# before(:each) { subject.vocab = "http://example.org/"}
|
||||
#
|
||||
# %{
|
||||
# "absolute IRI" => ["http://example.com/", "http://example.com/"],
|
||||
# "prefix:suffix" => ["suffix", "http://example.org/suffix"],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", "http://empty/suffix"],
|
||||
# "unmapped" => ["foo", "foo"],
|
||||
# "bnode" => ["_:a", RDF::Node("a")],
|
||||
# "relative" => ["http://base/foo/bar", "http://base/foo/bar"],
|
||||
# "odd CURIE" => ["experts", "http://example.org/perts"]
|
||||
# }.each do |title, (result, input)|
|
||||
# test title do
|
||||
# expect(subject.compact_iri(input, vocab: true)).to produce(result, logger)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
describe "compact-0018" do
|
||||
setup do
|
||||
context = JSON.LD.context(Jason.decode! """
|
||||
{
|
||||
"id1": "http://example.com/id1",
|
||||
"type1": "http://example.com/t1",
|
||||
"type2": "http://example.com/t2",
|
||||
"@language": "de",
|
||||
"term": {
|
||||
"@id": "http://example.com/term"
|
||||
},
|
||||
"term1": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list"
|
||||
},
|
||||
"term2": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list",
|
||||
"@language": "en"
|
||||
},
|
||||
"term3": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list",
|
||||
"@language": null
|
||||
},
|
||||
"term4": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list",
|
||||
"@type": "type1"
|
||||
},
|
||||
"term5": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list",
|
||||
"@type": "type2"
|
||||
}
|
||||
}
|
||||
""")
|
||||
context =
|
||||
JSON.LD.context(
|
||||
Jason.decode!("""
|
||||
{
|
||||
"id1": "http://example.com/id1",
|
||||
"type1": "http://example.com/t1",
|
||||
"type2": "http://example.com/t2",
|
||||
"@language": "de",
|
||||
"term": {
|
||||
"@id": "http://example.com/term"
|
||||
},
|
||||
"term1": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list"
|
||||
},
|
||||
"term2": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list",
|
||||
"@language": "en"
|
||||
},
|
||||
"term3": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list",
|
||||
"@language": null
|
||||
},
|
||||
"term4": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list",
|
||||
"@type": "type1"
|
||||
},
|
||||
"term5": {
|
||||
"@id": "http://example.com/term",
|
||||
"@container": "@list",
|
||||
"@type": "type2"
|
||||
}
|
||||
}
|
||||
""")
|
||||
)
|
||||
|
||||
%{example_context: context, inverse_context: JSON.LD.Context.inverse(context)}
|
||||
end
|
||||
|
||||
|
||||
%{
|
||||
"term" => [
|
||||
'{ "@value": "v0.1", "@language": "de" }',
|
||||
|
@ -317,59 +387,68 @@ defmodule JSON.LD.IRICompactionTest do
|
|||
{ "@value": "v5.6", "@type": "http://example.com/t2" }
|
||||
]
|
||||
}
|
||||
""",
|
||||
"""
|
||||
}
|
||||
|> Enum.each(fn {term, values} ->
|
||||
values = if is_binary(values),
|
||||
values =
|
||||
if is_binary(values),
|
||||
do: [values],
|
||||
else: values
|
||||
Enum.each(values, fn value ->
|
||||
value = Jason.decode!(value)
|
||||
@tag data: {term, value}
|
||||
test "uses #{term} for #{inspect value, limit: 3}",
|
||||
%{data: {term, value}, example_context: context,
|
||||
inverse_context: inverse_context} do
|
||||
assert compact_iri("http://example.com/term", context, inverse_context,
|
||||
value, true) == term
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
Enum.each(values, fn value ->
|
||||
value = Jason.decode!(value)
|
||||
@tag data: {term, value}
|
||||
test "uses #{term} for #{inspect(value, limit: 3)}",
|
||||
%{data: {term, value}, example_context: context, inverse_context: inverse_context} do
|
||||
assert compact_iri("http://example.com/term", context, inverse_context, value, true) ==
|
||||
term
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
describe "compact-0020" do
|
||||
setup do
|
||||
context = JSON.LD.context(%{
|
||||
"ex" => "http://example.org/ns#",
|
||||
"ex:property" => %{"@container" => "@list"}
|
||||
})
|
||||
context =
|
||||
JSON.LD.context(%{
|
||||
"ex" => "http://example.org/ns#",
|
||||
"ex:property" => %{"@container" => "@list"}
|
||||
})
|
||||
|
||||
%{example_context: context, inverse_context: JSON.LD.Context.inverse(context)}
|
||||
end
|
||||
|
||||
@tag skip: "TODO: we don't support 'position: :subject'"
|
||||
test "Compact @id that is a property IRI when @container is @list", %{
|
||||
example_context: context, inverse_context: inverse_context} do
|
||||
assert compact_iri("http://example.org/ns#property", context, inverse_context) == "ex:property"
|
||||
# expect(ctx.compact_iri("http://example.org/ns#property", position: :subject)).
|
||||
# to produce("ex:property", logger)
|
||||
example_context: context,
|
||||
inverse_context: inverse_context
|
||||
} do
|
||||
assert compact_iri("http://example.org/ns#property", context, inverse_context) ==
|
||||
"ex:property"
|
||||
|
||||
# expect(ctx.compact_iri("http://example.org/ns#property", position: :subject)).
|
||||
# to produce("ex:property", logger)
|
||||
end
|
||||
end
|
||||
|
||||
describe "compact-0041" do
|
||||
setup do
|
||||
context = JSON.LD.context(%{
|
||||
"name" => %{"@id" => "http://example.com/property", "@container" => "@list"}
|
||||
})
|
||||
context =
|
||||
JSON.LD.context(%{
|
||||
"name" => %{"@id" => "http://example.com/property", "@container" => "@list"}
|
||||
})
|
||||
|
||||
%{example_context: context, inverse_context: JSON.LD.Context.inverse(context)}
|
||||
end
|
||||
|
||||
test "Does not use @list with @index", %{
|
||||
example_context: context, inverse_context: inverse_context} do
|
||||
assert compact_iri("http://example.com/property", context, inverse_context,
|
||||
%{
|
||||
"@list" => ["one item"],
|
||||
"@index" => "an annotation"
|
||||
}) == "http://example.com/property"
|
||||
example_context: context,
|
||||
inverse_context: inverse_context
|
||||
} do
|
||||
assert compact_iri("http://example.com/property", context, inverse_context, %{
|
||||
"@list" => ["one item"],
|
||||
"@index" => "an annotation"
|
||||
}) == "http://example.com/property"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -4,132 +4,130 @@ defmodule JSON.LD.IRIExpansionTest do
|
|||
import JSON.LD.IRIExpansion
|
||||
|
||||
setup do
|
||||
context = JSON.LD.context(%{
|
||||
"@base" => "http://base/",
|
||||
"@vocab" => "http://vocab/",
|
||||
"ex" => "http://example.org/",
|
||||
"" => "http://empty/",
|
||||
"_" => "http://underscore/"
|
||||
})
|
||||
context =
|
||||
JSON.LD.context(%{
|
||||
"@base" => "http://base/",
|
||||
"@vocab" => "http://vocab/",
|
||||
"ex" => "http://example.org/",
|
||||
"" => "http://empty/",
|
||||
"_" => "http://underscore/"
|
||||
})
|
||||
|
||||
%{example_context: context}
|
||||
end
|
||||
|
||||
test "bnode", %{example_context: context} do
|
||||
test "bnode", %{example_context: context} do
|
||||
assert expand_iri("_:a", context) == "_:a"
|
||||
end
|
||||
|
||||
describe "relative IRI with no options" do
|
||||
# TODO: Test this with RDF.URIs and RDF.BlankNodes
|
||||
# %{
|
||||
# "absolute IRI" => ["http://example.org/", ~I<http://example.org/>],
|
||||
# "term" => ["ex", ~I<ex>],
|
||||
# "prefix:suffix" => ["ex:suffix", ~I<http://example.org/suffix>],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", ~I<http://empty/suffix>],
|
||||
# "unmapped" => ["foo", ~I<foo>],
|
||||
# "empty term" => ["", ~I<>],
|
||||
# "another abs IRI"=>["ex://foo", ~I<ex://foo>],
|
||||
# "absolute IRI looking like a curie" =>
|
||||
# ["foo:bar", ~I<foo:bar>],
|
||||
# "bnode" => ["_:t0", RDF.bnode("t0")],
|
||||
# "_" => ["_", ~I<_>],
|
||||
# }
|
||||
# TODO: Test this with RDF.URIs and RDF.BlankNodes
|
||||
# %{
|
||||
# "absolute IRI" => ["http://example.org/", ~I<http://example.org/>],
|
||||
# "term" => ["ex", ~I<ex>],
|
||||
# "prefix:suffix" => ["ex:suffix", ~I<http://example.org/suffix>],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", ~I<http://empty/suffix>],
|
||||
# "unmapped" => ["foo", ~I<foo>],
|
||||
# "empty term" => ["", ~I<>],
|
||||
# "another abs IRI"=>["ex://foo", ~I<ex://foo>],
|
||||
# "absolute IRI looking like a curie" =>
|
||||
# ["foo:bar", ~I<foo:bar>],
|
||||
# "bnode" => ["_:t0", RDF.bnode("t0")],
|
||||
# "_" => ["_", ~I<_>],
|
||||
# }
|
||||
%{
|
||||
"absolute IRI" => ["http://example.org/", "http://example.org/"],
|
||||
"term" => ["ex", "ex"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "foo"],
|
||||
"empty term" => ["", ""],
|
||||
"another abs IRI"=>["ex://foo", "ex://foo"],
|
||||
"absolute IRI looking like a curie" =>
|
||||
["foo:bar", "foo:bar"],
|
||||
"bnode" => ["_:t0", "_:t0"],
|
||||
"_" => ["_", "_"],
|
||||
"absolute IRI" => ["http://example.org/", "http://example.org/"],
|
||||
"term" => ["ex", "ex"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "foo"],
|
||||
"empty term" => ["", ""],
|
||||
"another abs IRI" => ["ex://foo", "ex://foo"],
|
||||
"absolute IRI looking like a curie" => ["foo:bar", "foo:bar"],
|
||||
"bnode" => ["_:t0", "_:t0"],
|
||||
"_" => ["_", "_"]
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: [input, result], example_context: context} do
|
||||
assert expand_iri(input, context) == result
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: [input, result], example_context: context} do
|
||||
assert expand_iri(input, context) == result
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "relative IRI with base IRI" do
|
||||
# TODO: Test this with RDF.URIs and RDF.BlankNodes
|
||||
# %{
|
||||
# "absolute IRI" => ["http://example.org/", ~I<http://example.org/>],
|
||||
# "term" => ["ex", ~I<http://base/ex>],
|
||||
# "prefix:suffix" => ["ex:suffix", ~I<http://example.org/suffix>],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", ~I<http://empty/suffix>],
|
||||
# "unmapped" => ["foo", ~I<http://base/foo>],
|
||||
# "empty term" => ["", ~I<http://base/>],
|
||||
# "another abs IRI"=>["ex://foo", ~I<ex://foo>],
|
||||
# "absolute IRI looking like a curie" =>
|
||||
# ["foo:bar", ~I<foo:bar>],
|
||||
# "bnode" => ["_:t0", RDF.bnode("t0")],
|
||||
# "_" => ["_", ~I<http://base/_>],
|
||||
# }
|
||||
# TODO: Test this with RDF.URIs and RDF.BlankNodes
|
||||
# %{
|
||||
# "absolute IRI" => ["http://example.org/", ~I<http://example.org/>],
|
||||
# "term" => ["ex", ~I<http://base/ex>],
|
||||
# "prefix:suffix" => ["ex:suffix", ~I<http://example.org/suffix>],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", ~I<http://empty/suffix>],
|
||||
# "unmapped" => ["foo", ~I<http://base/foo>],
|
||||
# "empty term" => ["", ~I<http://base/>],
|
||||
# "another abs IRI"=>["ex://foo", ~I<ex://foo>],
|
||||
# "absolute IRI looking like a curie" =>
|
||||
# ["foo:bar", ~I<foo:bar>],
|
||||
# "bnode" => ["_:t0", RDF.bnode("t0")],
|
||||
# "_" => ["_", ~I<http://base/_>],
|
||||
# }
|
||||
%{
|
||||
"absolute IRI" => ["http://example.org/", "http://example.org/"],
|
||||
"term" => ["ex", "http://base/ex"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "http://base/foo"],
|
||||
"empty term" => ["", "http://base/"],
|
||||
"another abs IRI"=>["ex://foo", "ex://foo"],
|
||||
"absolute IRI looking like a curie" =>
|
||||
["foo:bar", "foo:bar"],
|
||||
"bnode" => ["_:t0", "_:t0"],
|
||||
"_" => ["_", "http://base/_"],
|
||||
"absolute IRI" => ["http://example.org/", "http://example.org/"],
|
||||
"term" => ["ex", "http://base/ex"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "http://base/foo"],
|
||||
"empty term" => ["", "http://base/"],
|
||||
"another abs IRI" => ["ex://foo", "ex://foo"],
|
||||
"absolute IRI looking like a curie" => ["foo:bar", "foo:bar"],
|
||||
"bnode" => ["_:t0", "_:t0"],
|
||||
"_" => ["_", "http://base/_"]
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: [input, result], example_context: context} do
|
||||
assert expand_iri(input, context, true) == result
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: [input, result], example_context: context} do
|
||||
assert expand_iri(input, context, true) == result
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
describe "relative IRI @vocab" do
|
||||
# TODO: Test this with RDF.URIs and RDF.BlankNodes
|
||||
# %{
|
||||
# "absolute IRI" => ["http://example.org/", ~I<http://example.org/>],
|
||||
# "term" => ["ex", ~I<http://example.org/>],
|
||||
# "prefix:suffix" => ["ex:suffix", ~I<http://example.org/suffix>],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", ~I<http://empty/suffix>],
|
||||
# "unmapped" => ["foo", ~I<http://vocab/foo>],
|
||||
# "empty term" => ["", ~I<http://empty/>],
|
||||
# "another abs IRI"=>["ex://foo", ~I<ex://foo>],
|
||||
# "absolute IRI looking like a curie" =>
|
||||
# ["foo:bar", ~I<foo:bar>],
|
||||
# "bnode" => ["_:t0", RDF.bode("t0")],
|
||||
# "_" => ["_", ~I<http://underscore/>],
|
||||
# }
|
||||
# TODO: Test this with RDF.URIs and RDF.BlankNodes
|
||||
# %{
|
||||
# "absolute IRI" => ["http://example.org/", ~I<http://example.org/>],
|
||||
# "term" => ["ex", ~I<http://example.org/>],
|
||||
# "prefix:suffix" => ["ex:suffix", ~I<http://example.org/suffix>],
|
||||
# "keyword" => ["@type", "@type"],
|
||||
# "empty" => [":suffix", ~I<http://empty/suffix>],
|
||||
# "unmapped" => ["foo", ~I<http://vocab/foo>],
|
||||
# "empty term" => ["", ~I<http://empty/>],
|
||||
# "another abs IRI"=>["ex://foo", ~I<ex://foo>],
|
||||
# "absolute IRI looking like a curie" =>
|
||||
# ["foo:bar", ~I<foo:bar>],
|
||||
# "bnode" => ["_:t0", RDF.bode("t0")],
|
||||
# "_" => ["_", ~I<http://underscore/>],
|
||||
# }
|
||||
%{
|
||||
"absolute IRI" => ["http://example.org/", "http://example.org/"],
|
||||
"term" => ["ex", "http://example.org/"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "http://vocab/foo"],
|
||||
"empty term" => ["", "http://empty/"],
|
||||
"another abs IRI"=>["ex://foo", "ex://foo"],
|
||||
"absolute IRI looking like a curie" =>
|
||||
["foo:bar", "foo:bar"],
|
||||
"bnode" => ["_:t0", "_:t0"],
|
||||
"_" => ["_", "http://underscore/"],
|
||||
"absolute IRI" => ["http://example.org/", "http://example.org/"],
|
||||
"term" => ["ex", "http://example.org/"],
|
||||
"prefix:suffix" => ["ex:suffix", "http://example.org/suffix"],
|
||||
"keyword" => ["@type", "@type"],
|
||||
"empty" => [":suffix", "http://empty/suffix"],
|
||||
"unmapped" => ["foo", "http://vocab/foo"],
|
||||
"empty term" => ["", "http://empty/"],
|
||||
"another abs IRI" => ["ex://foo", "ex://foo"],
|
||||
"absolute IRI looking like a curie" => ["foo:bar", "foo:bar"],
|
||||
"bnode" => ["_:t0", "_:t0"],
|
||||
"_" => ["_", "http://underscore/"]
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: [input, result], example_context: context} do
|
||||
assert expand_iri(input, context, false, true) == result
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{data: [input, result], example_context: context} do
|
||||
assert expand_iri(input, context, false, true) == result
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ defmodule JSON.LD.RemoteContextTest do
|
|||
|
||||
setup_all do
|
||||
local =
|
||||
Jason.decode! """
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": {
|
||||
"name": "http://xmlns.com/foaf/0.1/name",
|
||||
|
@ -14,16 +14,16 @@ defmodule JSON.LD.RemoteContextTest do
|
|||
"name": "Manu Sporny",
|
||||
"homepage": "http://manu.sporny.org/"
|
||||
}
|
||||
"""
|
||||
""")
|
||||
|
||||
remote =
|
||||
Jason.decode! """
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": "http://example.com/test-context",
|
||||
"name": "Manu Sporny",
|
||||
"homepage": "http://manu.sporny.org/"
|
||||
}
|
||||
"""
|
||||
""")
|
||||
|
||||
{:ok, local: local, remote: remote}
|
||||
end
|
||||
|
@ -42,13 +42,13 @@ defmodule JSON.LD.RemoteContextTest do
|
|||
|
||||
test "failed loading of remote context" do
|
||||
remote =
|
||||
Jason.decode! """
|
||||
Jason.decode!("""
|
||||
{
|
||||
"@context": "http://fake.com/fake-context",
|
||||
"name": "Manu Sporny",
|
||||
"homepage": "http://manu.sporny.org/"
|
||||
}
|
||||
"""
|
||||
""")
|
||||
|
||||
assert_raise LoadingRemoteContextFailedError, fn ->
|
||||
JSON.LD.flatten(remote, nil, %Options{document_loader: DocumentLoader.Test})
|
||||
|
|
|
@ -17,5 +17,4 @@ defmodule JSON.LD.UtilsTest do
|
|||
assert compact_iri_parts("_:bar") == nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -6,45 +6,72 @@ defmodule JSON.LD.ValueCompactionTest do
|
|||
alias RDF.NS.{XSD}
|
||||
|
||||
setup do
|
||||
context = JSON.LD.context(%{
|
||||
"dc" => "http://purl.org/dc/terms/", # TODO: RDF::Vocab::DC.to_uri.to_s,
|
||||
"ex" => "http://example.org/",
|
||||
"foaf" => "http://xmlns.com/foaf/0.1/", # TODO: RDF::Vocab::FOAF.to_uri.to_s,
|
||||
"xsd" => to_string(XSD.__base_iri__),
|
||||
"langmap" => %{"@id" => "http://example.com/langmap", "@container" => "@language"},
|
||||
"list" => %{"@id" => "http://example.org/list", "@container" => "@list"},
|
||||
"nolang" => %{"@id" => "http://example.org/nolang", "@language" => nil},
|
||||
"dc:created" => %{"@type" => to_string(XSD.date)},
|
||||
"foaf:age" => %{"@type" => to_string(XSD.integer)},
|
||||
"foaf:knows" => %{"@type" => "@id"},
|
||||
context =
|
||||
JSON.LD.context(%{
|
||||
# TODO: RDF::Vocab::DC.to_uri.to_s,
|
||||
"dc" => "http://purl.org/dc/terms/",
|
||||
"ex" => "http://example.org/",
|
||||
# TODO: RDF::Vocab::FOAF.to_uri.to_s,
|
||||
"foaf" => "http://xmlns.com/foaf/0.1/",
|
||||
"xsd" => to_string(XSD.__base_iri__()),
|
||||
"langmap" => %{"@id" => "http://example.com/langmap", "@container" => "@language"},
|
||||
"list" => %{"@id" => "http://example.org/list", "@container" => "@list"},
|
||||
"nolang" => %{"@id" => "http://example.org/nolang", "@language" => nil},
|
||||
"dc:created" => %{"@type" => to_string(XSD.date())},
|
||||
"foaf:age" => %{"@type" => to_string(XSD.integer())},
|
||||
"foaf:knows" => %{"@type" => "@id"}
|
||||
})
|
||||
|
||||
%{example_context: context, inverse_context: JSON.LD.Context.inverse(context)}
|
||||
end
|
||||
|
||||
%{
|
||||
"absolute IRI" => ["foaf:knows", "http://example.com/", %{"@id" => "http://example.com/"}],
|
||||
"prefix:suffix" => ["foaf:knows", "ex:suffix", %{"@id" => "http://example.org/suffix"}],
|
||||
"integer" => ["foaf:age", "54", %{"@value" => "54", "@type" => to_string(XSD.integer)}],
|
||||
"date " => ["dc:created", "2011-12-27Z", %{"@value" => "2011-12-27Z", "@type" => to_string(XSD.date)}],
|
||||
"no IRI" => ["foo", %{"@id" => "http://example.com/"}, %{"@id" => "http://example.com/"}],
|
||||
"no IRI (CURIE)" => ["foo", %{"@id" => "http://xmlns.com/foaf/0.1/Person"}, %{"@id" => "http://xmlns.com/foaf/0.1/Person"}],
|
||||
"no boolean" => ["foo", %{"@value" => "true", "@type" => to_string(XSD.boolean)},%{"@value" => "true", "@type" => to_string(XSD.boolean)}],
|
||||
"no integer" => ["foo", %{"@value" => "54", "@type" => to_string(XSD.integer)},%{"@value" => "54", "@type" => to_string(XSD.integer)}],
|
||||
"no date " => ["foo", %{"@value" => "2011-12-27Z", "@type" => to_string(XSD.date)}, %{"@value" => "2011-12-27Z", "@type" => to_string(XSD.date)}],
|
||||
"no string " => ["foo", "string", %{"@value" => "string"}],
|
||||
"no lang " => ["nolang", "string", %{"@value" => "string"}],
|
||||
"native boolean" => ["foo", true, %{"@value" => true}],
|
||||
"native integer" => ["foo", 1, %{"@value" => 1}],
|
||||
"native integer(list)"=>["list", 1, %{"@value" => 1}],
|
||||
"native double" => ["foo", 1.1e1, %{"@value" => 1.1E1}],
|
||||
"absolute IRI" => ["foaf:knows", "http://example.com/", %{"@id" => "http://example.com/"}],
|
||||
"prefix:suffix" => ["foaf:knows", "ex:suffix", %{"@id" => "http://example.org/suffix"}],
|
||||
"integer" => ["foaf:age", "54", %{"@value" => "54", "@type" => to_string(XSD.integer())}],
|
||||
"date " => [
|
||||
"dc:created",
|
||||
"2011-12-27Z",
|
||||
%{"@value" => "2011-12-27Z", "@type" => to_string(XSD.date())}
|
||||
],
|
||||
"no IRI" => ["foo", %{"@id" => "http://example.com/"}, %{"@id" => "http://example.com/"}],
|
||||
"no IRI (CURIE)" => [
|
||||
"foo",
|
||||
%{"@id" => "http://xmlns.com/foaf/0.1/Person"},
|
||||
%{"@id" => "http://xmlns.com/foaf/0.1/Person"}
|
||||
],
|
||||
"no boolean" => [
|
||||
"foo",
|
||||
%{"@value" => "true", "@type" => to_string(XSD.boolean())},
|
||||
%{"@value" => "true", "@type" => to_string(XSD.boolean())}
|
||||
],
|
||||
"no integer" => [
|
||||
"foo",
|
||||
%{"@value" => "54", "@type" => to_string(XSD.integer())},
|
||||
%{"@value" => "54", "@type" => to_string(XSD.integer())}
|
||||
],
|
||||
"no date " => [
|
||||
"foo",
|
||||
%{"@value" => "2011-12-27Z", "@type" => to_string(XSD.date())},
|
||||
%{"@value" => "2011-12-27Z", "@type" => to_string(XSD.date())}
|
||||
],
|
||||
"no string " => ["foo", "string", %{"@value" => "string"}],
|
||||
"no lang " => ["nolang", "string", %{"@value" => "string"}],
|
||||
"native boolean" => ["foo", true, %{"@value" => true}],
|
||||
"native integer" => ["foo", 1, %{"@value" => 1}],
|
||||
"native integer(list)" => ["list", 1, %{"@value" => 1}],
|
||||
"native double" => ["foo", 1.1e1, %{"@value" => 1.1e1}]
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: [key, compacted, expanded], example_context: context,
|
||||
inverse_context: inverse_context} do
|
||||
assert compact_value(expanded, context, inverse_context, key) == compacted
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{
|
||||
data: [key, compacted, expanded],
|
||||
example_context: context,
|
||||
inverse_context: inverse_context
|
||||
} do
|
||||
assert compact_value(expanded, context, inverse_context, key) == compacted
|
||||
end
|
||||
end)
|
||||
|
||||
describe "@language" do
|
||||
setup %{example_context: context} do
|
||||
|
@ -53,55 +80,81 @@ defmodule JSON.LD.ValueCompactionTest do
|
|||
end
|
||||
|
||||
%{
|
||||
"@id" => ["foo", %{"@id" => "foo"}, %{"@id" => "foo"}],
|
||||
"integer" => ["foo", %{"@value" => "54", "@type" => to_string(XSD.integer)}, %{"@value" => "54", "@type" => to_string(XSD.integer)}],
|
||||
"date" => ["foo", %{"@value" => "2011-12-27Z","@type" => to_string(XSD.date)},%{"@value" => "2011-12-27Z", "@type" => to_string(XSD.date)}],
|
||||
"no lang" => ["foo", %{"@value" => "foo" }, %{"@value" => "foo"}],
|
||||
"same lang" => ["foo", "foo", %{"@value" => "foo", "@language" => "en"}],
|
||||
"other lang" => ["foo", %{"@value" => "foo", "@language" => "bar"}, %{"@value" => "foo", "@language" => "bar"}],
|
||||
"langmap" => ["langmap", "en", %{"@value" => "en", "@language" => "en"}],
|
||||
"no lang with @type coercion" => ["dc:created", %{"@value" => "foo"}, %{"@value" => "foo"}],
|
||||
"no lang with @id coercion" => ["foaf:knows", %{"@value" => "foo"}, %{"@value" => "foo"}],
|
||||
"no lang with @language=null" => ["nolang", "string", %{"@value" => "string"}],
|
||||
"same lang with @type coercion" => ["dc:created", %{"@value" => "foo"}, %{"@value" => "foo"}],
|
||||
"same lang with @id coercion" => ["foaf:knows", %{"@value" => "foo"}, %{"@value" => "foo"}],
|
||||
"other lang with @type coercion" => ["dc:created", %{"@value" => "foo", "@language" => "bar"}, %{"@value" => "foo", "@language" => "bar"}],
|
||||
"other lang with @id coercion" => ["foaf:knows", %{"@value" => "foo", "@language" => "bar"}, %{"@value" => "foo", "@language" => "bar"}],
|
||||
"native boolean" => ["foo", true, %{"@value" => true}],
|
||||
"native integer" => ["foo", 1, %{"@value" => 1}],
|
||||
"native integer(list)" => ["list", 1, %{"@value" => 1}],
|
||||
"native double" => ["foo", 1.1e1, %{"@value" => 1.1E1}],
|
||||
"@id" => ["foo", %{"@id" => "foo"}, %{"@id" => "foo"}],
|
||||
"integer" => [
|
||||
"foo",
|
||||
%{"@value" => "54", "@type" => to_string(XSD.integer())},
|
||||
%{"@value" => "54", "@type" => to_string(XSD.integer())}
|
||||
],
|
||||
"date" => [
|
||||
"foo",
|
||||
%{"@value" => "2011-12-27Z", "@type" => to_string(XSD.date())},
|
||||
%{"@value" => "2011-12-27Z", "@type" => to_string(XSD.date())}
|
||||
],
|
||||
"no lang" => ["foo", %{"@value" => "foo"}, %{"@value" => "foo"}],
|
||||
"same lang" => ["foo", "foo", %{"@value" => "foo", "@language" => "en"}],
|
||||
"other lang" => [
|
||||
"foo",
|
||||
%{"@value" => "foo", "@language" => "bar"},
|
||||
%{"@value" => "foo", "@language" => "bar"}
|
||||
],
|
||||
"langmap" => ["langmap", "en", %{"@value" => "en", "@language" => "en"}],
|
||||
"no lang with @type coercion" => ["dc:created", %{"@value" => "foo"}, %{"@value" => "foo"}],
|
||||
"no lang with @id coercion" => ["foaf:knows", %{"@value" => "foo"}, %{"@value" => "foo"}],
|
||||
"no lang with @language=null" => ["nolang", "string", %{"@value" => "string"}],
|
||||
"same lang with @type coercion" => [
|
||||
"dc:created",
|
||||
%{"@value" => "foo"},
|
||||
%{"@value" => "foo"}
|
||||
],
|
||||
"same lang with @id coercion" => ["foaf:knows", %{"@value" => "foo"}, %{"@value" => "foo"}],
|
||||
"other lang with @type coercion" => [
|
||||
"dc:created",
|
||||
%{"@value" => "foo", "@language" => "bar"},
|
||||
%{"@value" => "foo", "@language" => "bar"}
|
||||
],
|
||||
"other lang with @id coercion" => [
|
||||
"foaf:knows",
|
||||
%{"@value" => "foo", "@language" => "bar"},
|
||||
%{"@value" => "foo", "@language" => "bar"}
|
||||
],
|
||||
"native boolean" => ["foo", true, %{"@value" => true}],
|
||||
"native integer" => ["foo", 1, %{"@value" => 1}],
|
||||
"native integer(list)" => ["list", 1, %{"@value" => 1}],
|
||||
"native double" => ["foo", 1.1e1, %{"@value" => 1.1e1}]
|
||||
}
|
||||
|> Enum.each(fn ({title, data}) ->
|
||||
@tag data: data
|
||||
test title, %{data: [key, compacted, expanded], example_context: context,
|
||||
inverse_context: inverse_context} do
|
||||
assert compact_value(expanded, context, inverse_context, key) == compacted
|
||||
end
|
||||
end)
|
||||
|> Enum.each(fn {title, data} ->
|
||||
@tag data: data
|
||||
test title, %{
|
||||
data: [key, compacted, expanded],
|
||||
example_context: context,
|
||||
inverse_context: inverse_context
|
||||
} do
|
||||
assert compact_value(expanded, context, inverse_context, key) == compacted
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
# TODO
|
||||
# describe "keywords" do
|
||||
# before(:each) do
|
||||
# subject.set_mapping("id", "@id")
|
||||
# subject.set_mapping("type", "@type")
|
||||
# subject.set_mapping("list", "@list")
|
||||
# subject.set_mapping("set", "@set")
|
||||
# subject.set_mapping("language", "@language")
|
||||
# subject.set_mapping("literal", "@value")
|
||||
# end
|
||||
#
|
||||
# %{
|
||||
# "@id" => [%{"id" => "http://example.com/"}, %{"@id" => "http://example.com/"}],
|
||||
# "@type" => [%{"literal" => "foo", "type" => "http://example.com/"},
|
||||
# %{"@value" => "foo", "@type" => "http://example.com/"}],
|
||||
# "@value" => [%{"literal" => "foo", "language" => "bar"}, %{"@value" => "foo", "@language" => "bar"}],
|
||||
# }.each do |title, (compacted, expanded)|
|
||||
# test title do
|
||||
# expect(subject.compact_value("foo", expanded)).to produce(compacted, logger)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# TODO
|
||||
# describe "keywords" do
|
||||
# before(:each) do
|
||||
# subject.set_mapping("id", "@id")
|
||||
# subject.set_mapping("type", "@type")
|
||||
# subject.set_mapping("list", "@list")
|
||||
# subject.set_mapping("set", "@set")
|
||||
# subject.set_mapping("language", "@language")
|
||||
# subject.set_mapping("literal", "@value")
|
||||
# end
|
||||
#
|
||||
# %{
|
||||
# "@id" => [%{"id" => "http://example.com/"}, %{"@id" => "http://example.com/"}],
|
||||
# "@type" => [%{"literal" => "foo", "type" => "http://example.com/"},
|
||||
# %{"@value" => "foo", "@type" => "http://example.com/"}],
|
||||
# "@value" => [%{"literal" => "foo", "language" => "bar"}, %{"@value" => "foo", "@language" => "bar"}],
|
||||
# }.each do |title, (compacted, expanded)|
|
||||
# test title do
|
||||
# expect(subject.compact_value("foo", expanded)).to produce(compacted, logger)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue