rdf-turtle 3.0.1 → 3.0.2

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/rdf/turtle/writer.rb +109 -73
  4. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07b298640314a7317b9061aafd7c38941bbff437c19920992c82a275ef458d31
4
- data.tar.gz: 97e9132f36a0aaa818fd7749f96e78858e1b953be20c26a463cbe1262f3aaccd
3
+ metadata.gz: 1a8fb1c87f5b5149cc3430a1cdac50fdd7bfbaeacfeaa1a6770e54e83a1aff09
4
+ data.tar.gz: 7bf9532ca0c78ce4e24e51ef3a4076a8563e2f0fa2e9f9853616f47776f0947f
5
5
  SHA512:
6
- metadata.gz: 15da972d14df0a76b5e82095bfb4a7c2a8d9f2f019d20bb35c53de0a4ec2638586d583039ea0d426ffeed724026366537829b789201c2fdd37c47b56881de637
7
- data.tar.gz: 7c3a9f79b5b3ee5fdd8fb712c055846f4af1c5a94feac8d51865015f786d21e72d03820a72f64a3a327535d6e3ec20b4d28d178ec21de8685a17e5a08c9a5602
6
+ metadata.gz: 55ec7f72b8b020506c134805828ff43341ce455b469edfb396332c61b67ae3cc7bdf2a0dcfcb3808c47201c77564e046a84d0aaaba2a74553fd2d1c30c93a184
7
+ data.tar.gz: 308390211f1da6158fe12d322d8dec7e24ce94931ccd994fb202bf9ac5f38665ac3f1f2e7c02ae9a20e8f3b775462e8831b1357a96be045f8f427d5d241dbf84
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.1
1
+ 3.0.2
@@ -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 {|u| u.length}.reverse.detect {|u| uri.index(u.to_s) == 0}
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).map {|st| st.subject}.sort.uniq.each do |subject|
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 += recursable.map{|r| r.last}
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
- @subjects[statement.subject] = true
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 RDF::List.new(subject: l, graph: @graph).valid?
462
+ return @lists[l] && @lists[l].valid?
429
463
  end
430
-
431
- def do_list(l)
432
- list = RDF::List.new(subject: l, graph: @graph)
464
+
465
+ def do_list(l, position)
466
+ list = @lists[l]
433
467
  log_debug("do_list") {list.inspect}
434
- position = :subject
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 object be represented using a blankNodePropertyList?
454
- def p_squared?(resource, position)
489
+ # Can subject be represented as a blankNodePropertyList?
490
+ def blankNodePropertyList?(resource, position)
455
491
  resource.node? &&
456
- !@serialized.has_key?(resource) &&
457
- ref_count(resource) <= 1
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 an object as a blankNodePropertyList
461
- def p_squared(resource, position)
462
- return false unless p_squared?(resource, position)
497
+ # Represent resource as a blankNodePropertyList
498
+ def blankNodePropertyList(resource, position)
499
+ return false unless blankNodePropertyList?(resource, position)
463
500
 
464
- #log_debug("p_squared") {"#{resource.to_ntriples}, #{position}"}
501
+ log_debug("blankNodePropertyList") {resource.to_ntriples}
465
502
  subject_done(resource)
466
- @output.write(position == :subject ? '[' : ' [')
467
- log_depth do
468
- num_props = predicateObjectList(resource, true)
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 p_default(resource, position)
477
- #log_debug("p_default") {"#{resource.to_ntriples}, #{position}"}
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
- "[]?: #{p_squared?(resource, position)}, " +
523
+ "[]?: #{blankNodePropertyList?(resource, position)}, " +
490
524
  "rc: #{ref_count(resource)}"
491
525
  end
492
- raise RDF::WriterError, "Cannot serialize resource '#{resource}'" unless collection(resource, position) || p_squared(resource, position) || p_default(resource, position)
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 && p_squared?(obj, :object)
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) - [RDF.first.to_s, RDF.rest.to_s]
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
- def is_done?(subject)
575
- @serialized.include?(subject)
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.1
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-01-14 00:00:00.000000000 Z
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.3
198
+ rubygems_version: 2.7.6
199
199
  signing_key:
200
200
  specification_version: 4
201
201
  summary: Turtle reader/writer for Ruby.