lib/json/ld/evaluation_context.rb in json-ld-0.1.4.2 vs lib/json/ld/evaluation_context.rb in json-ld-0.1.5

- old
+ new

@@ -62,10 +62,18 @@ # # # This adds a language to plain strings that aren't otherwise coerced # @attr [String] attr :default_language, true + + # Default vocabulary + # + # + # Sets the default vocabulary used for expanding terms which + # aren't otherwise absolute IRIs + # @attr [String] + attr :vocab, true # Global options used in generating IRIs # @attr [Hash] options attr :options, true @@ -147,22 +155,37 @@ ec.provided_context = context debug("parse") {"=> provided_context: #{context.inspect}"} ec when Hash new_ec = self.dup - new_ec.provided_context = context - + new_ec.provided_context = context.dup + + { + '@language' => :default_language=, + '@vocab' => :vocab= + }.each do |key, setter| + v = context.fetch(key, false) + if v.nil? || v.is_a?(String) + context.delete(key) + debug("parse") {"Hash[#{key}] = #{v.inspect}"} + new_ec.send(setter, v) + elsif v + raise InvalidContext::Syntax, "#{key.inspect} is invalid" + end + end + num_updates = 1 while num_updates > 0 do num_updates = 0 # Map terms to IRIs/keywords first context.each do |key, value| # Expand a string value, unless it matches a keyword debug("parse") {"Hash[#{key}] = #{value.inspect}"} - if key == '@language' && (value.nil? || value.is_a?(String)) - new_ec.default_language = value + + if KEYWORDS.include?(key) + raise InvalidContext::Syntax, "key #{key.inspect} must not be a keyword" elsif term_valid?(key) # Remove all coercion information for the property new_ec.set_coerce(key, nil) new_ec.set_container(key, nil) @languages.delete(key) @@ -170,13 +193,11 @@ # Extract IRI mapping. This is complicated, as @id may have been aliased value = value.fetch('@id', nil) if value.is_a?(Hash) raise InvalidContext::Syntax, "unknown mapping for #{key.inspect} to #{value.class}" unless value.is_a?(String) || value.nil? iri = new_ec.expand_iri(value, :position => :predicate) if value.is_a?(String) - if iri && KEYWORDS.include?(key) - raise InvalidContext::Syntax, "key #{key.inspect} must not be a keyword" - elsif iri && new_ec.mappings.fetch(key, nil) != iri + if iri && new_ec.mappings.fetch(key, nil) != iri # Record term definition new_ec.set_mapping(key, iri) num_updates += 1 elsif value.nil? new_ec.set_mapping(key, nil) @@ -252,10 +273,11 @@ else debug("serlialize: generate context") debug {"=> context: #{inspect}"} ctx = Hash.ordered ctx['@language'] = default_language.to_s if default_language + ctx['@vocab'] = vocab.to_s if vocab # Mappings mappings.keys.sort{|a, b| a.to_s <=> b.to_s}.each do |k| next unless term_valid?(k.to_s) debug {"=> mappings[#{k}] => #{mappings[k]}"} @@ -451,19 +473,20 @@ # @see http://json-ld.org/spec/latest/json-ld-api/#iri-expansion def expand_iri(iri, options = {}) return iri unless iri.is_a?(String) prefix, suffix = iri.split(':', 2) return mapping(iri) if mapping(iri) # If it's an exact match - debug("expand_iri") {"prefix: #{prefix.inspect}, suffix: #{suffix.inspect}"} unless options[:quiet] + debug("expand_iri") {"prefix: #{prefix.inspect}, suffix: #{suffix.inspect}, vocab: #{vocab.inspect}"} unless options[:quiet] base = self.base unless [:predicate, :datatype].include?(options[:position]) prefix = prefix.to_s case when prefix == '_' && suffix then bnode(suffix) when iri.to_s[0,1] == "@" then iri when suffix.to_s[0,2] == '//' then uri(iri) when mappings.has_key?(prefix) then uri(mappings[prefix] + suffix.to_s) when base then base.join(iri) + when vocab then uri("#{vocab}#{iri}") else # Otherwise, it must be an absolute IRI u = uri(iri) u if u.absolute? || [:subject, :object].include?(options[:position]) end @@ -533,14 +556,24 @@ # Find terms having the greatest term match value least_distance = term_map.values.max terms = term_map.keys.select {|t| term_map[t] == least_distance} - # If the list of found terms is empty, append a compact IRI for - # each term which is a prefix of iri which does not have - # @type coercion, @container coercion or @language coercion rules - # along with the iri itself. + # If terms is empty, and the active context has a @vocab which is a + # prefix of iri where the resulting relative IRI is not a term in the + # active context. The resulting relative IRI is the unmatched part of iri. + if vocab && terms.empty? && iri.to_s.index(vocab) == 0 + terms << iri.to_s.sub(vocab, '') + debug("vocab") {"vocab: #{vocab}, rel: #{terms.first}"} + end + + # If terms is empty, add a compact IRI representation of iri for each + # term in the active context which maps to an IRI which is a prefix for + # iri where the resulting compact IRI is not a term in the active + # context. The resulting compact IRI is the term associated with the + # partially matched IRI in the active context concatenated with a colon + # (:) character and the unmatched part of iri. if terms.empty? debug("curies") {"mappings: #{mappings.inspect}"} curies = mappings.keys.map do |k| debug("curies[#{k}]") {"#{mapping(k).inspect}"} #debug("curies[#{k}]") {"#{(mapping(k).to_s.length > 0).inspect}, #{iri.to_s.index(mapping(k).to_s)}"} @@ -564,33 +597,35 @@ coerce(curie).nil? && language(curie) == default_language end debug("curies") {"selected #{terms.inspect}"} + end - # If we still don't have any terms and we're using standard_prefixes, - # try those, and add to mapping - if terms.empty? && @options[:standard_prefixes] - terms = RDF::Vocabulary. - select {|v| iri.index(v.to_uri.to_s) == 0}. - map do |v| - prefix = v.__name__.to_s.split('::').last.downcase - set_mapping(prefix, v.to_uri.to_s) - iri.sub(v.to_uri.to_s, "#{prefix}:").sub(/:$/, '') - end - debug("curies") {"using standard prefies: #{terms.inspect}"} - end + # If we still don't have any terms and we're using standard_prefixes, + # try those, and add to mapping + if terms.empty? && @options[:standard_prefixes] + terms = RDF::Vocabulary. + select {|v| iri.index(v.to_uri.to_s) == 0}. + map do |v| + prefix = v.__name__.to_s.split('::').last.downcase + set_mapping(prefix, v.to_uri.to_s) + iri.sub(v.to_uri.to_s, "#{prefix}:").sub(/:$/, '') + end + debug("curies") {"using standard prefies: #{terms.inspect}"} + end + if terms.empty? # If there is a mapping from the complete IRI to null, return null, # otherwise, return the complete IRI. if mappings.has_key?(iri.to_s) && !mapping(iri) debug("iri") {"use nil IRI mapping"} terms << nil else terms << iri.to_s end end - + # Get the first term based on distance and lexecographical order # Prefer terms that don't have @container @set over other terms, unless as set is true terms = terms.sort do |a, b| debug("term sort") {"c(a): #{container(a).inspect}, c(b): #{container(b)}"} if a.to_s.length == b.to_s.length