rdf-turtle 3.0.1 → 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/rdf/turtle/writer.rb +109 -73
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a8fb1c87f5b5149cc3430a1cdac50fdd7bfbaeacfeaa1a6770e54e83a1aff09
|
4
|
+
data.tar.gz: 7bf9532ca0c78ce4e24e51ef3a4076a8563e2f0fa2e9f9853616f47776f0947f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55ec7f72b8b020506c134805828ff43341ce455b469edfb396332c61b67ae3cc7bdf2a0dcfcb3808c47201c77564e046a84d0aaaba2a74553fd2d1c30c93a184
|
7
|
+
data.tar.gz: 308390211f1da6158fe12d322d8dec7e24ce94931ccd994fb202bf9ac5f38665ac3f1f2e7c02ae9a20e8f3b775462e8831b1357a96be045f8f427d5d241dbf84
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0.
|
1
|
+
3.0.2
|
data/lib/rdf/turtle/writer.rb
CHANGED
@@ -183,8 +183,15 @@ module RDF::Turtle
|
|
183
183
|
log_debug("\nserialize") {"graph: #{@graph.size}"}
|
184
184
|
|
185
185
|
preprocess
|
186
|
+
|
186
187
|
start_document
|
187
188
|
|
189
|
+
# Remove lists that are referenced and have non-list properties;
|
190
|
+
# these are legal, but can't be serialized as lists
|
191
|
+
@lists.reject! do |node, list|
|
192
|
+
ref_count(node) > 0 && non_list_prop_count(node) > 0
|
193
|
+
end
|
194
|
+
|
188
195
|
order_subjects.each do |subject|
|
189
196
|
unless is_done?(subject)
|
190
197
|
statement(subject)
|
@@ -193,7 +200,7 @@ module RDF::Turtle
|
|
193
200
|
end
|
194
201
|
super
|
195
202
|
end
|
196
|
-
|
203
|
+
|
197
204
|
# Return a QName for the URI, or nil. Adds namespace of QName to defined prefixes
|
198
205
|
# @param [RDF::Resource] resource
|
199
206
|
# @return [String, nil] value to use to identify URI
|
@@ -210,7 +217,7 @@ module RDF::Turtle
|
|
210
217
|
pname = case
|
211
218
|
when @uri_to_pname.has_key?(uri)
|
212
219
|
return @uri_to_pname[uri]
|
213
|
-
when u = @uri_to_prefix.keys.sort_by {|
|
220
|
+
when u = @uri_to_prefix.keys.sort_by {|uu| uu.length}.reverse.detect {|uu| uri.index(uu.to_s) == 0}
|
214
221
|
# Use a defined prefix
|
215
222
|
prefix = @uri_to_prefix[u]
|
216
223
|
unless u.to_s.empty?
|
@@ -227,7 +234,7 @@ module RDF::Turtle
|
|
227
234
|
else
|
228
235
|
nil
|
229
236
|
end
|
230
|
-
|
237
|
+
|
231
238
|
# Make sure pname is a valid pname
|
232
239
|
if pname
|
233
240
|
md = Terminals::PNAME_LN.match(pname) || Terminals::PNAME_NS.match(pname)
|
@@ -236,7 +243,7 @@ module RDF::Turtle
|
|
236
243
|
|
237
244
|
@uri_to_pname[uri] = pname
|
238
245
|
end
|
239
|
-
|
246
|
+
|
240
247
|
# Take a hash from predicate uris to lists of values.
|
241
248
|
# Sort the lists of values. Return a sorted list of properties.
|
242
249
|
# @param [Hash{String => Array<Resource>}] properties A hash of Property to Resource mappings
|
@@ -244,17 +251,17 @@ module RDF::Turtle
|
|
244
251
|
def sort_properties(properties)
|
245
252
|
# Make sorted list of properties
|
246
253
|
prop_list = []
|
247
|
-
|
254
|
+
|
248
255
|
predicate_order.each do |prop|
|
249
256
|
next unless properties[prop.to_s]
|
250
257
|
prop_list << prop.to_s
|
251
258
|
end
|
252
|
-
|
259
|
+
|
253
260
|
properties.keys.sort.each do |prop|
|
254
261
|
next if prop_list.include?(prop.to_s)
|
255
262
|
prop_list << prop.to_s
|
256
263
|
end
|
257
|
-
|
264
|
+
|
258
265
|
log_debug("sort_properties") {prop_list.join(', ')}
|
259
266
|
prop_list
|
260
267
|
end
|
@@ -283,7 +290,7 @@ module RDF::Turtle
|
|
283
290
|
quoted(literal.to_s)
|
284
291
|
end
|
285
292
|
end
|
286
|
-
|
293
|
+
|
287
294
|
##
|
288
295
|
# Returns the Turtle representation of a URI reference.
|
289
296
|
#
|
@@ -295,7 +302,7 @@ module RDF::Turtle
|
|
295
302
|
log_debug("relativize") {"#{uri.to_ntriples} => #{md.inspect}"} if md != uri.to_s
|
296
303
|
md != uri.to_s ? "<#{md}>" : (get_pname(uri) || "<#{uri}>")
|
297
304
|
end
|
298
|
-
|
305
|
+
|
299
306
|
##
|
300
307
|
# Returns the Turtle representation of a blank node.
|
301
308
|
#
|
@@ -305,12 +312,12 @@ module RDF::Turtle
|
|
305
312
|
def format_node(node, options = {})
|
306
313
|
options[:unique_bnodes] ? node.to_unique_base : node.to_base
|
307
314
|
end
|
308
|
-
|
315
|
+
|
309
316
|
protected
|
310
317
|
# Output @base and @prefix definitions
|
311
318
|
def start_document
|
312
319
|
@output.write("#{indent}@base <#{base_uri}> .\n") unless base_uri.to_s.empty?
|
313
|
-
|
320
|
+
|
314
321
|
log_debug("start_document") {prefixes.inspect}
|
315
322
|
prefixes.keys.sort_by(&:to_s).each do |prefix|
|
316
323
|
@output.write("#{indent}@prefix #{prefix}: <#{prefixes[prefix]}> .\n")
|
@@ -325,7 +332,7 @@ module RDF::Turtle
|
|
325
332
|
# `\[rdf:type, rdfs:label, dc:title\]`
|
326
333
|
# @return [Array<URI>]
|
327
334
|
def predicate_order; [RDF.type, RDF::RDFS.label, RDF::URI("http://purl.org/dc/terms/title")]; end
|
328
|
-
|
335
|
+
|
329
336
|
# Order subjects for output. Override this to output subjects in another order.
|
330
337
|
#
|
331
338
|
# Uses #top_classes and #base_uri.
|
@@ -333,31 +340,44 @@ module RDF::Turtle
|
|
333
340
|
def order_subjects
|
334
341
|
seen = {}
|
335
342
|
subjects = []
|
336
|
-
|
343
|
+
|
337
344
|
# Start with base_uri
|
338
345
|
if base_uri && @subjects.keys.include?(base_uri)
|
339
346
|
subjects << RDF::URI(base_uri)
|
340
347
|
seen[RDF::URI(base_uri)] = true
|
341
348
|
end
|
342
|
-
|
349
|
+
|
343
350
|
# Add distinguished classes
|
344
351
|
top_classes.each do |class_uri|
|
345
|
-
graph.query(predicate: RDF.type, object: class_uri).
|
352
|
+
graph.query(predicate: RDF.type, object: class_uri).
|
353
|
+
map {|st| st.subject}.
|
354
|
+
sort.
|
355
|
+
uniq.
|
356
|
+
each do |subject|
|
346
357
|
log_debug("order_subjects") {subject.to_ntriples}
|
347
358
|
subjects << subject
|
348
359
|
seen[subject] = true
|
349
360
|
end
|
350
361
|
end
|
351
|
-
|
362
|
+
|
363
|
+
# Mark as seen lists that are part of another list
|
364
|
+
@lists.values.map(&:statements).
|
365
|
+
flatten.each do |st|
|
366
|
+
seen[st.object] if @lists.has_key?(st.object)
|
367
|
+
end
|
368
|
+
|
369
|
+
# List elements should not be targets for top-level serialization
|
370
|
+
list_elements = @lists.values.map(&:to_a).flatten.compact
|
371
|
+
|
352
372
|
# Sort subjects by resources over bnodes, ref_counts and the subject URI itself
|
353
|
-
recursable = @subjects.keys.
|
373
|
+
recursable = (@subjects.keys - list_elements).
|
354
374
|
select {|s| !seen.include?(s)}.
|
355
375
|
map {|r| [r.node? ? 1 : 0, ref_count(r), r]}.
|
356
376
|
sort
|
357
|
-
|
358
|
-
subjects
|
377
|
+
|
378
|
+
subjects + recursable.map{|r| r.last}
|
359
379
|
end
|
360
|
-
|
380
|
+
|
361
381
|
# Perform any preprocessing of statements required
|
362
382
|
def preprocess
|
363
383
|
# Load defined prefixes
|
@@ -375,14 +395,27 @@ module RDF::Turtle
|
|
375
395
|
@graph.each {|statement| preprocess_statement(statement)}
|
376
396
|
end
|
377
397
|
end
|
378
|
-
|
398
|
+
|
379
399
|
# Perform any statement preprocessing required. This is used to perform reference counts and determine required
|
380
400
|
# prefixes.
|
381
401
|
# @param [Statement] statement
|
382
402
|
def preprocess_statement(statement)
|
383
403
|
#log_debug("preprocess") {statement.to_ntriples}
|
384
404
|
bump_reference(statement.object)
|
385
|
-
|
405
|
+
# Count properties of this subject
|
406
|
+
(@subjects[statement.subject] ||= {})[statement.predicate] ||= 0
|
407
|
+
@subjects[statement.subject][statement.predicate] += 1
|
408
|
+
|
409
|
+
# Collect lists
|
410
|
+
if statement.predicate == RDF.first
|
411
|
+
l = RDF::List.new(subject: statement.subject, graph: graph)
|
412
|
+
@lists[statement.subject] = l if l.valid?
|
413
|
+
end
|
414
|
+
|
415
|
+
if statement.object == RDF.nil || statement.subject == RDF.nil
|
416
|
+
# Add an entry for the list tail
|
417
|
+
@lists[RDF.nil] ||= RDF::List[]
|
418
|
+
end
|
386
419
|
|
387
420
|
# Pre-fetch pnames, to fill prefixes
|
388
421
|
get_pname(statement.subject)
|
@@ -401,6 +434,7 @@ module RDF::Turtle
|
|
401
434
|
# Reset internal helper instance variables
|
402
435
|
def reset
|
403
436
|
@lists = {}
|
437
|
+
|
404
438
|
@references = {}
|
405
439
|
@serialized = {}
|
406
440
|
@subjects = {}
|
@@ -425,13 +459,13 @@ module RDF::Turtle
|
|
425
459
|
# Checks if l is a valid RDF list, i.e. no nodes have other properties.
|
426
460
|
def is_valid_list?(l)
|
427
461
|
#log_debug("is_valid_list?") {l.inspect}
|
428
|
-
return
|
462
|
+
return @lists[l] && @lists[l].valid?
|
429
463
|
end
|
430
|
-
|
431
|
-
def do_list(l)
|
432
|
-
list =
|
464
|
+
|
465
|
+
def do_list(l, position)
|
466
|
+
list = @lists[l]
|
433
467
|
log_debug("do_list") {list.inspect}
|
434
|
-
|
468
|
+
subject_done(RDF.nil)
|
435
469
|
list.each_statement do |st|
|
436
470
|
next unless st.predicate == RDF.first
|
437
471
|
log_debug {" list this: #{st.subject} first: #{st.object}[#{position}]"}
|
@@ -443,38 +477,38 @@ module RDF::Turtle
|
|
443
477
|
|
444
478
|
def collection(node, position)
|
445
479
|
return false if !is_valid_list?(node)
|
480
|
+
return false if position == :subject && ref_count(node) > 0
|
481
|
+
return false if position == :object && non_list_prop_count(node) > 0
|
446
482
|
#log_debug("collection") {"#{node.to_ntriples}, #{position}"}
|
447
483
|
|
448
484
|
@output.write(position == :subject ? "(" : " (")
|
449
|
-
log_depth {do_list(node)}
|
485
|
+
log_depth {do_list(node, position)}
|
450
486
|
@output.write(')')
|
451
487
|
end
|
452
488
|
|
453
|
-
# Can
|
454
|
-
def
|
489
|
+
# Can subject be represented as a blankNodePropertyList?
|
490
|
+
def blankNodePropertyList?(resource, position)
|
455
491
|
resource.node? &&
|
456
|
-
|
457
|
-
|
492
|
+
!is_valid_list?(resource) &&
|
493
|
+
(!is_done?(resource) || position == :subject) &&
|
494
|
+
ref_count(resource) == (position == :object ? 1 : 0)
|
458
495
|
end
|
459
496
|
|
460
|
-
# Represent
|
461
|
-
def
|
462
|
-
return false unless
|
497
|
+
# Represent resource as a blankNodePropertyList
|
498
|
+
def blankNodePropertyList(resource, position)
|
499
|
+
return false unless blankNodePropertyList?(resource, position)
|
463
500
|
|
464
|
-
|
501
|
+
log_debug("blankNodePropertyList") {resource.to_ntriples}
|
465
502
|
subject_done(resource)
|
466
|
-
@output.write(position == :subject ?
|
467
|
-
log_depth
|
468
|
-
|
469
|
-
@output.write(num_props > 1 ? "\n#{indent} ]" : "]")
|
470
|
-
end
|
471
|
-
|
503
|
+
@output.write(position == :subject ? "\n#{indent} [" : ' [')
|
504
|
+
num_props = log_depth {predicateObjectList(resource, true)}
|
505
|
+
@output.write((num_props > 1 ? "\n#{indent}" : "") + (position == :object ? ']' : '] .'))
|
472
506
|
true
|
473
507
|
end
|
474
508
|
|
475
509
|
# Default singular resource representation.
|
476
|
-
def
|
477
|
-
#log_debug("
|
510
|
+
def p_term(resource, position)
|
511
|
+
#log_debug("p_term") {"#{resource.to_ntriples}, #{position}"}
|
478
512
|
l = (position == :subject ? "" : " ") + format_term(resource, options)
|
479
513
|
@output.write(l)
|
480
514
|
end
|
@@ -486,12 +520,15 @@ module RDF::Turtle
|
|
486
520
|
"#{resource.to_ntriples}, " +
|
487
521
|
"pos: #{position}, " +
|
488
522
|
"()?: #{is_valid_list?(resource)}, " +
|
489
|
-
"[]?: #{
|
523
|
+
"[]?: #{blankNodePropertyList?(resource, position)}, " +
|
490
524
|
"rc: #{ref_count(resource)}"
|
491
525
|
end
|
492
|
-
raise RDF::WriterError, "Cannot serialize resource '#{resource}'" unless
|
526
|
+
raise RDF::WriterError, "Cannot serialize resource '#{resource}'" unless
|
527
|
+
collection(resource, position) ||
|
528
|
+
blankNodePropertyList(resource, position) ||
|
529
|
+
p_term(resource, position)
|
493
530
|
end
|
494
|
-
|
531
|
+
|
495
532
|
def predicate(resource)
|
496
533
|
log_debug("predicate") {resource.to_ntriples}
|
497
534
|
if resource == RDF.type
|
@@ -507,7 +544,7 @@ module RDF::Turtle
|
|
507
544
|
return if objects.empty?
|
508
545
|
|
509
546
|
objects.each_with_index do |obj, i|
|
510
|
-
if i > 0 &&
|
547
|
+
if i > 0 && blankNodePropertyList?(obj, :object)
|
511
548
|
@output.write ", "
|
512
549
|
elsif i > 0
|
513
550
|
@output.write ",\n#{indent(4)}"
|
@@ -524,7 +561,8 @@ module RDF::Turtle
|
|
524
561
|
(properties[st.predicate.to_s] ||= []) << st.object
|
525
562
|
end
|
526
563
|
|
527
|
-
prop_list = sort_properties(properties)
|
564
|
+
prop_list = sort_properties(properties)
|
565
|
+
prop_list -= [RDF.first.to_s, RDF.rest.to_s] if @lists.include?(subject)
|
528
566
|
log_debug("predicateObjectList") {prop_list.inspect}
|
529
567
|
return 0 if prop_list.empty?
|
530
568
|
|
@@ -539,22 +577,6 @@ module RDF::Turtle
|
|
539
577
|
properties.keys.length
|
540
578
|
end
|
541
579
|
|
542
|
-
# Can subject be represented as a blankNodePropertyList?
|
543
|
-
def blankNodePropertyList?(subject)
|
544
|
-
ref_count(subject) == 0 && subject.node? && !is_valid_list?(subject)
|
545
|
-
end
|
546
|
-
|
547
|
-
# Represent subject as a blankNodePropertyList?
|
548
|
-
def blankNodePropertyList(subject)
|
549
|
-
return false unless blankNodePropertyList?(subject)
|
550
|
-
|
551
|
-
log_debug("blankNodePropertyList") {subject.to_ntriples}
|
552
|
-
@output.write("\n#{indent} [")
|
553
|
-
num_props = log_depth {predicateObjectList(subject, true)}
|
554
|
-
@output.write(num_props > 1 ? "\n#{indent} ] ." : "] .")
|
555
|
-
true
|
556
|
-
end
|
557
|
-
|
558
580
|
# Render triples having the same subject using an explicit subject
|
559
581
|
def triples(subject)
|
560
582
|
@output.write("\n#{indent}")
|
@@ -563,18 +585,28 @@ module RDF::Turtle
|
|
563
585
|
@output.write(" .")
|
564
586
|
true
|
565
587
|
end
|
566
|
-
|
588
|
+
|
567
589
|
def statement(subject)
|
568
|
-
log_debug("statement") {"#{subject.to_ntriples}, bnodePL?: #{blankNodePropertyList?(subject)}"}
|
590
|
+
log_debug("statement") {"#{subject.to_ntriples}, bnodePL?: #{blankNodePropertyList?(subject, :subject)}"}
|
569
591
|
subject_done(subject)
|
570
|
-
blankNodePropertyList(subject) || triples(subject)
|
592
|
+
blankNodePropertyList(subject, :subject) || triples(subject)
|
571
593
|
@output.puts
|
572
594
|
end
|
573
|
-
|
574
|
-
|
575
|
-
|
595
|
+
|
596
|
+
# Return the number of statements having this resource as a subject
|
597
|
+
# @return [Integer]
|
598
|
+
def prop_count(subject)
|
599
|
+
@subjects.fetch(subject, {}).values.reduce(:+) || 0
|
600
|
+
end
|
601
|
+
|
602
|
+
# Return the number of statements having this resource as a subject other than for list properties
|
603
|
+
# @return [Integer]
|
604
|
+
def non_list_prop_count(subject)
|
605
|
+
@subjects.fetch(subject, {}).
|
606
|
+
reject {|k, v| [RDF.type, RDF.first, RDF.rest].include?(k)}.
|
607
|
+
values.reduce(:+) || 0
|
576
608
|
end
|
577
|
-
|
609
|
+
|
578
610
|
# Return the number of times this node has been referenced in the object position
|
579
611
|
# @return [Integer]
|
580
612
|
def ref_count(resource)
|
@@ -587,7 +619,11 @@ module RDF::Turtle
|
|
587
619
|
def bump_reference(resource)
|
588
620
|
@references[resource] = ref_count(resource) + 1
|
589
621
|
end
|
590
|
-
|
622
|
+
|
623
|
+
def is_done?(subject)
|
624
|
+
@serialized.include?(subject)
|
625
|
+
end
|
626
|
+
|
591
627
|
# Mark a subject as done.
|
592
628
|
def subject_done(subject)
|
593
629
|
@serialized[subject] = true
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdf-turtle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregg Kellogg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdf
|
@@ -195,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
195
195
|
version: '0'
|
196
196
|
requirements: []
|
197
197
|
rubyforge_project:
|
198
|
-
rubygems_version: 2.7.
|
198
|
+
rubygems_version: 2.7.6
|
199
199
|
signing_key:
|
200
200
|
specification_version: 4
|
201
201
|
summary: Turtle reader/writer for Ruby.
|