@@ -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
@@ -79,29 +81,26 @@ class API
7981 # Rename bnodes as part of expansion, or keep them the same.
8082 # @option options [Boolean] :unique_bnodes (false)
8183 # Use unique bnode identifiers, defaults to using the identifier which the node was originally initialized with (if any).
82- # @option options [Boolean] :simple_compact_iris (false)
83- # When compacting IRIs, do not use terms with expanded term definitions
8484 # @option options [Symbol] :adapter used with MultiJson
8585 # @option options [Boolean] :validate Validate input, if a string or readable object.
8686 # @yield [api]
8787 # @yieldparam [API]
8888 # @raise [JsonLdError]
8989 def initialize ( input , context , options = { } , &block )
9090 @options = {
91- compactArrays : true ,
92- rename_bnodes : true ,
93- documentLoader : self . class . method ( :documentLoader )
94- }
95- @options = @options . merge ( options )
91+ compactArrays : true ,
92+ rename_bnodes : true ,
93+ documentLoader : self . class . method ( :documentLoader )
94+ } . merge ( options )
9695 @namer = options [ :unique_bnodes ] ? BlankNodeUniqer . new : ( @options [ :rename_bnodes ] ? BlankNodeNamer . new ( "b" ) : BlankNodeMapper . new )
9796
9897 # For context via Link header
99- context_ref = nil
98+ remote_base , context_ref = nil , nil
10099
101100 @value = case input
102101 when Array , Hash then input . dup
103102 when IO , StringIO
104- @options = { base : input . base_uri } . merge! ( @options ) if input . respond_to? ( :base_uri )
103+ @options = { base : input . base_uri } . merge ( @options ) if input . respond_to? ( :base_uri )
105104
106105 # if input impelements #links, attempt to get a contextUrl from that link
107106 content_type = input . respond_to? ( :content_type ) ? input . content_type : "application/json"
@@ -116,8 +115,9 @@ def initialize(input, context, options = {}, &block)
116115 when String
117116 remote_doc = @options [ :documentLoader ] . call ( input , @options )
118117
119- @options = { base : remote_doc . documentUrl } . merge! ( @options )
118+ remote_base = remote_doc . documentUrl
120119 context_ref = remote_doc . contextUrl
120+ @options = { base : remote_doc . documentUrl } . merge ( @options ) unless @options [ :no_default_base ]
121121
122122 case remote_doc . document
123123 when String
@@ -128,9 +128,6 @@ def initialize(input, context, options = {}, &block)
128128 end
129129 end
130130
131- # Update calling context :base option, if not defined
132- options [ :base ] ||= @options [ :base ] if @options [ :base ]
133-
134131 # If not provided, first use context from document, or from a Link header
135132 context ||= ( @value [ '@context' ] if @value . is_a? ( Hash ) ) || context_ref
136133 @context = Context . parse ( context || { } , @options )
@@ -160,25 +157,38 @@ def initialize(input, context, options = {}, &block)
160157 # @param [Hash{Symbol => Object}] options
161158 # @option options (see #initialize)
162159 # @raise [JsonLdError]
163- # @yield jsonld
160+ # @yield jsonld, base_iri
164161 # @yieldparam [Array<Hash>] jsonld
165162 # The expanded JSON-LD document
163+ # @yieldparam [RDF::URI] base_iri
164+ # The document base as determined during expansion
166165 # @yieldreturn [Object] returned object
167166 # @return [Object, Array<Hash>]
168167 # If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
169168 # @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 ) )
169+ def self . expand ( input , options = { } , &block )
170+ result , doc_base = nil
171+ API . new ( input , options [ :expandContext ] , options ) do
172+ result = self . expand ( self . value , nil , self . context , ordered : options . fetch ( :ordered , true ) )
173+ doc_base = @options [ :base ]
174174 end
175175
176176 # 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.
177177 result = result [ '@graph' ] if result . is_a? ( Hash ) && result . keys == %w( @graph )
178178
179179 # Finally, if element is a JSON object, it is wrapped into an array.
180180 result = [ result ] . compact unless result . is_a? ( Array )
181- block_given? ? yield ( result ) : result
181+
182+ if block_given?
183+ case block . arity
184+ when 1 then yield ( result )
185+ when 2 then yield ( result , doc_base )
186+ else
187+ raise "Unexpected number of yield parameters to expand"
188+ end
189+ else
190+ result
191+ end
182192 end
183193
184194 ##
@@ -204,13 +214,17 @@ def self.expand(input, options = {})
204214 # @raise [JsonLdError]
205215 # @see http://json-ld.org/spec/latest/json-ld-api/#compaction-algorithm
206216 def self . compact ( input , context , options = { } )
207- expanded = result = nil
217+ result = nil
218+ options = { compactToRelative : true } . merge ( options )
208219
209220 # 1) Perform the Expansion Algorithm on the JSON-LD input.
210221 # This removes any existing context to allow the given context to be cleanly applied.
211- expanded_input = options [ :expanded ] ? input : API . expand ( input , options )
222+ expanded_input = options [ :expanded ] ? input : API . expand ( input , options ) do |result , base_iri |
223+ options [ :base ] ||= base_iri if options [ :compactToRelative ]
224+ result
225+ end
212226
213- API . new ( expanded_input , context , options ) do
227+ API . new ( expanded_input , context , options . merge ( no_default_base : true ) ) do
214228 log_debug ( ".compact" ) { "expanded input: #{ expanded_input . to_json ( JSON_STATE ) rescue 'malformed json' } " }
215229 result = compact ( value )
216230
@@ -246,12 +260,16 @@ def self.compact(input, context, options = {})
246260 # @see http://json-ld.org/spec/latest/json-ld-api/#framing-algorithm
247261 def self . flatten ( input , context , options = { } )
248262 flattened = [ ]
263+ options = { compactToRelative : true } . merge ( options )
249264
250265 # Expand input to simplify processing
251- expanded_input = options [ :expanded ] ? input : API . expand ( input , options )
266+ expanded_input = options [ :expanded ] ? input : API . expand ( input , options ) do |result , base_iri |
267+ options [ :base ] ||= base_iri if options [ :compactToRelative ]
268+ result
269+ end
252270
253271 # Initialize input using
254- API . new ( expanded_input , context , options ) do
272+ API . new ( expanded_input , context , options . merge ( no_default_base : true ) ) do
255273 log_debug ( ".flatten" ) { "expanded input: #{ value . to_json ( JSON_STATE ) rescue 'malformed json' } " }
256274
257275 # Initialize node map to a JSON object consisting of a single member whose key is @default and whose value is an empty JSON object.
@@ -316,13 +334,14 @@ def self.frame(input, frame, options = {})
316334 options = {
317335 base : ( input if input . is_a? ( String ) ) ,
318336 compactArrays : true ,
337+ compactToRelative : true ,
319338 embed : '@last' ,
320339 explicit : false ,
321340 requireAll : true ,
322341 omitDefault : false ,
323342 pruneBlankNodeIdentifiers : true ,
324343 documentLoader : method ( :documentLoader )
325- } . merge! ( options )
344+ } . merge ( options )
326345
327346 framing_state = {
328347 graphMap : { } ,
@@ -344,13 +363,16 @@ def self.frame(input, frame, options = {})
344363 end
345364
346365 # Expand input to simplify processing
347- expanded_input = options [ :expanded ] ? input : API . expand ( input , options )
366+ expanded_input = options [ :expanded ] ? input : API . expand ( input , options ) do |result , base_iri |
367+ options [ :base ] ||= base_iri if options [ :compactToRelative ]
368+ result
369+ end
348370
349371 # Expand frame to simplify processing
350372 expanded_frame = API . expand ( frame , options . merge ( processingMode : "json-ld-1.1-expand-frame" ) )
351373
352374 # Initialize input using frame as context
353- API . new ( expanded_input , nil , options ) do
375+ API . new ( expanded_input , nil , options . merge ( no_default_base : true ) ) do
354376 log_debug ( ".frame" ) { "expanded frame: #{ expanded_frame . to_json ( JSON_STATE ) rescue 'malformed json' } " }
355377
356378 # Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
0 commit comments