Skip to content

Commit

Permalink
Merge a9f39d4 into db345f2
Browse files Browse the repository at this point in the history
  • Loading branch information
gkellogg committed Jan 8, 2017
2 parents db345f2 + a9f39d4 commit 5e31405
Show file tree
Hide file tree
Showing 6 changed files with 389 additions and 69 deletions.
28 changes: 25 additions & 3 deletions lib/json/ld/compact.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,32 @@ def compact(element, property: nil)
end
end

if %w(@language @index).include?(container)
if %w(@language @index @id @type).include?(container)
map_object = result[item_active_property] ||= {}
compacted_item = compacted_item['@value'] if container == '@language' && value?(compacted_item)
map_key = expanded_item[container]
compacted_item = case container
when '@id'
id_prop = context.compact_iri('@id', vocab: true, quiet: true)
map_key = compacted_item[id_prop]
compacted_item.delete(id_prop)
compacted_item
when '@index'
index_prop = context.compact_iri('@index', vocab: true, quiet: true)
map_key = expanded_item[container]
#compacted_item.delete(index_prop)
compacted_item
when '@language'
map_key = expanded_item[container]
value?(expanded_item) ? expanded_item['@value'] : compacted_item
when '@type'
type_prop = context.compact_iri('@type', vocab: true, quiet: true)
map_key, types = Array(compacted_item[type_prop])
if Array(types).empty?
compacted_item.delete(type_prop)
else
compacted_item[type_prop] = types
end
compacted_item
end
merge_compacted_value(map_object, map_key, compacted_item)
else
compacted_item = [compacted_item] if
Expand Down
42 changes: 30 additions & 12 deletions lib/json/ld/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class TermDefinition
# @return [String] Type mapping
attr_accessor :type_mapping

# @return ['@set', '@list'] Container mapping
# @return ['@index', '@language', '@index', '@set', '@type', '@id'] Container mapping
attr_accessor :container_mapping

# Language mapping of term, `false` is used if there is explicitly no language mapping for this term.
Expand All @@ -62,7 +62,7 @@ def simple?; simple; end
# @param [String] term
# @param [String] id
# @param [String] type_mapping Type mapping
# @param ['@set', '@list'] container_mapping
# @param ['@index', '@language', '@index', '@set', '@type', '@id'] container_mapping
# @param [String] language_mapping
# Language mapping of term, `false` is used if there is explicitly no language mapping for this term
# @param [Boolean] reverse_property
Expand Down Expand Up @@ -569,12 +569,14 @@ def create_term_definition(local_context, term, defined)
raise JsonLdError::InvalidIRIMapping, "non-absolute @reverse IRI: #{definition.id} on term #{term.inspect}" unless
definition.id.is_a?(RDF::URI) && definition.id.absolute?

# If value contains an @container member, set the container mapping of definition to its value; if its value is neither @set, nor @index, nor null, an invalid reverse property error has been detected (reverse properties only support set- and index-containers) and processing is aborted.
if (container = value.fetch('@container', false))
# If value contains an @container member, set the container mapping of definition to its value; if its value is neither @set, @index, @type, @id, an absolute IRI nor null, an invalid reverse property error has been detected (reverse properties only support set- and index-containers) and processing is aborted.
if value.has_key?('@container')
container = value['@container']
# FIXME: Are URIS, @id, and @type reasonable for reverse mappings?
raise JsonLdError::InvalidReverseProperty,
"unknown mapping for '@container' to #{container.inspect} on term #{term.inspect}" unless
['@set', '@index', nil].include?(container)
definition.container_mapping = container
"unknown mapping for '@container' to #{container.inspect} on term #{term.inspect}" if
%w(@language @list).include?(container)
definition.container_mapping = check_container(container, local_context, defined, term)
end
definition.reverse_property = true
elsif value.has_key?('@id') && value['@id'] != term
Expand Down Expand Up @@ -610,10 +612,8 @@ def create_term_definition(local_context, term, defined)
@iri_to_term[definition.id] = term if simple_term && definition.id

if value.has_key?('@container')
container = value['@container']
raise JsonLdError::InvalidContainerMapping, "unknown mapping for '@container' to #{container.inspect} on term #{term.inspect}" unless %w(@list @set @language @index).include?(container)
#log_debug("") {"container_mapping: #{container.inspect}"}
definition.container_mapping = container
#log_debug("") {"container_mapping: #{value['@container'].inspect}"}
definition.container_mapping = check_container(value['@container'], local_context, defined, term)
end

