@@ -63,6 +63,8 @@ class API
6363 # The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context. If not specified, and a base IRI is found from `input`, options[:base] will be modified with this value.
6464 # @option options [Boolean] :compactArrays (true)
6565 # If set to `true`, the JSON-LD processor replaces arrays with just one element with that element during compaction. If set to `false`, all arrays will remain arrays even if they have just one element.
66+ # @option options [Boolean] :compactToRelative (true)
67+ # Creates document relative IRIs when compacting, if `true`, otherwise leaves expanded.
6668 # @option options [Proc] :documentLoader
6769 # The callback of the loader to be used to retrieve remote documents and contexts. If specified, it must be used to retrieve remote documents and contexts; otherwise, if not specified, the processor's built-in loader must be used. See {documentLoader} for the method signature.
6870 # @option options [String, #read, Hash, Array, JSON::LD::Context] :expandContext
@@ -88,11 +90,11 @@ class API
8890 # @raise [JsonLdError]
8991 def initialize ( input , context , options = { } , &block )
9092 @options = {
91- compactArrays : true ,
92- rename_bnodes : true ,
93- documentLoader : self . class . method ( :documentLoader )
94- }
95- @options = @options . merge ( options )
93+ compactArrays : true ,
94+ rename_bnodes : true ,
95+ documentLoader : self . class . method ( :documentLoader ) ,
96+ compactToRelative : true
97+ } . merge ( options )
9698 @namer = options [ :unique_bnodes ] ? BlankNodeUniqer . new : ( @options [ :rename_bnodes ] ? BlankNodeNamer . new ( "b" ) : BlankNodeMapper . new )
9799
98100 # For context via Link header
@@ -101,7 +103,7 @@ def initialize(input, context, options = {}, &block)
101103 @value = case input
102104 when Array , Hash then input . dup
103105 when IO , StringIO
104- @options = { base : input . base_uri } . merge! ( @options ) if input . respond_to? ( :base_uri )
106+ @options = { base : input . base_uri } . merge ( @options ) if input . respond_to? ( :base_uri )
105107
106108 # if input impelements #links, attempt to get a contextUrl from that link
107109 content_type = input . respond_to? ( :content_type ) ? input . content_type : "application/json"
@@ -116,7 +118,7 @@ def initialize(input, context, options = {}, &block)
116118 when String
117119 remote_doc = @options [ :documentLoader ] . call ( input , @options )
118120
119- @options = { base : remote_doc . documentUrl } . merge! ( @options )
121+ @options = { base : remote_doc . documentUrl } . merge ( @options )
120122 context_ref = remote_doc . contextUrl
121123
122124 case remote_doc . document
@@ -128,9 +130,6 @@ def initialize(input, context, options = {}, &block)
128130 end
129131 end
130132
131- # Update calling context :base option, if not defined
132- options [ :base ] ||= @options [ :base ] if @options [ :base ]
133-
134133 # If not provided, first use context from document, or from a Link header
135134 context ||= ( @value [ '@context' ] if @value . is_a? ( Hash ) ) || context_ref
136135 @context = Context . parse ( context || { } , @options )
@@ -139,6 +138,13 @@ def initialize(input, context, options = {}, &block)
139138 @options [ :processingMode ] ||= @context . processingMode || "json-ld-1.0"
140139 @options [ :validate ] ||= %w( json-ld-1.0 json-ld-1.1 ) . include? ( @options [ :processingMode ] )
141140
141+ # If, after processing, the context does not have a _base IRI_, and the _compactToRelative_ option is set to true or processingMode is json-ld-1.0, set _base IRI_ in the active context to either the _base_ option from the API, if set, or the IRI of the currently being processed document.
142+ if !@context . base && @options [ :base ]
143+ doc_base = RDF ::URI ( @options [ :base ] ) . dup
144+ doc_base . canonicalize! if options [ :canonicalize ]
145+ @context . base = doc_base
146+ end
147+
142148 if block_given?
143149 case block . arity
144150 when 0 , -1 then instance_eval ( &block )
@@ -160,25 +166,38 @@ def initialize(input, context, options = {}, &block)
160166 # @param [Hash{Symbol => Object}] options
161167 # @option options (see #initialize)
162168 # @raise [JsonLdError]
163- # @yield jsonld
169+ # @yield jsonld, base_iri
164170 # @yieldparam [Array<Hash>] jsonld
165171 # The expanded JSON-LD document
172+ # @yieldparam [RDF::URI] base_iri
173+ # The document base as determined during expansion
166174 # @yieldreturn [Object] returned object
167175 # @return [Object, Array<Hash>]
168176 # If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
169177 # @see http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm
170- def self . expand ( input , options = { } )
171- result = nil
172- API . new ( input , options [ :expandContext ] , options ) do |api |
173- result = api . expand ( api . value , nil , api . context , ordered : options . fetch ( :ordered , true ) )
178+ def self . expand ( input , options = { } , &block )
179+ result , doc_base = nil
180+ API . new ( input , options [ :expandContext ] , options ) do
181+ result = self . expand ( self . value , nil , self . context , ordered : options . fetch ( :ordered , true ) )
182+ doc_base = @options [ :base ]
174183 end
175184
176185 # If, after the algorithm outlined above is run, the resulting element is an JSON object with just a @graph property, element is set to the value of @graph's value.
177186 result = result [ '@graph' ] if result . is_a? ( Hash ) && result . keys == %w( @graph )
178187
179188 # Finally, if element is a JSON object, it is wrapped into an array.
180189 result = [ result ] . compact unless result . is_a? ( Array )
181- block_given? ? yield ( result ) : result
190+
191+ if block_given?
192+ case block . arity
193+ when 1 then yield ( result )
194+ when 2 then yield ( result , doc_base )
195+ else
196+ raise "Unexpected number of yield parameters to expand"
197+ end
198+ else
199+ result
200+ end
182201 end
183202
184203 ##
@@ -204,12 +223,16 @@ def self.expand(input, options = {})
204223 # @raise [JsonLdError]
205224 # @see http://json-ld.org/spec/latest/json-ld-api/#compaction-algorithm
206225 def self . compact ( input , context , options = { } )
207- expanded = result = nil
226+ result = nil
208227
209228 # 1) Perform the Expansion Algorithm on the JSON-LD input.
210229 # This removes any existing context to allow the given context to be cleanly applied.
211- expanded_input = options [ :expanded ] ? input : API . expand ( input , options )
230+ expanded_input = options [ :expanded ] ? input : API . expand ( input , options ) do |result , base_iri |
231+ options [ :base ] ||= base_iri
232+ result
233+ end
212234
235+ #require 'byebug'; byebug
213236 API . new ( expanded_input , context , options ) do
214237 log_debug ( ".compact" ) { "expanded input: #{ expanded_input . to_json ( JSON_STATE ) rescue 'malformed json' } " }
215238 result = compact ( value )
@@ -248,7 +271,10 @@ def self.flatten(input, context, options = {})
248271 flattened = [ ]
249272
250273 # Expand input to simplify processing
251- expanded_input = options [ :expanded ] ? input : API . expand ( input , options )
274+ expanded_input = options [ :expanded ] ? input : API . expand ( input , options ) do |result , base_iri |
275+ options [ :base ] ||= base_iri
276+ result
277+ end
252278
253279 # Initialize input using
254280 API . new ( expanded_input , context , options ) do
@@ -322,7 +348,7 @@ def self.frame(input, frame, options = {})
322348 omitDefault : false ,
323349 pruneBlankNodeIdentifiers : true ,
324350 documentLoader : method ( :documentLoader )
325- } . merge! ( options )
351+ } . merge ( options )
326352
327353 framing_state = {
328354 graphMap : { } ,
@@ -344,7 +370,10 @@ def self.frame(input, frame, options = {})
344370 end
345371
346372 # Expand input to simplify processing
347- expanded_input = options [ :expanded ] ? input : API . expand ( input , options )
373+ expanded_input = options [ :expanded ] ? input : API . expand ( input , options ) do |result , base_iri |
374+ options [ :base ] ||= base_iri
375+ result
376+ end
348377
349378 # Expand frame to simplify processing
350379 expanded_frame = API . expand ( frame , options . merge ( processingMode : "json-ld-1.1-expand-frame" ) )
0 commit comments