json-ld 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTQ0NzdhNmE4YWFlYzBjYzVkOWVlOGJjNGJjNDdiY2ZmN2RhYzdhMg==
4
+ ZTJhN2RiZDA5MjA5ZTUxZGJhZTZhMTI5NGNkOTM3YjA0ZjkxYzlkYQ==
5
5
  data.tar.gz: !binary |-
6
- NWNkMTAxNWU0NGQxOGQ5NjlmMTFkZmFiZDY3MWRiZDFkMWE2YWMwNQ==
6
+ ZTRlZDUwNDA0Nzc5MWIyMDg5YmQ2MTQzY2JlYzU2YWYxMzAyNDBjMQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZGExMzc0OThkODZlNDJiOWI2ZDRmOGU3Y2RkNTY5YWVjNzcyZmM1N2M3NzY0
10
- OGM5YzcyYzM5NzUxMTNiZDE1NDVhYmZhMzNhN2ZkYzkwZWY5YTJkMDA2NjRm
11
- ZjA0M2ViODM0YTk0MTQzMDk2MmE2MTg2NzQ1Y2MyNzc2N2Q3NjA=
9
+ YmY4M2Q3NjEyYmNkOTBlYjdiYzAzOWI1ZThhMDQ2ZjA3MWQ0ZTFiZDFkOGMx
10
+ NzAyYzRhYjgwNGMyMjMxNTNjZjQ2N2I2NzQyZTRlMGQzMThjZmY1Mjc3ZTk4
11
+ OTYyNDYzMmY1Yzc5ODFlNGY3MDY1OGQxOTY5NGRhZTdmY2FiOGQ=
12
12
  data.tar.gz: !binary |-
13
- MzljNjBiZDdlYWZjZjM4YmI5MmZhZDQ0ZTI3N2EyYjU2MWIzNjBlZTk5ZjI1
14
- YjM2YzkyMmEyNDU2NmEyNTMwNzE3NGE4YTczMTMzYWE2MDA3MzI5ZDhkNWEw
15
- Y2MwMjllM2FjNTRiYTRhMzRlMGVjYWIwYzg4Zjc0NzJiNGMxZmU=
13
+ ZjBlNTM5YTJkY2YxY2UyOGMxMmRlMjRiODk5Yjk0M2EyM2NiNzlkODBjNWY2
14
+ ODg1MzAwOTUxODc0OTI3NDI3NzA0ZDk0ZGEzNTE3YjVmZWNhMzk2ZWY1MDAw
15
+ M2M5NmFjM2M0OGUzMjA1Zjk2Zjg4ZDhiOTY0YWM2OTJlNWNiZjg=
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # JSON-LD reader/writer
2
2
 