if value.has_key?('@context')
Expand Down Expand Up @@ -956,7 +956,12 @@ def compact_iri(iri, value: nil, vocab: nil, reverse: false, quiet: false, **opt
default_language = self.default_language || @none
containers = []
tl, tl_value = "@language", "@null"
containers << '@index' if index?(value)

# If the value is a JSON Object, then for the keywords @index, @id, and @type, if the value contains that keyword, append it to containers.
%w(@index @id @type).each do |kw|
containers << kw if value.has_key?(kw)
end if value.is_a?(Hash)

if reverse
tl, tl_value = "@type", "@reverse"
containers << '@set'
Expand Down Expand Up @@ -1485,5 +1490,18 @@ def languages
memo
end
end

# Ensure @container mapping is appropriate
# The result is the original container definition. For IRI containers, this is necessary to be able to determine the @type mapping for string values
def check_container(container, local_context, defined, term)
case container
when '@set', '@list', '@language', '@index', '@type', '@id', nil
# Okay
else
raise JsonLdError::InvalidContainerMapping,
"unknown mapping for '@container' to #{container.inspect} on term #{term.inspect}"
end
container
end
end
end
17 changes: 12 additions & 5 deletions lib/json/ld/expand.rb
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ def expand(input, active_property, context, ordered: true)
# Use a term-specific context, if defined
term_context = context.term_definitions[key].context if context.term_definitions[key]
active_context = term_context ? context.parse(term_context) : context
expanded_value = if active_context.container(key) == '@language' && value.is_a?(Hash)
container = active_context.container(key)
expanded_value = if container == '@language' && value.is_a?(Hash)
# 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:

# Set multilingual array to an empty array.
Expand All @@ -272,19 +273,25 @@ def expand(input, active_property, context, ordered: true)
end

ary
elsif active_context.container(key) == '@index' && value.is_a?(Hash)
# Otherwise, if key's container mapping in active context is @index and value is a JSON object then value is expanded from an index map as follows:
elsif %w(@index @id @type).include?(container) && value.is_a?(Hash)
# Otherwise, if key's container mapping in active context is @index, @id, @type, an IRI or Blank Node and value is a JSON object then value is expanded from an index map as follows:

# Set ary to an empty array.
ary = []
container, ary = container.to_s, []

# For each key-value in the object:
keys = ordered ? value.keys.sort : value.keys
keys.each do |k|
# Initialize index value to the result of using this algorithm recursively, passing active context, key as active property, and index value as element.
index_value = expand([value[k]].flatten, key, active_context, ordered: ordered)
index_value.each do |item|
item['@index'] ||= k
case container
when '@id', '@index' then item[container] ||= k
# If container is @type add the key-value pair (@type-[index]) to item, appending any existing values in item
when '@type' then item[container] = [k].concat(Array(item[container]))
end

# Append item to expanded value.
ary << item
end
end
Expand Down
149 changes: 148 additions & 1 deletion spec/compact_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,46 @@
end
end

context "language maps" do
context "@container: @index" do
{
"compact-0029" => {
input: %([{
"@id": "http://example.com/article",
"http://example.com/vocab/author": [{
"@id": "http://example.org/person/1",
"@index": "regular"
}, {
"@id": "http://example.org/guest/cd24f329aa",
"@index": "guest"
}]
}]),
context: %({
"author": {"@id": "http://example.com/vocab/author", "@container": "@index" }
}),
output: %({
"@context": {
"author": {
"@id": "http://example.com/vocab/author",
"@container": "@index"
}
},
"@id": "http://example.com/article",
"author": {
"regular": {
"@id": "http://example.org/person/1"
},
"guest": {
"@id": "http://example.org/guest/cd24f329aa"
}
}
})
},
}.each_pair do |title, params|
it(title) {run_compact(params)}
end
end

context "@container: @language" do
{
"compact-0024" => {
input: %([
Expand Down Expand Up @@ -440,6 +479,114 @@
end
end

context "@container: @id" do
{
"Indexes to object not having an @id" => {
input: %([{
"http://example/idmap": [
{"http://example/label": [{"@value": "Object with @id _:bar"}], "@id": "_:bar"},
{"http://example/label": [{"@value": "Object with @id <foo>"}], "@id": "http://example.org/foo"}
]
}]),
context: %({
"@vocab": "http://example/",
"idmap": {"@container": "@id"}
}),
output: %({
"@context": {
"@vocab": "http://example/",
"idmap": {"@container": "@id"}
},
"idmap": {
"http://example.org/foo": {"label": "Object with @id <foo>"},
"_:bar": {"label": "Object with @id _:bar"}
}
}),
},
"Indexes to object already having an @id" => {
input: %([{
"http://example/idmap": [
{"@id": "_:foo", "http://example/label": [{"@value": "Object with @id _:bar"}]},
{"@id": "http://example.org/bar", "http://example/label": [{"@value": "Object with @id <foo>"}]}
]
}]),
context: %({
"@vocab": "http://example/",
"idmap": {"@container": "@id"}
}),
output: %({
"@context": {
"@vocab": "http://example/",
"idmap": {"@container": "@id"}
},
"idmap": {
"_:foo": {"label": "Object with @id _:bar"},
"http://example.org/bar": {"label": "Object with @id <foo>"}
}
}),
},
}.each_pair do |title, params|
it(title) {run_compact(params)}
end
end

context "@container: @type" do
{
"Indexes to object not having an @type" => {
input: %([{
"http://example/typemap": [
{"http://example/label": [{"@value": "Object with @type _:bar"}], "@type": ["_:bar"]},
{"http://example/label": [{"@value": "Object with @type <foo>"}], "@type": ["http://example.org/foo"]}
]
}]),
context: %({
"@vocab": "http://example/",
"typemap": {"@container": "@type"}
}),
output: %({
"@context": {
"@vocab": "http://example/",
"typemap": {"@container": "@type"}
},
"typemap": {
"http://example.org/foo": {"label": "Object with @type <foo>"},
"_:bar": {"label": "Object with @type _:bar"}
}
})
},
"Indexes to object already having an @type" => {
input: %([{
"http://example/typemap": [
{
"@type": ["_:bar", "_:foo"],
"http://example/label": [{"@value": "Object with @type _:bar"}]
},
{
"@type": ["http://example.org/foo", "http://example.org/bar"],
"http://example/label": [{"@value": "Object with @type <foo>"}]
}
]
}]),
context: %({
"@vocab": "http://example/",
"typemap": {"@container": "@type"}
}),
output: %({
"@context": {
"@vocab": "http://example/",
"typemap": {"@container": "@type"}
},
"typemap": {
"http://example.org/foo": {"@type": "http://example.org/bar", "label": "Object with @type <foo>"},
"_:bar": {"@type": "_:foo", "label": "Object with @type _:bar"}
}
})
},
}.each_pair do |title, params|
it(title) {run_compact(params)}
end
end

context "@graph" do
{
"Uses @graph given mutliple inputs" => {
Expand Down
Loading

0 comments on commit 5e31405

Please sign in to comment.