diff --git a/lib/json/ld/context.ex b/lib/json/ld/context.ex index 72966a2..76019f2 100644 --- a/lib/json/ld/context.ex +++ b/lib/json/ld/context.ex @@ -15,35 +15,34 @@ defmodule JSON.LD.Context do def create(%{"@context" => json_ld_context}, options), - do: new(options) |> update(json_ld_context) + do: new(options) |> update(json_ld_context, [], options) - def update(active, local, remote \\ []) + def update(active, local, remote \\ [], options \\ %JSON.LD.Options{}) - def update(%JSON.LD.Context{} = active, local, remote) when is_list(local) do + def update(%JSON.LD.Context{} = active, local, remote, options) when is_list(local) do Enum.reduce local, active, fn (local, result) -> - do_update(result, local, remote) + do_update(result, local, remote, options) end end # 2) If local context is not an array, set it to an array containing only local context. - def update(%JSON.LD.Context{} = active, local, remote) do - update(active, [local], remote) + def update(%JSON.LD.Context{} = active, local, remote, options) 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{} = active, nil, remote) do - # TODO: "If set, the base option of a JSON-LD API Implementation overrides the base IRI." - JSON.LD.Context.new(base: active.base_iri) + defp do_update(%JSON.LD.Context{} = active, nil, remote, options) do + JSON.LD.Context.new(base: JSON.LD.Options.new(options).base || active.base_iri) end # 3.2) If context is a string, [it's interpreted as a remote context] - defp do_update(%JSON.LD.Context{} = active, local, remote) when is_binary(local) do + defp do_update(%JSON.LD.Context{} = active, local, remote, options) when is_binary(local) do # TODO: fetch remote context and call recursively with remote updated end # 3.4) - 3.8) - defp do_update(%JSON.LD.Context{} = active, local, remote) when is_map(local) do + 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), {language, local} <- Map.pop(local, "@language", false) do @@ -56,7 +55,7 @@ defmodule JSON.LD.Context do end # 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, _), + defp do_update(_, local, _, _), do: raise JSON.LD.InvalidLocalContextError, message: "#{inspect local} is not a valid @context value" diff --git a/lib/json/ld/expansion.ex b/lib/json/ld/expansion.ex index d485e0a..bc35060 100644 --- a/lib/json/ld/expansion.ex +++ b/lib/json/ld/expansion.ex @@ -72,7 +72,7 @@ defmodule JSON.LD.Expansion do when is_map(element) do # 5) if Map.has_key?(element, "@context") do - active_context = JSON.LD.Context.update(active_context, Map.get(element, "@context")) + active_context = JSON.LD.Context.update(active_context, Map.get(element, "@context"), [], options) end # 6) and 7) result = element @@ -206,7 +206,6 @@ defmodule JSON.LD.Expansion 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 -# |> IO.inspect(label: "value") |> Enum.sort_by(fn {language, _} -> language end) |> Enum.reduce([], fn ({language, language_value}, language_map_result) -> language_map_result ++ ( @@ -225,7 +224,6 @@ defmodule JSON.LD.Expansion do end) ) -# |> IO.inspect(label: "result") end) # 7.6) is_map(value) && term_def && term_def.container_mapping == "@index" -> @@ -327,7 +325,8 @@ defmodule JSON.LD.Expansion do do: result = nil # 12) If active property is null or @graph, drop free-floating values as follows: - if active_property in [nil, "@graph"] and ( + # Spec FIXME: Due to case 10) we might land with a list here; other implementations deal with that, by just returning in step 10) + 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"))), @@ -349,7 +348,8 @@ defmodule JSON.LD.Expansion do Details at """ def expand_value(active_context, active_property, value) do - with term_def when term_def != nil <- active_context.term_defs[active_property] 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)} @@ -370,8 +370,6 @@ defmodule JSON.LD.Expansion do true -> %{"@value" => value} end - else - _ -> %{"@value" => value} end end diff --git a/lib/json/ld/iri_expansion.ex b/lib/json/ld/iri_expansion.ex index fb8c568..57a0b9a 100644 --- a/lib/json/ld/iri_expansion.ex +++ b/lib/json/ld/iri_expansion.ex @@ -30,8 +30,8 @@ defmodule JSON.LD.IRIExpansion do result = cond do # 3) If vocab is true and the active context has a term definition for value, return the associated IRI mapping. - vocab && (term_def = active_context.term_defs[value]) -> - term_def.iri_mapping + vocab && Map.has_key?(active_context.term_defs, value) -> + (term_def = active_context.term_defs[value]) && term_def.iri_mapping # 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 diff --git a/test/suite/expand_test.exs b/test/suite/expand_test.exs new file mode 100644 index 0000000..44e9523 --- /dev/null +++ b/test/suite/expand_test.exs @@ -0,0 +1,21 @@ +defmodule JSON.LD.TestSuite.ExpandTest do + use ExUnit.Case, async: false + + import JSON.LD.TestSuite + + setup_all do + [base_iri: manifest("expand")["baseIri"]] + end + + test_cases("expand") + |> Enum.each(fn %{"name" => name, "input" => input} = test_case -> + @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 diff --git a/test/unit/expansion_test.exs b/test/unit/expansion_test.exs index 4cfec39..56794ec 100644 --- a/test/unit/expansion_test.exs +++ b/test/unit/expansion_test.exs @@ -911,7 +911,6 @@ defmodule JSON.LD.ExpansionTest do "ex:integer" => %{"@type" => "xsd:integer"}, "ex:double" => %{"@type" => "xsd:double"}, "ex:boolean" => %{"@type" => "xsd:boolean"}, - "@language" => "en" }) %{example_context: context} end