3
- [JSON-LD][] reader/writer for [RDF.rb][RDF.rb] and fully conforming [JSON-LD][] processor.
3
+ [JSON-LD][] reader/writer for [RDF.rb][RDF.rb] and fully conforming [JSON-LD API][] processor. Additionally this gem implements [JSON-LD Framing][].
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/json-ld.png)](http://badge.fury.io/rb/json-ld)
6
6
  [![Build Status](https://secure.travis-ci.org/ruby-rdf/json-ld.png?branch=master)](http://travis-ci.org/ruby-rdf/json-ld)
@@ -220,13 +220,12 @@ Install with `gem install json-ld`
220
220
  Full documentation available on [RubyDoc](http://rubydoc.info/gems/json-ld/file/README.md)
221
221
 
222
222
  ## Differences from [JSON-LD API][]
223
- The specified JSON-LD API is based on a WebIDL definition intended for use within the browser.
223
+ The specified JSON-LD API is based on a WebIDL definition implementing [Promises][] intended for use within a browser.
224
224
  This version implements a more Ruby-like variation of this API without the use
225
- of futures and callback arguments, preferring Ruby blocks. All API methods
226
- execute synchronously, so that the return from a method can be used as well as a block.
225
+ of promises or callback arguments, preferring Ruby blocks. All API methods
226
+ execute synchronously, so that the return from a method can typically be used as well as a block.
227
227
 
228
- Note, the API method signatures differed in versions before 1.0, in that they also had
229
- a callback parameter.
228
+ Note, the API method signatures differed in versions before 1.0, in that they also had a callback parameter. And 1.0.6 has some other minor method signature differences than previous versions. This should be the only exception to the use of semantic versioning.
230
229
 
231
230
  ### Principal Classes
232
231
  * {JSON::LD}
@@ -288,5 +287,7 @@ see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file.
288
287
  [PDD]: http://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html
289
288
  [RDF.rb]: http://rubygems.org/gems/rdf
290
289
  [Backports]: http://rubygems.org/gems/backports
291
- [JSON-LD]: http://json-ld.org/spec/latest/
292
- [JSON-LD API]: http://json-ld.org/spec/latest/json-ld-api/
290
+ [JSON-LD]: http://www.w3.org/TR/json-ld/ "JSON-LD 1.0"
291
+ [JSON-LD API]: http://www.w3.org/TR/json-ld-api/ "JSON-LD 1.0 Processing Algorithms and API"
292
+ [JSON-LD Framing]: http://json-ld.org/spec/latest/json-ld-framing/ "JSON-LD Framing 1.0"
293
+ [Promises]: http://dom.spec.whatwg.org/#promises
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.5
1
+ 1.0.6
data/bin/jsonld CHANGED
@@ -26,7 +26,8 @@ def run(input, options)
26
26
  prefixes = {}
27
27
  start = Time.new
28
28
  if options[:expand]
29
- output = JSON::LD::API.expand(input, options[:context], options)
29
+ options = options.merge(:expandContext => options.delete(:context)) if options.has_key?(:context)
30
+ output = JSON::LD::API.expand(input, options)
30
31
  secs = Time.new - start
31
32
  options[:output].puts output.to_json(JSON::LD::JSON_STATE)
32
33
  STDERR.puts "Expanded in #{secs} seconds." unless options[:quiet]
@@ -88,48 +88,49 @@ module JSON
88
88
  def self.debug?; @debug; end
89
89
  def self.debug=(value); @debug = value; end
90
90
 
91
- class ProcessingError < Exception
92
- class CompactionToListOfLists < ProcessingError; end
93
- class Conflict < ProcessingError; end
94
- class ConflictingIndexes < ProcessingError; end
95
- class InvalidIdValue < ProcessingError; end
96
- class InvalidIndexValue < ProcessingError; end
97
- class InvalidLanguageMapValue < ProcessingError; end
98
- class InvalidLanguageTaggedString < ProcessingError; end
99
- class InvalidLanguageTaggedValue < ProcessingError; end
100
- class InvalidReversePropertyMap < ProcessingError; end
101
- class InvalidReversePropertyValue < ProcessingError; end
102
- class InvalidReverseValue < ProcessingError; end
103
- class InvalidSetOrListObject < ProcessingError; end
104
- class InvalidTypedValue < ProcessingError; end
105
- class InvalidTypeValue < ProcessingError; end
106
- class InvalidValueObject < ProcessingError; end
107
- class InvalidValueObjectValue < ProcessingError; end
108
- class LanguageMap < ProcessingError; end
109
- class ListOfLists < ProcessingError; end
110
- class LoadingDocumentFailed < ProcessingError; end
111
- class Lossy < ProcessingError; end
112
- end
113
-
114
- class InvalidContext < Exception
115
- class CollidingKeywords < InvalidContext; end
116
- class CyclicIRIMapping < InvalidContext; end
117
- class InvalidBaseIRI < InvalidContext; end
118
- class InvalidBaseIRI < InvalidContext; end
119
- class InvalidContainerMapping < InvalidContext; end
120
- class InvalidDefaultLanguage < InvalidContext; end
121
- class InvalidIRIMapping < InvalidContext; end
122
- class InvalidKeywordAlias < InvalidContext; end
123
- class InvalidLanguageMapping < InvalidContext; end
124
- class InvalidLocalContext < InvalidContext; end
125
- class InvalidRemoteContext < InvalidContext; end
126
- class InvalidReverseProperty < InvalidContext; end
127
- class InvalidTermDefinition < InvalidContext; end
128
- class InvalidTypeMapping < InvalidContext; end
129
- class InvalidVocabMapping < InvalidContext; end
130
- class KeywordRedefinition < InvalidContext; end
131
- class LoadingRemoteContextFailed < InvalidContext; end
132
- class RecursiveContextInclusion < InvalidContext; end
91
+ class JsonLdError < Exception
92
+ def to_s
93
+ "#{self.class.instance_variable_get :@code}: #{super}"
94
+ end
95
+ def code
96
+ self.class.instance_variable_get :@code
97
+ end
98
+
99
+ class LoadingDocumentFailed < JsonLdError; @code = "loading document failed"; end
100
+ class ListOfLists < JsonLdError; @code = "list of lists"; end
101
+ class InvalidIndexValue < JsonLdError; @code = "invalid @index value"; end
102
+ class ConflictingIndexes < JsonLdError; @code = "conflicting indexes"; end
103
+ class InvalidIdValue < JsonLdError; @code = "invalid @id value"; end
104
+ class InvalidLocalContext < JsonLdError; @code = "invalid local context"; end
105
+ class MultipleContextLinkHeaders < JsonLdError; @code = "multiple context link headers"; end
106
+ class LoadingRemoteContextFailed < JsonLdError; @code = "loading remote context failed"; end
107
+ class InvalidRemoteContext < JsonLdError; @code = "invalid remote context"; end
108
+ class RecursiveContextInclusion < JsonLdError; @code = "recursive context inclusion"; end
109
+ class InvalidBaseIRI < JsonLdError; @code = "invalid base IRI"; end
110
+ class InvalidVocabMapping < JsonLdError; @code = "invalid vocab mapping"; end
111
+ class InvalidDefaultLanguage < JsonLdError; @code = "invalid default language"; end
112
+ class KeywordRedefinition < JsonLdError; @code = "keyword redefinition"; end
113
+ class InvalidTermDefinition < JsonLdError; @code = "invalid term definition"; end
114
+ class InvalidReverseProperty < JsonLdError; @code = "invalid reverse property"; end
115
+ class InvalidIRIMapping < JsonLdError; @code = "invalid IRI mapping"; end
116
+ class CyclicIRIMapping < JsonLdError; @code = "cyclic IRI mapping"; end
117
+ class InvalidKeywordAlias < JsonLdError; @code = "invalid keyword alias"; end
118
+ class InvalidTypeMapping < JsonLdError; @code = "invalid type mapping"; end
119
+ class InvalidLanguageMapping < JsonLdError; @code = "invalid language mapping"; end
120
+ class CollidingKeywords < JsonLdError; @code = "colliding keywords"; end
121
+ class InvalidContainerMapping < JsonLdError; @code = "invalid container mapping"; end
122
+ class InvalidTypeValue < JsonLdError; @code = "invalid type value"; end
123
+ class InvalidValueObject < JsonLdError; @code = "invalid value object"; end
124
+ class InvalidValueObjectValue < JsonLdError; @code = "invalid value object value"; end
125
+ class InvalidLanguageTaggedString < JsonLdError; @code = "invalid language-tagged string"; end
126
+ class InvalidLanguageTaggedValue < JsonLdError; @code = "invalid language-tagged value"; end
127
+ class InvalidTypedValue < JsonLdError; @code = "invalid typed value"; end
128
+ class InvalidSetOrListObject < JsonLdError; @code = "invalid set or list object"; end
129
+ class InvalidLanguageMapValue < JsonLdError; @code = "invalid language map value"; end
130
+ class CompactionToListOfLists < JsonLdError; @code = "compaction to list of lists"; end
131
+ class InvalidReversePropertyMap < JsonLdError; @code = "invalid reverse property map"; end
132
+ class InvalidReverseValue < JsonLdError; @code = "invalid @reverse value"; end
133
+ class InvalidReversePropertyValue < JsonLdError; @code = "invalid reverse property value"; end
133
134
  end
134
135
 
135
136
  class InvalidFrame < Exception
@@ -48,39 +48,56 @@ module JSON::LD
48
48
  # Initialize the API, reading in any document and setting global options
49
49
  #
50
50
  # @param [String, #read, Hash, Array] input
51
- # @param [String, #read,, Hash, Array, JSON::LD::Context] context
51
+ # @param [String, #read, Hash, Array, JSON::LD::Context] context
52
52
  # An external context to use additionally to the context embedded in input when expanding the input.
53
53
  # @param [Hash{Symbol => Object}] options
54
- # @option options [Boolean] :base
54
+ # @option options [String, #to_s] :base
55
55
  # 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.
56
+ # If not specified, and a base IRI is found from `input`, options[:base] will be modified with this value.
56
57
  # @option options [Boolean] :compactArrays (true)
57
58
  # 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.
59
+ # @option options [Proc] :documentLoader
60
+ # 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.
61
+ # @option options [String, #read, Hash, Array, JSON::LD::Context] :expandContext
62
+ # A context that is used to initialize the active context when expanding a document.
58
63
  # @option options [Boolean, String, RDF::URI] :flatten
59
64
  # If set to a value that is not `false`, the JSON-LD processor must modify the output of the Compaction Algorithm or the Expansion Algorithm by coalescing all properties associated with each subject via the Flattening Algorithm. The value of `flatten must` be either an _IRI_ value representing the name of the graph to flatten, or `true`. If the value is `true`, then the first graph encountered in the input document is selected and flattened.
60
- # @option options [Boolean] :optimize (false)
61
- # If set to `true`, the JSON-LD processor is allowed to optimize the output of the Compaction Algorithm to produce even compacter representations. The algorithm for compaction optimization is beyond the scope of this specification and thus not defined. Consequently, different implementations *MAY* implement different optimization algorithms.
62
- # (Presently, this is a noop).
65
+ # @option options [String] :processingMode ("json-ld-1.0")
66
+ # If set to "json-ld-1.0", the JSON-LD processor must produce exactly the same results as the algorithms defined in this specification. If set to another value, the JSON-LD processor is allowed to extend or modify the algorithms defined in this specification to enable application-specific optimizations. The definition of such optimizations is beyond the scope of this specification and thus not defined. Consequently, different implementations may implement different optimizations. Developers must not define modes beginning with json-ld as they are reserved for future versions of this specification.
67
+ # @option options [String] :produceGeneralizedRdf (false)
68
+ # Unless the produce generalized RDF flag is set to true, RDF triples containing a blank node predicate are excluded from output.
63
69
  # @option options [Boolean] :useNativeTypes (true)
64
70
  # If set to `true`, the JSON-LD processor will use native datatypes for expression xsd:integer, xsd:boolean, and xsd:double values, otherwise, it will use the expanded form.
65
- # @option options [Boolean] :useRdfType (false)
66
- # If set to `true`, the JSON-LD processor will try to convert datatyped literals to JSON native types instead of using the expanded object form when converting from RDF. `xsd:boolean` values will be converted to `true` or `false`. `xsd:integer` and `xsd:double` values will be converted to JSON numbers.
67
71
  # @option options [Boolean] :rename_bnodes (true)
68
72
  # Rename bnodes as part of expansion, or keep them the same.
69
73
  # @yield [api]
70
74
  # @yieldparam [API]
71
75
  def initialize(input, context, options = {}, &block)
72
76
  @options = {:compactArrays => true}.merge(options)
73
- options = {:rename_bnodes => true}.merge(options)
77
+ @options[:validate] = true if @options[:processingMode] == "json-ld-1.0"
78
+ @options[:documentLoader] ||= self.class.method(:documentLoader)
79
+ options[:rename_bnodes] ||= true
74
80
  @namer = options[:rename_bnodes] ? BlankNodeNamer.new("b") : BlankNodeMapper.new
81
+ content_type = nil
75
82
  @value = case input
76
83
  when Array, Hash then input.dup
77
- when IO, StringIO then JSON.parse(input.read)
84
+ when IO, StringIO
85
+ @options = {:base => input.base_uri}.merge(@options) if input.respond_to?(:base_uri)
86
+ JSON.parse(input.read)
78
87
  when String
79
- content = nil
80
- @options = {:base => input}.merge(@options)
81
- RDF::Util::File.open_file(input, OPEN_OPTS) {|f| content = JSON.parse(f.read)}
82
- content
88
+ remote_doc = @options[:documentLoader].call(input, @options)
89
+
90
+ @options = {:base => remote_doc.documentUrl}.merge(@options)
91
+ context = context ? [context, remote_doc.contextUrl].compact : remote_doc.contextUrl
92
+
93
+ case remote_doc.document
94
+ when String then JSON.parse(remote_doc.document)
95
+ else remote_doc.document
96
+ end
83
97
  end
98
+
99
+ # Update calling context :base option, if not defined
100
+ options[:base] ||= @options[:base] if @options[:base]
84
101
  @context = Context.new(@options)
85
102
  @context = @context.parse(context) if context
86
103
 
@@ -100,20 +117,18 @@ module JSON::LD
100
117
  #
101
118
  # @param [String, #read, Hash, Array] input
102
119
  # The JSON-LD object to copy and perform the expansion upon.
103
- # @param [String, #read, Hash, Array, JSON::LD::Context] context
104
- # An external context to use additionally to the context embedded in input when expanding the input.
105
120
  # @param [Hash{Symbol => Object}] options
106
121
  # See options in {JSON::LD::API#initialize}
107
- # @raise [InvalidContext]
122
+ # @raise [JsonLdError]
108
123
  # @yield jsonld
109
124
  # @yieldparam [Array<Hash>] jsonld
110
125
  # The expanded JSON-LD document
111
126
  # @return [Array<Hash>]
112
127
  # The expanded JSON-LD document
113
128
  # @see http://json-ld.org/spec/latest/json-ld-api/#expansion-algorithm
114
- def self.expand(input, context = nil, options = {})
129
+ def self.expand(input, options = {})
115
130
  result = nil
116
- API.new(input, context, options) do |api|
131
+ API.new(input, options[:expandContext], options) do |api|
117
132
  result = api.expand(api.value, nil, api.context)
118
133
  end
119
134
 
@@ -147,14 +162,14 @@ module JSON::LD
147
162
  # The compacted JSON-LD document
148
163
  # @return [Hash]
149
164
  # The compacted JSON-LD document
150
- # @raise [InvalidContext, ProcessingError]
165
+ # @raise [JsonLdError]
151
166
  # @see http://json-ld.org/spec/latest/json-ld-api/#compaction-algorithm
152
167
  def self.compact(input, context, options = {})
153
168
  expanded = result = nil
154
169
 
155
170
  # 1) Perform the Expansion Algorithm on the JSON-LD input.
156
171
  # This removes any existing context to allow the given context to be cleanly applied.
157
- expanded = API.expand(input, nil, options.merge(:debug => nil))
172
+ expanded = API.expand(input, options)
158
173
 
159
174
  API.new(expanded, context, options) do
160
175
  debug(".compact") {"expanded input: #{expanded.to_json(JSON_STATE)}"}
@@ -195,10 +210,10 @@ module JSON::LD
195
210
  flattened = []
196
211
 
197
212
  # Expand input to simplify processing
198
- expanded_input = API.expand(input, nil, options)
213
+ expanded_input = API.expand(input, options)
199
214
 
200
215
  # Initialize input using frame as context
201
- API.new(expanded_input, nil, options) do
216
+ API.new(expanded_input, context, options) do
202
217
  debug(".flatten") {"expanded input: #{value.to_json(JSON_STATE)}"}
203
218
 
204
219
  # Initialize node map to a JSON object consisting of a single member whose key is @default and whose value is an empty JSON object.
@@ -221,7 +236,7 @@ module JSON::LD
221
236
 
222
237
  if context && !flattened.empty?
223
238
  # Otherwise, return the result of compacting flattened according the Compaction algorithm passing context ensuring that the compaction result uses the @graph keyword (or its alias) at the top-level, even if the context is empty or if there is only one element to put in the @graph array. This ensures that the returned document has a deterministic structure.
224
- compacted = compact(flattened, nil)
239
+ compacted = depth {compact(flattened, nil)}
225
240
  compacted = [compacted] unless compacted.is_a?(Array)
226
241
  kwgraph = self.context.compact_iri('@graph', :quiet => true)
227
242
  flattened = self.context.serialize.merge(kwgraph => compacted)
@@ -274,22 +289,25 @@ module JSON::LD
274
289
  framing_state[:embed] = options[:embed] if options.has_key?(:embed)
275
290
  framing_state[:explicit] = options[:explicit] if options.has_key?(:explicit)
276
291
  framing_state[:omitDefault] = options[:omitDefault] if options.has_key?(:omitDefault)
292
+ options[:documentLoader] ||= method(:documentLoader)
277
293
 
278
294
  # de-reference frame to create the framing object
279
295
  frame = case frame
280
296
  when Hash then frame.dup
281
297
  when IO, StringIO then JSON.parse(frame.read)
282
298
  when String
283
- content = nil
284
- RDF::Util::File.open_file(frame, OPEN_OPTS) {|f| content = JSON.parse(f.read)}
285
- content
299
+ remote_doc = options[:documentLoader].call(frame)
300
+ case remote_doc.document
301
+ when String then JSON.parse(remote_doc.document)
302
+ else remote_doc.document
303
+ end
286
304
  end
287
305
 
288
- # Expand frame to simplify processing
289
- expanded_frame = API.expand(frame)
290
-
291
306
  # Expand input to simplify processing
292
- expanded_input = API.expand(input)
307
+ expanded_input = API.expand(input, options)
308
+
309
+ # Expand frame to simplify processing
310
+ expanded_frame = API.expand(frame, options)
293
311
 
294
312
  # Initialize input using frame as context
295
313
  API.new(expanded_input, nil, options) do
@@ -334,25 +352,23 @@ module JSON::LD
334
352
  #
335
353
  # @param [String, #read, Hash, Array] input
336
354
  # The JSON-LD object to process when outputting statements.
337
- # @param [String, #read, Hash, Array, JSON::LD::Context] context
338
- # An external context to use additionally to the context embedded in input when expanding the input.
339
355
  # @param [{Symbol,String => Object}] options
340
356
  # See options in {JSON::LD::API#initialize}
341
357
  # Options passed to {JSON::LD::API.expand}
342
- # @option options [Boolean] :produceGeneralizedRDF (false)
358
+ # @option options [Boolean] :produceGeneralizedRdf (false)
343
359
  # If true, output will include statements having blank node predicates, otherwise they are dropped.
344
- # @raise [InvalidContext]
360
+ # @raise [JsonLdError]
345
361
  # @return [Array<RDF::Statement>] if no block given
346
362
  # @yield statement
347
363
  # @yieldparam [RDF::Statement] statement
348
- def self.toRDF(input, context = nil, options = {}, &block)
364
+ def self.toRDF(input, options = {}, &block)
349
365
  results = []
350
366
  results.extend(RDF::Enumerable)
351
367
 
352
368
  # Expand input to simplify processing
353
- expanded_input = API.expand(input, context, options)
369
+ expanded_input = API.expand(input, options)
354
370
 
355
- API.new(expanded_input, context, options) do
371
+ API.new(expanded_input, nil, options) do
356
372
  # 1) Perform the Expansion Algorithm on the JSON-LD input.
357
373
  # This removes any existing context to allow the given context to be cleanly applied.
358
374
  debug(".toRDF") {"expanded input: #{expanded_input.to_json(JSON_STATE)}"}
@@ -367,8 +383,29 @@ module JSON::LD
367
383
  node_map.each do |graph_name, graph|
368
384
  context = as_resource(graph_name) unless graph_name == '@default'
369
385
  debug(".toRDF") {"context: #{context ? context.to_ntriples : 'null'}"}
386
+ # Drop results for graphs which are named with relative IRIs
387
+ if graph_name.is_a?(RDF::URI) && !graph_name.absolute
388
+ debug(".toRDF") {"drop relative graph_name: #{statement.to_ntriples}"}
389
+ next
390
+ end
370
391
  graph_to_rdf(graph).each do |statement|
371
- next if statement.predicate.node? && !options[:produceGeneralizedRDF]
392
+ next if statement.predicate.node? && !options[:produceGeneralizedRdf]
393
+ # Drop results with relative IRIs
394
+ relative = statement.to_a.any? do |r|
395
+ case r
396
+ when RDF::URI
397
+ r.relative?
398
+ when RDF::Literal
399
+ r.has_datatype? && r.datatype.relative?
400
+ else
401
+ false
402
+ end
403
+ end
404
+ if relative
405
+ debug(".toRDF") {"drop statement with relative IRIs: #{statement.to_ntriples}"}
406
+ next
407
+ end
408
+
372
409
  statement.context = context if context
373
410
  if block_given?
374
411
  yield statement
@@ -395,7 +432,7 @@ module JSON::LD
395
432
  # @return [Array<Hash>]
396
433
  # The JSON-LD document in expanded form
397
434
  def self.fromRDF(input, options = {}, &block)
398
- options = {:useNativeTypes => true}.merge(options)
435
+ options = {:useNativeTypes => false}.merge(options)
399
436
  result = nil
400
437
 
401
438
  API.new(nil, nil, options) do |api|
@@ -405,6 +442,114 @@ module JSON::LD
405
442
  yield result if block_given?
406
443
  result
407
444
  end
445
+
446
+ ##
447
+ # Default document loader.
448
+ # @param [RDF::URI, String] url
449
+ # @param [Hash<Symbol => Object>] options
450
+ # @option options [Boolean] :validate
451
+ # Allow only appropriate content types
452
+ # @return [RemoteDocument] retrieved remote document and context information unless block given
453
+ # @yield remote_document
454
+ # @yieldparam [RemoteDocument] remote_document
455
+ # @raise [JsonLdError]
456
+ def self.documentLoader(url, options = {})
457
+ require 'net/http' unless defined?(Net::HTTP)
458
+ remote_document = nil
459
+ options[:headers] ||= OPEN_OPTS[:headers]
460
+
461
+ url = url.to_s[5..-1] if url.to_s.start_with?("file:")
462
+ case url.to_s
463
+ when /^http/
464
+ parsed_url = ::URI.parse(url.to_s)
465
+ until remote_document do
466
+ Net::HTTP::start(parsed_url.host, parsed_url.port) do |http|
467
+ request = Net::HTTP::Get.new(parsed_url.request_uri, options[:headers])
468
+ http.request(request) do |response|
469
+ case response
470
+ when Net::HTTPSuccess
471
+ # found object
472
+ content_type, ct_param = response.content_type.to_s.downcase.split(";")
473
+
474
+ # If the passed input is a DOMString representing the IRI of a remote document, dereference it. If the retrieved document's content type is neither application/json, nor application/ld+json, nor any other media type using a +json suffix as defined in [RFC6839], reject the promise passing an loading document failed error.
475
+ if content_type && options[:validate]
476
+ main, sub = content_type.split("/")
477
+ raise JSON::LD::JsonLdError::LoadingDocumentFailed, "content_type: #{content_type}" if
478
+ main != 'application' ||
479
+ sub !~ /^(.*\+)?json$/
480
+ end
481
+
482
+ remote_document = RemoteDocument.new(parsed_url.to_s, response.body)
483
+
484
+ # If the input has been retrieved, the response has an HTTP Link Header [RFC5988] using the http://www.w3.org/ns/json-ld#context link relation and a content type of application/json or any media type with a +json suffix as defined in [RFC6839] except application/ld+json, update the active context using the Context Processing algorithm, passing the context referenced in the HTTP Link Header as local context. The HTTP Link Header is ignored for documents served as application/ld+json If multiple HTTP Link Headers using the http://www.w3.org/ns/json-ld#context link relation are found, the promise is rejected with a JsonLdError whose code is set to multiple context link headers and processing is terminated.
485
+ unless content_type.start_with?("application/ld+json")
486
+ links = response["link"].to_s.
487
+ split(",").
488
+ map(&:strip).
489
+ select {|h| h =~ %r{rel=\"http://www.w3.org/ns/json-ld#context\"}}
490
+ case links.length
491
+ when 0 then #nothing to do
492
+ when 1
493
+ remote_document.contextUrl = links.first.match(/<([^>]*)>/) && $1
494
+ else
495
+ raise JSON::LD::JsonLdError::MultipleContextLinkHeaders,
496
+ "expected at most 1 Link header with rel=jsonld:context, got #{links.length}"
497
+ end
498
+ end
499
+
500
+ yield remote_document if block_given?
501
+ when Net::HTTPRedirection
502
+ # Follow redirection
503
+ parsed_url = ::URI.parse(response["Location"])
504
+ else
505
+ raise JSON::LD::JsonLdError::LoadingDocumentFailed, "<#{parsed_url}>: #{response.msg}(#{response.code})"
506
+ end
507
+ end
508
+ end
509
+ end
510
+ else
511
+ # Use regular open
512
+ RDF::Util::File.open_file(url, options) do |f|
513
+ remote_document = RemoteDocument.new(url, f.read)
514
+ content_type, ct_param = f.content_type.to_s.downcase.split(";") if f.respond_to?(:content_type)
515
+ if content_type && options[:validate]
516
+ main, sub = content_type.split("/")
517
+ raise JSON::LD::JsonLdError::LoadingDocumentFailed, "content_type: #{content_type}" if
518
+ main != 'application' ||
519
+ sub !~ /^(.*\+)?json$/
520
+ end
521
+
522
+ yield remote_document if block_given?
523
+ end
524
+ end
525
+ remote_document
526
+ end
527
+
528
+ ##
529
+ # A {RemoteDocument} is returned from a {documentLoader}.
530
+ class RemoteDocument
531
+ # @return [String] URL of the loaded document, after redirects
532
+ attr_reader :documentUrl
533
+
534
+ # @return [String, Array<Hash>, Hash]
535
+ # The retrieved document, either as raw text or parsed JSON
536
+ attr_reader :document
537
+
538
+ # @return [String]
539
+ # The URL of a remote context as specified by an HTTP Link header with rel=`http://www.w3.org/ns/json-ld#context`
540
+ attr_accessor :contextUrl
541
+
542
+ # @param [String] url URL of the loaded document, after redirects
543
+ # @param [String, Array<Hash>, Hash] document
544
+ # The retrieved document, either as raw text or parsed JSON
545
+ # @param [String] context_url (nil)
546
+ # The URL of a remote context as specified by an HTTP Link header with rel=`http://www.w3.org/ns/json-ld#context`
547
+ def initialize(url, document, context_url = nil)
548
+ @documentUrl = url
549
+ @document = document
550
+ @contextUrl = context_url
551
+ end
552
+ end
408
553
  end
409
554
  end
410
555