json-ld 0.0.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.markdown +36 -0
- data/{README → README.markdown} +1 -1
- data/VERSION +1 -1
- data/lib/json/ld.rb +92 -16
- data/lib/json/ld/api.rb +320 -0
- data/lib/json/ld/evaluation_context.rb +640 -0
- data/lib/json/ld/extensions.rb +6 -0
- data/lib/json/ld/reader.rb +120 -270
- data/lib/json/ld/writer.rb +210 -284
- metadata +53 -38
data/lib/json/ld/writer.rb
CHANGED
@@ -34,10 +34,8 @@ module JSON::LD
|
|
34
34
|
#
|
35
35
|
# The writer will add prefix definitions, and use them for creating @context definitions, and minting CURIEs
|
36
36
|
#
|
37
|
-
# @example Creating
|
37
|
+
# @example Creating @@context prefix definitions in output
|
38
38
|
# JSON::LD::Writer.buffer(
|
39
|
-
# :base_uri => "http://example.com/",
|
40
|
-
# :vocab => "http://example.net/"
|
41
39
|
# :prefixes => {
|
42
40
|
# nil => "http://example.com/ns#",
|
43
41
|
# :foaf => "http://xmlns.com/foaf/0.1/"}
|
@@ -47,7 +45,7 @@ module JSON::LD
|
|
47
45
|
# end
|
48
46
|
# end
|
49
47
|
#
|
50
|
-
# Select the :
|
48
|
+
# Select the :expand option to output JSON-LD in expanded form
|
51
49
|
#
|
52
50
|
# @see http://json-ld.org/spec/ED/20110507/
|
53
51
|
# @see http://json-ld.org/spec/ED/20110507/#the-normalization-algorithm
|
@@ -55,39 +53,18 @@ module JSON::LD
|
|
55
53
|
class Writer < RDF::Writer
|
56
54
|
format Format
|
57
55
|
|
58
|
-
# @attr [Graph] Graph of statements serialized
|
56
|
+
# @attr [RDF::Graph] Graph of statements serialized
|
59
57
|
attr :graph
|
60
|
-
|
61
|
-
attr
|
62
|
-
|
63
|
-
attr :vocab
|
64
|
-
|
65
|
-
# Type coersion to use for serialization. Defaults to DEFAULT_COERCION
|
66
|
-
#
|
67
|
-
# Maintained as a reverse mapping of `property` => `type`.
|
68
|
-
#
|
69
|
-
# @attr [Hash{String => String}]
|
70
|
-
attr :coerce, true
|
71
|
-
|
72
|
-
##
|
73
|
-
# Local implementation of ruby Hash class to allow for ordering in 1.8.x implementations.
|
74
|
-
#
|
75
|
-
# @return [Hash, InsertOrderPreservingHash]
|
76
|
-
def self.new_hash
|
77
|
-
if RUBY_VERSION < "1.9"
|
78
|
-
InsertOrderPreservingHash.new
|
79
|
-
else
|
80
|
-
Hash.new
|
81
|
-
end
|
82
|
-
end
|
83
|
-
def new_hash; self.class.new_hash; end
|
58
|
+
|
59
|
+
# @attr [EvaluationContext] context used to load and administer contexts
|
60
|
+
attr :context
|
84
61
|
|
85
62
|
##
|
86
63
|
# Return the pre-serialized Hash before turning into JSON
|
87
64
|
#
|
88
65
|
# @return [Hash]
|
89
66
|
def self.hash(*args, &block)
|
90
|
-
hash =
|
67
|
+
hash = Hash.new
|
91
68
|
self.new(hash, *args, &block)
|
92
69
|
hash
|
93
70
|
end
|
@@ -103,16 +80,24 @@ module JSON::LD
|
|
103
80
|
# the encoding to use on the output stream (Ruby 1.9+)
|
104
81
|
# @option options [Boolean] :canonicalize (false)
|
105
82
|
# whether to canonicalize literals when serializing
|
106
|
-
# @option options [Boolean] :normalize (false)
|
107
|
-
# Output document in [normalized form](http://json-ld.org/spec/latest/#normalization-1)
|
108
83
|
# @option options [Hash] :prefixes (Hash.new)
|
109
84
|
# the prefix mappings to use (not supported by all writers)
|
110
|
-
# @option options [#to_s] :base_uri (nil)
|
111
|
-
# Base IRI used for relativizing IRIs
|
112
|
-
# @option options [#to_s] :vocab (nil)
|
113
|
-
# Vocabulary prefix used for relativizing IRIs
|
114
85
|
# @option options [Boolean] :standard_prefixes (false)
|
115
86
|
# Add standard prefixes to @prefixes, if necessary.
|
87
|
+
# @option options [IO, Array, Hash, String, EvaluationContext] :context (Hash.new)
|
88
|
+
# context to use when serializing. Constructed context for native serialization.
|
89
|
+
# @option options [Boolean] :automatic (true)
|
90
|
+
# Automatically create context coercions and generate compacted form
|
91
|
+
# @option options [Boolean] :expand (false)
|
92
|
+
# Output document in [expanded form](http://json-ld.org/spec/latest/json-ld-api/#expansion)
|
93
|
+
# @option options [Boolean] :compact (false)
|
94
|
+
# Output document in [compacted form](http://json-ld.org/spec/latest/json-ld-api/#compaction).
|
95
|
+
# Requires a referenced evaluation context
|
96
|
+
# @option options [Boolean] :normalize (false)
|
97
|
+
# Output document in [normalized form](http://json-ld.org/spec/latest/json-ld-api/#normalization)
|
98
|
+
# @option options [IO, Array, Hash, String] :frame
|
99
|
+
# Output document in [framed form](http://json-ld.org/spec/latest/json-ld-api/#framing)
|
100
|
+
# using the referenced document as a frame.
|
116
101
|
# @yield [writer] `self`
|
117
102
|
# @yieldparam [RDF::Writer] writer
|
118
103
|
# @yieldreturn [void]
|
@@ -121,8 +106,8 @@ module JSON::LD
|
|
121
106
|
def initialize(output = $stdout, options = {}, &block)
|
122
107
|
super do
|
123
108
|
@graph = RDF::Graph.new
|
124
|
-
@
|
125
|
-
|
109
|
+
@options[:automatic] = true unless [:automatic, :expand, :compact, :frame, :normalize].any? {|k| options.has_key?(k)}
|
110
|
+
|
126
111
|
if block_given?
|
127
112
|
case block.arity
|
128
113
|
when 0 then instance_eval(&block)
|
@@ -138,7 +123,7 @@ module JSON::LD
|
|
138
123
|
# @param [Graph] graph
|
139
124
|
# @return [void]
|
140
125
|
def write_graph(graph)
|
141
|
-
|
126
|
+
debug {"Add graph #{graph.inspect}"}
|
142
127
|
@graph = graph
|
143
128
|
end
|
144
129
|
|
@@ -168,18 +153,32 @@ module JSON::LD
|
|
168
153
|
# @return [void]
|
169
154
|
# @see #write_triple
|
170
155
|
def write_epilogue
|
171
|
-
@base_uri = RDF::URI(@options[:base_uri]) if @options[:base_uri] && !@options[:normalize]
|
172
|
-
@vocab = @options[:vocab] unless @options[:normalize]
|
173
156
|
@debug = @options[:debug]
|
174
157
|
|
175
158
|
reset
|
159
|
+
|
160
|
+
raise RDF::WriterError, "Compaction requres a context" if @options[:compact] && !@options[:context]
|
176
161
|
|
177
|
-
|
162
|
+
@context = EvaluationContext.new(@options)
|
163
|
+
@context = @context.parse(@options[:context]) if @options[:context]
|
164
|
+
@context.language = @options[:language] if @options[:language]
|
165
|
+
@context.lists.each {|p| @list_range[p] = true}
|
166
|
+
|
167
|
+
debug {"\nserialize: graph: #{@graph.size}"}
|
168
|
+
debug {"=> options: #{@options.reject {|k,v| k == :debug}.inspect}"}
|
169
|
+
debug {"=> context: #{@context.inspect}"}
|
178
170
|
|
179
171
|
preprocess
|
180
|
-
|
181
|
-
#
|
182
|
-
|
172
|
+
|
173
|
+
# Update prefix mappings to those defined in context
|
174
|
+
@options[:prefixes] = {}
|
175
|
+
@context.iri_to_term.each_pair do |iri, term|
|
176
|
+
debug {"add prefix #{term.inspect} for #{iri}"}
|
177
|
+
prefix(term, iri) # Define for output
|
178
|
+
end
|
179
|
+
|
180
|
+
# Don't generate context for expanded or normalized output
|
181
|
+
json_hash = (@options[:expand] || @options[:normalize]) ? Hash.new : context.serialize(:depth => @depth)
|
183
182
|
|
184
183
|
elements = []
|
185
184
|
order_subjects.each do |subject|
|
@@ -190,16 +189,18 @@ module JSON::LD
|
|
190
189
|
|
191
190
|
return if elements.empty?
|
192
191
|
|
192
|
+
# If there are more than one top-level subjects, place in an array form
|
193
193
|
if elements.length == 1 && elements.first.is_a?(Hash)
|
194
194
|
json_hash.merge!(elements.first)
|
195
195
|
else
|
196
|
-
json_hash['@
|
196
|
+
json_hash['@id'] = elements
|
197
197
|
end
|
198
198
|
|
199
199
|
if @output.is_a?(Hash)
|
200
200
|
@output.merge!(json_hash)
|
201
201
|
else
|
202
202
|
json_state = if @options[:normalize]
|
203
|
+
# Normalization uses a compressed form
|
203
204
|
JSON::State.new(
|
204
205
|
:indent => "",
|
205
206
|
:space => "",
|
@@ -223,35 +224,24 @@ module JSON::LD
|
|
223
224
|
##
|
224
225
|
# Returns the representation of a IRI reference.
|
225
226
|
#
|
226
|
-
# Spec confusion: should a subject
|
227
|
+
# Spec confusion: should a subject IRI be normalized?
|
227
228
|
#
|
228
229
|
# @param [RDF::URI] value
|
229
230
|
# @param [Hash{Symbol => Object}] options
|
230
231
|
# @option options [:subject, :predicate, :object] position
|
231
232
|
# Useful when determining how to serialize.
|
232
233
|
# @option options [RDF::URI] property
|
233
|
-
# Property for object reference, which can be used to return
|
234
|
-
# bare strings, rather than {"iri":}
|
234
|
+
# Property for object reference, which can be used to return bare strings
|
235
235
|
# @return [Object]
|
236
|
-
def
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
when :predicate
|
243
|
-
# attempt vocab replacement
|
244
|
-
short = '@type' if value == RDF.type
|
245
|
-
short ||= value.to_s.sub(@vocab.to_s, "")
|
246
|
-
short == value.to_s ? (get_curie(value) || value.to_s) : short
|
247
|
-
else
|
248
|
-
# Encode like a subject
|
249
|
-
iri_range?(options[:property]) ?
|
250
|
-
format_uri(value, :position => :subject) :
|
251
|
-
{'@iri' => format_uri(value, :position => :subject)}
|
236
|
+
def format_iri(value, options = {})
|
237
|
+
debug {"format_iri(#{options.inspect}, #{value.inspect})"}
|
238
|
+
|
239
|
+
result = context.compact_iri(value, {:depth => @depth}.merge(options))
|
240
|
+
unless options[:position] != :object || iri_range?(options[:property])
|
241
|
+
result = {"@id" => result}
|
252
242
|
end
|
253
243
|
|
254
|
-
|
244
|
+
debug {"=> #{result.inspect}"}
|
255
245
|
result
|
256
246
|
end
|
257
247
|
|
@@ -260,9 +250,9 @@ module JSON::LD
|
|
260
250
|
# @param [Hash{Symbol => Object}] options
|
261
251
|
# @return [String]
|
262
252
|
# @raise [NotImplementedError] unless implemented in subclass
|
263
|
-
# @
|
253
|
+
# @see {#format\_iri}
|
264
254
|
def format_node(value, options = {})
|
265
|
-
|
255
|
+
format_iri(value, options)
|
266
256
|
end
|
267
257
|
|
268
258
|
##
|
@@ -274,137 +264,88 @@ module JSON::LD
|
|
274
264
|
# Property referencing literal for type coercion
|
275
265
|
# @return [Object]
|
276
266
|
def format_literal(literal, options = {})
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
end
|
267
|
+
debug {"format_literal(#{options.inspect}, #{literal.inspect})"}
|
268
|
+
|
269
|
+
value = Hash.new
|
270
|
+
value['@literal'] = literal.value
|
271
|
+
value['@type'] = literal.datatype.to_s if literal.has_datatype?
|
272
|
+
value['@language'] = literal.language.to_s if literal.has_language?
|
284
273
|
|
285
|
-
case literal
|
286
|
-
when RDF::Literal::Integer, RDF::Literal::
|
274
|
+
result = case literal
|
275
|
+
when RDF::Literal::Boolean, RDF::Literal::Integer, RDF::Literal::Double
|
287
276
|
literal.object
|
288
|
-
|
289
|
-
|
290
|
-
# Datatype coercion where literal has the same datatype
|
291
|
-
literal.value
|
292
|
-
else
|
293
|
-
format_literal(literal, :normal => true)
|
294
|
-
end
|
277
|
+
else
|
278
|
+
context.compact_value(options[:property], value, {:depth => @depth}.merge(options))
|
295
279
|
end
|
280
|
+
|
281
|
+
debug {"=> #{result.inspect}"}
|
282
|
+
result
|
296
283
|
end
|
297
284
|
|
298
285
|
##
|
299
286
|
# Serialize an RDF list
|
287
|
+
#
|
300
288
|
# @param [RDF::URI] object
|
301
289
|
# @param [Hash{Symbol => Object}] options
|
302
290
|
# @option options [RDF::URI] property
|
303
|
-
# Property referencing literal for type coercion
|
291
|
+
# Property referencing literal for type and list coercion
|
304
292
|
# @return [Hash{"@list" => Array<Object>}]
|
305
293
|
def format_list(object, options = {})
|
306
294
|
predicate = options[:property]
|
307
|
-
list =
|
295
|
+
list = RDF::List.new(object, @graph)
|
296
|
+
ary = []
|
308
297
|
|
309
|
-
|
298
|
+
debug {"format_list(#{list.inspect}, #{predicate})"}
|
310
299
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
add_debug {"format_list serialize #{item.inspect}"}
|
318
|
-
list << if predicate || item.literal?
|
319
|
-
property(predicate, item)
|
300
|
+
depth do
|
301
|
+
list.each_statement do |st|
|
302
|
+
next unless st.predicate == RDF.first
|
303
|
+
debug {" format_list this: #{st.subject} first: #{st.object}"}
|
304
|
+
ary << if predicate || st.object.literal?
|
305
|
+
property(predicate, st.object)
|
320
306
|
else
|
321
|
-
subject(
|
307
|
+
subject(st.object)
|
322
308
|
end
|
309
|
+
subject_done(st.subject)
|
323
310
|
end
|
324
|
-
object = p.fetch(RDF.rest.to_s, []).first
|
325
311
|
end
|
326
|
-
@depth -= 1
|
327
312
|
|
328
|
-
# Returns
|
329
|
-
|
330
|
-
{
|
313
|
+
# Returns
|
314
|
+
ary = {'@list' => ary} unless predicate && list_range?(predicate)
|
315
|
+
debug {"format_list => #{ary.inspect}"}
|
316
|
+
ary
|
331
317
|
end
|
332
318
|
|
333
319
|
private
|
334
|
-
##
|
335
|
-
# Generate @context
|
336
|
-
# @return [Hash]
|
337
|
-
def start_document
|
338
|
-
ctx = new_hash
|
339
|
-
ctx['@base'] = base_uri.to_s if base_uri
|
340
|
-
ctx['@vocab'] = vocab.to_s if vocab
|
341
|
-
|
342
|
-
# Prefixes
|
343
|
-
prefixes.keys.sort {|a,b| a.to_s <=> b.to_s}.each do |k|
|
344
|
-
next if DEFAULT_CONTEXT.has_key?(k.to_s)
|
345
|
-
add_debug {"prefix[#{k}] => #{prefixes[k]}"}
|
346
|
-
ctx[k.to_s] = prefixes[k].to_s
|
347
|
-
end
|
348
|
-
|
349
|
-
# Coerce
|
350
|
-
add_debug {"start_doc: coerce= #{coerce.inspect}"}
|
351
|
-
unless coerce == DEFAULT_COERCE
|
352
|
-
c_h = new_hash
|
353
|
-
coerce.keys.sort.each do |k|
|
354
|
-
next if ['@type', RDF.type.to_s].include?(k.to_s)
|
355
|
-
next if [DEFAULT_COERCE[k], false, RDF::XSD.integer.to_s, RDF::XSD.boolean.to_s].include?(coerce[k])
|
356
|
-
k_iri = k == '@iri' ? '@iri' : format_uri(k, :position => :predicate)
|
357
|
-
d_iri = format_uri(coerce[k], :position => :subject)
|
358
|
-
add_debug {"coerce[#{k_iri}] => #{d_iri}, k=#{k.inspect}"}
|
359
|
-
case c_h[d_iri]
|
360
|
-
when nil
|
361
|
-
c_h[d_iri] = k_iri
|
362
|
-
when Array
|
363
|
-
c_h[d_iri] << k_iri
|
364
|
-
else
|
365
|
-
c_h[d_iri] = [c_h[d_iri], k_iri]
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
ctx['@coerce'] = c_h unless c_h.empty?
|
370
|
-
end
|
371
|
-
|
372
|
-
add_debug {"start_doc: context=#{ctx.inspect}"}
|
373
|
-
|
374
|
-
# Return hash with @context, or empty
|
375
|
-
r = new_hash
|
376
|
-
r['@context'] = ctx unless ctx.empty?
|
377
|
-
r
|
378
|
-
end
|
379
|
-
|
380
320
|
# Perform any preprocessing of statements required
|
381
321
|
def preprocess
|
382
|
-
# Load defined prefixes
|
383
|
-
(@options[:prefixes] || {}).each_pair do |k, v|
|
384
|
-
@iri_to_prefix[v.to_s] = k
|
385
|
-
end
|
386
|
-
@options[:prefixes] = new_hash # Will define actual used when matched
|
387
|
-
|
388
322
|
@graph.each {|statement| preprocess_statement(statement)}
|
389
323
|
end
|
390
324
|
|
391
325
|
# Perform any statement preprocessing required. This is used to perform reference counts and determine required
|
392
326
|
# prefixes.
|
327
|
+
#
|
393
328
|
# @param [Statement] statement
|
394
329
|
def preprocess_statement(statement)
|
395
|
-
|
330
|
+
debug {"preprocess: #{statement.inspect}"}
|
396
331
|
references = ref_count(statement.object) + 1
|
397
332
|
@references[statement.object] = references
|
398
333
|
@subjects[statement.subject] = true
|
399
334
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
335
|
+
depth do
|
336
|
+
# Pre-fetch qnames, to fill prefixes
|
337
|
+
format_iri(statement.subject, :position => :subject)
|
338
|
+
format_iri(statement.predicate, :position => :predicate)
|
339
|
+
|
340
|
+
# To figure out coercion requirements
|
341
|
+
if statement.object.literal?
|
342
|
+
format_literal(statement.object, :property => statement.predicate)
|
343
|
+
datatype_range?(statement.predicate)
|
344
|
+
else
|
345
|
+
format_iri(statement.object, :position => :object)
|
346
|
+
iri_range?(statement.predicate)
|
347
|
+
end
|
348
|
+
list_range?(statement.predicate)
|
408
349
|
end
|
409
350
|
|
410
351
|
@references[statement.predicate] = ref_count(statement.predicate) + 1
|
@@ -414,53 +355,53 @@ module JSON::LD
|
|
414
355
|
# Option contains referencing property, if this is recursive
|
415
356
|
# @return [Hash]
|
416
357
|
def subject(subject, options = {})
|
417
|
-
defn =
|
358
|
+
defn = Hash.new
|
418
359
|
|
419
360
|
raise RDF::WriterError, "Illegal use of subject #{subject.inspect}, not supported" unless subject.resource?
|
420
361
|
|
421
362
|
subject_done(subject)
|
422
363
|
properties = @graph.properties(subject)
|
423
|
-
|
364
|
+
debug {"subject: #{subject.inspect}, props: #{properties.inspect}"}
|
424
365
|
|
425
366
|
@graph.query(:subject => subject).each do |st|
|
426
367
|
raise RDF::WriterError, "Illegal use of predicate #{st.predicate.inspect}, not supported in RDF/XML" unless st.predicate.uri?
|
427
368
|
end
|
428
369
|
|
429
|
-
if subject.node? && ref_count(subject) > (options[:property] ? 1 : 0) && options[:
|
370
|
+
if subject.node? && ref_count(subject) > (options[:property] ? 1 : 0) && options[:expand]
|
430
371
|
raise RDF::WriterError, "Can't serialize named node when normalizing"
|
431
372
|
end
|
432
373
|
|
433
374
|
# Subject may be a list
|
434
375
|
if is_valid_list?(subject)
|
435
|
-
|
436
|
-
defn['@
|
376
|
+
debug "subject is a list"
|
377
|
+
defn['@id'] = format_list(subject)
|
437
378
|
properties.delete(RDF.first.to_s)
|
438
379
|
properties.delete(RDF.rest.to_s)
|
439
380
|
|
440
381
|
# Special case, if there are no properties, then we can just serialize the list itself
|
441
382
|
return defn if properties.empty?
|
442
383
|
elsif subject.uri? || ref_count(subject) > 1
|
443
|
-
|
384
|
+
debug "subject is an iri or it's a node referenced multiple times"
|
444
385
|
# Don't need to set subject if it's a Node without references
|
445
|
-
defn['@
|
386
|
+
defn['@id'] = format_iri(subject, :position => :subject)
|
446
387
|
else
|
447
|
-
|
388
|
+
debug "subject is an unreferenced BNode"
|
448
389
|
end
|
449
390
|
|
450
391
|
prop_list = order_properties(properties)
|
451
|
-
|
392
|
+
debug {"=> property order: #{prop_list.inspect}"}
|
452
393
|
|
453
394
|
prop_list.each do |prop|
|
454
395
|
predicate = RDF::URI.intern(prop)
|
455
396
|
|
456
|
-
p_iri =
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
397
|
+
p_iri = format_iri(predicate, :position => :predicate)
|
398
|
+
depth do
|
399
|
+
defn[p_iri] = property(predicate, properties[prop])
|
400
|
+
debug {"prop(#{p_iri}) => #{properties[prop]} => #{defn[p_iri].inspect}"}
|
401
|
+
end
|
461
402
|
end
|
462
403
|
|
463
|
-
|
404
|
+
debug {"subject: #{subject} has defn: #{defn.inspect}"}
|
464
405
|
defn
|
465
406
|
end
|
466
407
|
|
@@ -484,55 +425,13 @@ module JSON::LD
|
|
484
425
|
if is_valid_list?(objects)
|
485
426
|
format_list(objects, :property => predicate)
|
486
427
|
elsif is_done?(objects) || !@subjects.include?(objects)
|
487
|
-
|
428
|
+
format_iri(objects, :position => :object, :property => predicate)
|
488
429
|
else
|
489
430
|
subject(objects, :property => predicate)
|
490
431
|
end
|
491
432
|
end
|
492
433
|
end
|
493
434
|
|
494
|
-
##
|
495
|
-
# Return a CURIE for the IRI, or nil. Adds namespace of CURIE to defined prefixes
|
496
|
-
# @param [RDF::Resource] resource
|
497
|
-
# @return [String, nil] value to use to identify IRI
|
498
|
-
def get_curie(resource)
|
499
|
-
add_debug {"get_curie(#{resource.inspect})"}
|
500
|
-
case resource
|
501
|
-
when RDF::Node
|
502
|
-
return resource.to_s
|
503
|
-
when String
|
504
|
-
iri = resource
|
505
|
-
resource = RDF::URI(resource)
|
506
|
-
return nil unless resource.absolute?
|
507
|
-
when RDF::URI
|
508
|
-
iri = resource.to_s
|
509
|
-
return iri if options[:normalize]
|
510
|
-
else
|
511
|
-
return nil
|
512
|
-
end
|
513
|
-
|
514
|
-
curie = case
|
515
|
-
when @iri_to_curie.has_key?(iri)
|
516
|
-
return @iri_to_curie[iri]
|
517
|
-
when u = @iri_to_prefix.keys.detect {|u| iri.index(u.to_s) == 0}
|
518
|
-
# Use a defined prefix
|
519
|
-
prefix = @iri_to_prefix[u]
|
520
|
-
prefix(prefix, u) # Define for output
|
521
|
-
iri.sub(u.to_s, "#{prefix}:")
|
522
|
-
when @options[:standard_prefixes] && vocab = RDF::Vocabulary.detect {|v| iri.index(v.to_uri.to_s) == 0}
|
523
|
-
prefix = vocab.__name__.to_s.split('::').last.downcase
|
524
|
-
@iri_to_prefix[vocab.to_uri.to_s] = prefix
|
525
|
-
prefix(prefix, vocab.to_uri) # Define for output
|
526
|
-
iri.sub(vocab.to_uri.to_s, "#{prefix}:")
|
527
|
-
else
|
528
|
-
nil
|
529
|
-
end
|
530
|
-
|
531
|
-
@iri_to_curie[iri] = curie
|
532
|
-
rescue Addressable::URI::InvalidURIError => e
|
533
|
-
raise RDF::WriterError, "Invalid IRI #{resource.inspect}: #{e.message}"
|
534
|
-
end
|
535
|
-
|
536
435
|
##
|
537
436
|
# Take a hash from predicate IRIs to lists of values.
|
538
437
|
# Sort the lists of values. Return a sorted list of properties.
|
@@ -542,8 +441,8 @@ module JSON::LD
|
|
542
441
|
# Make sorted list of properties
|
543
442
|
prop_list = []
|
544
443
|
|
545
|
-
properties.keys.sort do |a,b|
|
546
|
-
|
444
|
+
properties.keys.sort do |a, b|
|
445
|
+
format_iri(a, :position => :predicate) <=> format_iri(b, :position => :predicate)
|
547
446
|
end.each do |prop|
|
548
447
|
prop_list << prop.to_s
|
549
448
|
end
|
@@ -553,7 +452,6 @@ module JSON::LD
|
|
553
452
|
|
554
453
|
# Order subjects for output. Override this to output subjects in another order.
|
555
454
|
#
|
556
|
-
# Uses #base_uri.
|
557
455
|
# @return [Array<Resource>] Ordered list of subjects
|
558
456
|
def order_subjects
|
559
457
|
seen = {}
|
@@ -561,14 +459,8 @@ module JSON::LD
|
|
561
459
|
|
562
460
|
return @subjects.keys.sort do |a,b|
|
563
461
|
format_iri(a, :position => :subject) <=> format_iri(b, :position => :subject)
|
564
|
-
end
|
462
|
+
end unless @options[:automatic]
|
565
463
|
|
566
|
-
# Start with base_uri
|
567
|
-
if base_uri && @subjects.keys.include?(base_uri)
|
568
|
-
subjects << base_uri
|
569
|
-
seen[base_uri] = true
|
570
|
-
end
|
571
|
-
|
572
464
|
# Sort subjects by resources over bnodes, ref_counts and the subject URI itself
|
573
465
|
recursable = @subjects.keys.
|
574
466
|
select {|s| !seen.include?(s)}.
|
@@ -589,16 +481,30 @@ module JSON::LD
|
|
589
481
|
# @param [RDF::URI] predicate
|
590
482
|
# @return [Boolean]
|
591
483
|
def iri_range?(predicate)
|
592
|
-
return false if predicate.nil? || @options[:
|
484
|
+
return false if predicate.nil? || [RDF.first, RDF.rest].include?(predicate) || @options[:expand]
|
485
|
+
return true if predicate == RDF.type
|
593
486
|
|
594
|
-
unless coerce
|
595
|
-
|
596
|
-
|
597
|
-
|
487
|
+
unless context.coerce(predicate)
|
488
|
+
not_iri = !@options[:automatic]
|
489
|
+
#debug {" (automatic) = #{(!not_iri).inspect}"}
|
490
|
+
|
491
|
+
# Any literal object makes it not so
|
492
|
+
not_iri ||= @graph.query(:predicate => predicate).to_a.any? do |st|
|
493
|
+
l = RDF::List.new(st.object, @graph)
|
494
|
+
#debug {" o.literal? #{st.object.literal?.inspect}"}
|
495
|
+
#debug {" l.valid? #{l.valid?.inspect}"}
|
496
|
+
#debug {" l.any.valid? #{l.to_a.any?(&:literal?).inspect}"}
|
497
|
+
st.object.literal? || (l.valid? && l.to_a.any?(&:literal?))
|
498
|
+
end
|
499
|
+
#debug {" (literal) = #{(!not_iri).inspect}"}
|
500
|
+
|
501
|
+
# FIXME: detect when values are all represented through chaining
|
502
|
+
|
503
|
+
context.coerce(predicate, not_iri ? false : '@id')
|
598
504
|
end
|
599
505
|
|
600
|
-
|
601
|
-
coerce
|
506
|
+
debug {"iri_range(#{predicate}) = #{context.coerce(predicate).inspect}"}
|
507
|
+
context.coerce(predicate) == '@id'
|
602
508
|
end
|
603
509
|
|
604
510
|
##
|
@@ -606,23 +512,56 @@ module JSON::LD
|
|
606
512
|
# @param [RDF::URI] predicate
|
607
513
|
# @return [Boolean]
|
608
514
|
def datatype_range?(predicate)
|
609
|
-
unless coerce
|
515
|
+
unless context.coerce(predicate)
|
610
516
|
# objects of all statements with the predicate must be literal
|
611
517
|
# and have the same non-nil datatype
|
612
518
|
dt = nil
|
613
|
-
@
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
519
|
+
if @options[:automatic]
|
520
|
+
@graph.query(:predicate => predicate) do |st|
|
521
|
+
debug {"datatype_range? literal? #{st.object.literal?.inspect} dt? #{(st.object.literal? && st.object.has_datatype?).inspect}"}
|
522
|
+
if st.object.literal? && st.object.has_datatype?
|
523
|
+
dt = st.object.datatype.to_s if dt.nil?
|
524
|
+
debug {"=> dt: #{st.object.datatype}"}
|
525
|
+
dt = false unless dt == st.object.datatype.to_s
|
526
|
+
else
|
527
|
+
dt = false
|
528
|
+
end
|
529
|
+
end
|
530
|
+
# Cause necessary prefixes to be output
|
531
|
+
format_iri(dt, :position => :datatype) if dt && !NATIVE_DATATYPES.include?(dt.to_s)
|
532
|
+
debug {"range(#{predicate}) = #{dt.inspect}"}
|
533
|
+
else
|
534
|
+
dt = false
|
535
|
+
end
|
536
|
+
context.coerce(predicate, dt)
|
537
|
+
end
|
538
|
+
|
539
|
+
context.coerce(predicate)
|
540
|
+
end
|
541
|
+
|
542
|
+
##
|
543
|
+
# Is every use of the predicate an RDF Collection?
|
544
|
+
#
|
545
|
+
# @param [RDF::URI] predicate
|
546
|
+
# @return [Boolean]
|
547
|
+
def list_range?(predicate)
|
548
|
+
return false if [RDF.first, RDF.rest].include?(predicate)
|
549
|
+
|
550
|
+
unless @list_range.include?(predicate.to_s)
|
551
|
+
# objects of all statements with the predicate must be a list
|
552
|
+
@list_range[predicate.to_s] = if @options[:automatic]
|
553
|
+
@graph.query(:predicate => predicate).to_a.all? do |st|
|
554
|
+
is_valid_list?(st.object)
|
619
555
|
end
|
556
|
+
else
|
557
|
+
false
|
620
558
|
end
|
621
|
-
|
622
|
-
|
559
|
+
context.list(predicate, true) if @list_range[predicate.to_s]
|
560
|
+
|
561
|
+
debug {"list(#{predicate}) = #{@list_range[predicate.to_s].inspect}"}
|
623
562
|
end
|
624
563
|
|
625
|
-
|
564
|
+
@list_range[predicate.to_s]
|
626
565
|
end
|
627
566
|
|
628
567
|
# Reset internal helper instance variables
|
@@ -631,46 +570,13 @@ module JSON::LD
|
|
631
570
|
@references = {}
|
632
571
|
@serialized = {}
|
633
572
|
@subjects = {}
|
634
|
-
@
|
573
|
+
@list_range = {}
|
635
574
|
end
|
636
575
|
|
637
|
-
# Add debug event to debug array, if specified
|
638
|
-
#
|
639
|
-
# @param [String] message
|
640
|
-
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
641
|
-
def add_debug(message = "")
|
642
|
-
return unless ::JSON::LD.debug? || @options[:debug]
|
643
|
-
message = message + yield if block_given?
|
644
|
-
msg = "#{" " * @depth * 2}#{message}"
|
645
|
-
STDERR.puts msg if ::JSON::LD::debug?
|
646
|
-
@debug << msg if @debug.is_a?(Array)
|
647
|
-
end
|
648
|
-
|
649
576
|
# Checks if l is a valid RDF list, i.e. no nodes have other properties.
|
650
577
|
def is_valid_list?(l)
|
651
|
-
|
652
|
-
|
653
|
-
add_debug {"is_valid_list: false, #{l.inspect}: #{props.inspect}"}
|
654
|
-
return false
|
655
|
-
end
|
656
|
-
|
657
|
-
while l && l != RDF.nil do
|
658
|
-
#add_debug {"is_valid_list(length): #{props.length}"}
|
659
|
-
return false unless props.has_key?(RDF.first.to_s) && props.has_key?(RDF.rest.to_s)
|
660
|
-
n = props[RDF.rest.to_s]
|
661
|
-
unless n.is_a?(Array) && n.length == 1
|
662
|
-
add_debug {"is_valid_list: false, #{n.inspect}"}
|
663
|
-
return false
|
664
|
-
end
|
665
|
-
l = n.first
|
666
|
-
unless l.node? || l == RDF.nil
|
667
|
-
add_debug {"is_valid_list: false, #{l.inspect}"}
|
668
|
-
return false
|
669
|
-
end
|
670
|
-
props = @graph.properties(l)
|
671
|
-
end
|
672
|
-
add_debug {"is_valid_list: valid"}
|
673
|
-
true
|
578
|
+
#debug {"is_valid_list: #{l.inspect}"}
|
579
|
+
return RDF::List.new(l, @graph).valid?
|
674
580
|
end
|
675
581
|
|
676
582
|
def is_done?(subject)
|
@@ -681,6 +587,26 @@ module JSON::LD
|
|
681
587
|
def subject_done(subject)
|
682
588
|
@serialized[subject] = true
|
683
589
|
end
|
590
|
+
|
591
|
+
# Add debug event to debug array, if specified
|
592
|
+
#
|
593
|
+
# @param [String] message
|
594
|
+
# @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
|
595
|
+
def debug(*args)
|
596
|
+
return unless ::JSON::LD.debug? || @options[:debug]
|
597
|
+
message = " " * @depth * 2 + (args.empty? ? "" : args.join(": "))
|
598
|
+
message += yield if block_given?
|
599
|
+
puts message if JSON::LD::debug?
|
600
|
+
@options[:debug] << message if @options[:debug].is_a?(Array)
|
601
|
+
end
|
602
|
+
|
603
|
+
# Increase depth around a method invocation
|
604
|
+
def depth
|
605
|
+
@depth += 1
|
606
|
+
ret = yield
|
607
|
+
@depth -= 1
|
608
|
+
ret
|
609
|
+
end
|
684
610
|
end
|
685
611
|
end
|
686
612
|
|