rdf-turtle 3.1.2 → 3.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cab6a95280c9ece9d97ed7bde134044c247d0990cd9451772b3726a85c1dafce
4
- data.tar.gz: 56c87a3f41a22ffae491aed4197261e204332541855777f1822bd24d965047f0
3
+ metadata.gz: 57d92c642076069a911db896415fa9dbd3203b6c82a9074e0fc8a53dafb2389d
4
+ data.tar.gz: 238620d914ce1ff78d8710e12083d03eacb5f9be7106d0d12ef15c5105445b72
5
5
  SHA512:
6
- metadata.gz: c5ce46d746a7ed5ac19c3b8fde60934485781149302d6b02e64d1b45dd70a6402c46ba8ca9e7eb06c3d96030c93c26646751bc92155dc239dbc41179d9c72c49
7
- data.tar.gz: ad9a7a6ea210992f5a0ddbd037ded7b7f0a0129e4fc4a5610ad03f313381354207394840c9c729c70192906dfac58294c216a62382316b909f79a34c3db9d172
6
+ metadata.gz: 0db24a8c173d7b522ba06e1af9e957c513a551a211d22b6a2d19b7218a79e236f6db1f66b738a1a76e5b96ef6161f432f7ece17dea4af8d3de46520960a89977
7
+ data.tar.gz: a2826f9e8fbd34364acc54a117e283067982491e9c8e736ac1c8fcb86aac1f6a3acc43811040242fea19e79019d809fd14dae6b46ecc0568b67775dddd84369d
data/README.md CHANGED
@@ -3,8 +3,9 @@
3
3
  [Turtle][] reader/writer for [RDF.rb][RDF.rb] .
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/rdf-turtle.png)](https://badge.fury.io/rb/rdf-turtle)
6
- [![Build Status](https://travis-ci.org/ruby-rdf/rdf-turtle.png?branch=master)](https://travis-ci.org/ruby-rdf/rdf-turtle)
7
- [![Coverage Status](https://coveralls.io/repos/ruby-rdf/rdf-turtle/badge.svg)](https://coveralls.io/r/ruby-rdf/rdf-turtle)
6
+ [![Build Status](https://github.com/ruby-rdf/rdf-turtle/workflows/CI/badge.svg?branch=develop)](https://github.com/ruby-rdf/rdf-turtle/actions?query=workflow%3ACI)
7
+ [![Coverage Status](https://coveralls.io/repos/ruby-rdf/rdf-turtle/badge.svg?branch=develop)](https://coveralls.io/github/ruby-rdf/rdf-turtle?branch=develop)
8
+ [![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf)
8
9
 
9
10
  ## Description
10
11
  This is a [Ruby][] implementation of a [Turtle][] parser for [RDF.rb][].
@@ -16,9 +17,9 @@ Install with `gem install rdf-turtle`
16
17
 
17
18
  * 100% free and unencumbered [public domain](https://unlicense.org/) software.
18
19
  * Implements a complete parser for [Turtle][].
19
- * Compatible with Ruby >= 2.2.2.
20
+ * Compatible with Ruby >= 2.6.
20
21
  * Optional streaming writer, to serialize large graphs
21
- * Provisional support for [Turtle*][RDF*].
22
+ * Provisional support for [Turtle-star][RDF-star].
22
23
 
23
24
  ## Usage
24
25
  Instantiate a reader from a local file:
@@ -35,9 +36,9 @@ Write a graph to a file:
35
36
  writer << graph
36
37
  end
37
38
 
38
- ## Turtle* (RDFStar)
39
+ ## Turtle-star (RDF-star)
39
40
 
40
- Both reader and writer include provisional support for [Turtle*][RDF*].
41
+ Both reader and writer include provisional support for [Turtle-star][RDF-star].
41
42
 
42
43
  Internally, an `RDF::Statement` is treated as another resource, along with `RDF::URI` and `RDF::Node`, which allows an `RDF::Statement` to have a `#subject` or `#object` which is also an `RDF::Statement`.
43
44
 
@@ -65,19 +66,29 @@ By default, the Turtle reader will reject a document containing a subject resour
65
66
  end
66
67
  # => RDF::ReaderError
67
68
 
68
- Readers support a `rdfstar` option with either `:PG` (Property Graph) or `:SA` (Separate Assertions) modes. In `:PG` mode, statements that are used in the subject or object positions are also implicitly added to the graph:
69
+ Readers support a boolean valued `rdfstar` option; only one statement is asserted, although the reified statement is contained within the graph.
69
70
 
70
71
  graph = RDF::Graph.new do |graph|
71
- RDF::Turtle::Reader.new(ttl, rdfstar: :PG) {|reader| graph << reader}
72
+ RDF::Turtle::Reader.new(ttl, rdfstar: true) {|reader| graph << reader}
72
73
  end
73
- graph.count #=> 2
74
+ graph.count #=> 1
75
+
76
+ ### Reading a Graph containing statement annotations
74
77
 
75
- When using the `:SA` mode, only one statement is asserted, although the reified statement is contained within the graph.
78
+ Annotations are introduced using the `{| ... |}` syntax, which is treated like a `blankNodePropertyList`,
79
+ where the subject is the the triple ending with that annotation.
76
80
 
81
+ ttl = %(
82
+ @prefix foaf: <http://xmlns.com/foaf/0.1/> .
83
+ @prefix ex: <http://example.com/> .
84
+ <bob> foaf:age 23 {| ex:certainty 9.0e-1 |} .
85
+ )
77
86
  graph = RDF::Graph.new do |graph|
78
- RDF::Turtle::Reader.new(ttl, rdfstar: :SA) {|reader| graph << reader}
87
+ RDF::Turtle::Reader.new(ttl) {|reader| graph << reader}
79
88
  end
80
- graph.count #=> 1
89
+ # => RDF::ReaderError
90
+
91
+ Note that this requires the `rdfstar` option to be set.
81
92
 
82
93
  ## Documentation
83
94
  Full documentation available on [Rubydoc.info][Turtle doc]
@@ -129,9 +140,9 @@ This version uses a hand-written parser using the Lexer from the [EBNF][] gem in
129
140
 
130
141
  ## Dependencies
131
142
 
132
- * [Ruby](https://ruby-lang.org/) (>= 2.4)
133
- * [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.1)
134
- * [EBNF][] (~> 1.1)
143
+ * [Ruby](https://ruby-lang.org/) (>= 2.6)
144
+ * [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.2)
145
+ * [EBNF][] (~> 1.2)
135
146
 
136
147
  ## Installation
137
148
 
@@ -159,7 +170,9 @@ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange develo
159
170
  list in the the `README`. Alphabetical order applies.
160
171
  * Do note that in order for us to merge any non-trivial changes (as a rule
161
172
  of thumb, additions larger than about 15 lines of code), we need an
162
- explicit [public domain dedication][PDD] on record from you.
173
+ explicit [public domain dedication][PDD] on record from you,
174
+ which you will be asked to agree to on the first commit to a repo within the organization.
175
+ Note that the agreement applies to all repos in the [Ruby RDF](https://github.com/ruby-rdf/) organization.
163
176
 
164
177
  ## License
165
178
  This is free and unencumbered public domain software. For more information,
@@ -171,13 +184,13 @@ A copy of the [Turtle EBNF][] and derived parser files are included in the repos
171
184
  [RDF]: https://www.w3.org/RDF/
172
185
  [YARD]: https://yardoc.org/
173
186
  [YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
174
- [PDD]: https://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html
175
- [RDF.rb]: https://rubydoc.info/github/ruby-rdf/rdf
187
+ [PDD]: https://unlicense.org/#unlicensing-contributions
188
+ [RDF.rb]: https://ruby-rdf.github.io/rdf
176
189
  [EBNF]: https://rubygems.org/gems/ebnf
177
190
  [Backports]: https://rubygems.org/gems/backports
178
191
  [N-Triples]: https://www.w3.org/TR/rdf-testcases/#ntriples
179
192
  [Turtle]: https://www.w3.org/TR/2012/WD-turtle-20120710/
180
- [RDF*]: https://lists.w3.org/Archives/Public/public-rdf-star/
181
- [Turtle doc]: https://rubydoc.info/github/ruby-rdf/rdf-turtle/master/file/README.md
193
+ [RDF-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html
194
+ [Turtle doc]: https://ruby-rdf.github.io/rdf-turtle/master/file/README.md
182
195
  [Turtle EBNF]: https://dvcs.w3.org/hg/rdf/file/default/rdf-turtle/turtle.bnf
183
196
  [Freebase Dumps]: https://developers.google.com/freebase/data
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.1.2
1
+ 3.2.1
@@ -18,6 +18,7 @@ module RDF::Turtle
18
18
  class Format < RDF::Format
19
19
  content_type 'text/turtle',
20
20
  extension: :ttl,
21
+ uri: 'http://www.w3.org/ns/formats/Turtle',
21
22
  aliases: %w(
22
23
  text/rdf+turtle
23
24
  application/turtle;q=0.2
@@ -25,7 +25,14 @@ module RDF::Turtle
25
25
  terminal(:STRING_LITERAL_SINGLE_QUOTE, STRING_LITERAL_SINGLE_QUOTE, unescape: true)
26
26
 
27
27
  # String terminals
28
- terminal(nil, %r([\(\),.;\[\]Aa]|\^\^|true|false|<<|>>))
28
+ terminal(nil, %r(
29
+ [\(\),.;\[\]Aa]
30
+ | \^\^
31
+ | \{\|
32
+ | \|\}
33
+ | true|false
34
+ | <<|>>
35
+ )x)
29
36
 
30
37
  terminal(:PREFIX, PREFIX)
31
38
  terminal(:BASE, BASE)
@@ -33,7 +40,7 @@ module RDF::Turtle
33
40
 
34
41
  ##
35
42
  # Reader options
36
- # @see https://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Reader#options-class_method
43
+ # @see https://ruby-rdf.github.io/rdf/RDF/Reader#options-class_method
37
44
  def self.options
38
45
  super + [
39
46
  RDF::CLI::Option.new(
@@ -92,11 +99,11 @@ module RDF::Turtle
92
99
  @prod_stack = []
93
100
 
94
101
  @options[:base_uri] = RDF::URI(base_uri || "")
95
- log_debug("base IRI") {base_uri.inspect}
102
+ debug("base IRI") {base_uri.inspect}
96
103
 
97
- log_debug("validate") {validate?.inspect}
98
- log_debug("canonicalize") {canonicalize?.inspect}
99
- log_debug("intern") {intern?.inspect}
104
+ debug("validate") {validate?.inspect}
105
+ debug("canonicalize") {canonicalize?.inspect}
106
+ debug("intern") {intern?.inspect}
100
107
 
101
108
  @lexer = EBNF::LL1::Lexer.new(input, self.class.patterns, **@options)
102
109
 
@@ -185,7 +192,7 @@ module RDF::Turtle
185
192
 
186
193
  # Create a literal
187
194
  def literal(value, **options)
188
- log_debug("literal", depth: @options[:depth]) do
195
+ debug("literal", depth: @options[:depth]) do
189
196
  "value: #{value.inspect}, " +
190
197
  "options: #{options.inspect}, " +
191
198
  "validate: #{validate?.inspect}, " +
@@ -221,7 +228,7 @@ module RDF::Turtle
221
228
  ''
222
229
  end
223
230
  suffix = suffix.to_s.sub(/^\#/, "") if base.index("#")
224
- log_debug("pname", depth: options[:depth]) {"base: '#{base}', suffix: '#{suffix}'"}
231
+ debug("pname", depth: options[:depth]) {"base: '#{base}', suffix: '#{suffix}'"}
225
232
  process_iri(base + suffix.to_s)
226
233
  end
227
234
 
@@ -283,7 +290,7 @@ module RDF::Turtle
283
290
  terminated = token.value == '@prefix'
284
291
  error("Expected PNAME_NS", production: :prefix, token: pfx) unless pfx === :PNAME_NS
285
292
  error("Expected IRIREF", production: :prefix, token: iri) unless iri === :IRIREF
286
- log_debug("prefixID", depth: options[:depth]) {"Defined prefix #{pfx.inspect} mapping to #{iri.inspect}"}
293
+ debug("prefixID", depth: options[:depth]) {"Defined prefix #{pfx.inspect} mapping to #{iri.inspect}"}
287
294
  prefix(pfx.value[0..-2], process_iri(iri))
288
295
  error("prefixId", "#{token} should be downcased") if token.value.start_with?('@') && token.value != '@prefix'
289
296
 
@@ -340,6 +347,10 @@ module RDF::Turtle
340
347
  last_object = nil
341
348
  while object = prod(:_objectList_2) {read_object(subject, predicate)}
342
349
  last_object = object
350
+
351
+ # If object is followed by an annotation, read that and also emit an embedded triple.
352
+ read_annotation(subject, predicate, object)
353
+
343
354
  break unless @lexer.first === ','
344
355
  @lexer.shift while @lexer.first === ','
345
356
  end
@@ -362,7 +373,7 @@ module RDF::Turtle
362
373
  read_iri ||
363
374
  read_BlankNode ||
364
375
  read_collection ||
365
- read_rdfstar ||
376
+ read_quotedTriple ||
366
377
  error( "Expected subject", production: :subject, token: @lexer.first)
367
378
  end
368
379
  end
@@ -375,7 +386,7 @@ module RDF::Turtle
375
386
  read_collection ||
376
387
  read_blankNodePropertyList ||
377
388
  read_literal ||
378
- read_rdfstar
389
+ read_quotedTriple
379
390
 
380
391
  add_statement(:object, RDF::Statement(subject, predicate, object)) if subject && predicate
381
392
  object
@@ -383,35 +394,71 @@ module RDF::Turtle
383
394
  end
384
395
  end
385
396
 
386
- # Read an RDF* reified statement
397
+ # Read an RDF-star reified statement
387
398
  # @return [RDF::Statement]
388
- def read_rdfstar
399
+ def read_quotedTriple
389
400
  return unless @options[:rdfstar]
390
401
  if @lexer.first.value == '<<'
391
- prod(:rdfstar) do
402
+ prod(:quotedTriple) do
392
403
  @lexer.shift # eat <<
393
- subject = read_subject || error("Failed to parse subject", production: :rdfstar, token: @lexer.first)
394
- predicate = read_verb || error("Failed to parse predicate", production: :rdfstar, token: @lexer.first)
395
- object = read_object || error("Failed to parse object", production: :rdfstar, token: @lexer.first)
404
+ subject = read_qtSubject || error("Failed to parse subject", production: :quotedTriple, token: @lexer.first)
405
+ predicate = read_verb || error("Failed to parse predicate", production: :quotedTriple, token: @lexer.first)
406
+ object = read_qtObject || error("Failed to parse object", production: :quotedTriple, token: @lexer.first)
396
407
  unless @lexer.first.value == '>>'
397
- error("Failed to end of embedded triple", production: :rdfstar, token: @lexer.first)
408
+ error("Failed to end of embedded triple", production: :quotedTriple, token: @lexer.first)
398
409
  end
399
410
  @lexer.shift
400
- statement = RDF::Statement(subject, predicate, object)
401
- # Emit the statement if in Property Graph mode
402
- add_statement(:rdfstar, statement) if @options[:rdfstar] == :PG
411
+ statement = RDF::Statement(subject, predicate, object, quoted: true)
403
412
  statement
404
413
  end
405
414
  end
406
415
  end
407
416
 
417
+ # @return [RDF::Resource]
418
+ def read_qtSubject
419
+ prod(:qtSubject) do
420
+ read_iri ||
421
+ read_BlankNode ||
422
+ read_quotedTriple ||
423
+ error( "Expected embedded subject", production: :qtSubject, token: @lexer.first)
424
+ end
425
+ end
426
+
427
+ # @return [RDF::Term]
428
+ def read_qtObject(subject = nil, predicate = nil)
429
+ prod(:qtObject) do
430
+ read_iri ||
431
+ read_BlankNode ||
432
+ read_literal ||
433
+ read_quotedTriple
434
+ end
435
+ end
436
+
437
+ # Read an annotation on a triple
438
+ def read_annotation(subject, predicate, object)
439
+ error("Unexpected end of file", production: :annotation) unless token = @lexer.first
440
+ if token === '{|'
441
+ prod(:annotation, %(|})) do
442
+ @lexer.shift
443
+
444
+ # Statement becomes subject for predicateObjectList
445
+ statement = RDF::Statement(subject, predicate, object, quoted: true)
446
+ read_predicateObjectList(statement) ||
447
+ error("Expected predicateObjectList", production: :annotation, token: @lexer.first)
448
+ error("annotation", "Expected closing '|}'") unless @lexer.first === '|}'
449
+ @lexer.shift
450
+ end
451
+ end
452
+
453
+ end
454
+
408
455
  # @return [RDF::Literal]
409
456
  def read_literal
410
457
  error("Unexpected end of file", production: :literal) unless token = @lexer.first
411
458
  case token.type || token.value
412
459
  when :INTEGER then prod(:literal) {literal(@lexer.shift.value, datatype: RDF::XSD.integer)}
413
460
  when :DECIMAL
414
- prod(:litearl) do
461
+ prod(:literal) do
415
462
  value = @lexer.shift.value
416
463
  value = "0#{value}" if value.start_with?(".")
417
464
  literal(value, datatype: RDF::XSD.decimal)
@@ -455,7 +502,7 @@ module RDF::Turtle
455
502
  if token === '['
456
503
  prod(:blankNodePropertyList, %{]}) do
457
504
  @lexer.shift
458
- log_info("blankNodePropertyList", depth: options[:depth]) {"token: #{token.inspect}"}
505
+ progress("blankNodePropertyList", depth: options[:depth]) {"token: #{token.inspect}"}
459
506
  node = bnode
460
507
  read_predicateObjectList(node)
461
508
  error("blankNodePropertyList", "Expected closing ']'") unless @lexer.first === ']'
@@ -471,7 +518,7 @@ module RDF::Turtle
471
518
  prod(:collection, %{)}) do
472
519
  @lexer.shift
473
520
  token = @lexer.first
474
- log_info("collection", depth: options[:depth]) {"token: #{token.inspect}"}
521
+ progress("collection", depth: options[:depth]) {"token: #{token.inspect}"}
475
522
  objects = []
476
523
  while object = read_object
477
524
  objects << object
@@ -508,7 +555,7 @@ module RDF::Turtle
508
555
  def prod(production, recover_to = [])
509
556
  @prod_stack << {prod: production, recover_to: recover_to}
510
557
  @options[:depth] += 1
511
- log_recover("#{production}(start)", depth: options[:depth]) {"token: #{@lexer.first.inspect}"}
558
+ recover("#{production}(start)", depth: options[:depth], token: @lexer.first)
512
559
  yield
513
560
  rescue EBNF::LL1::Lexer::Error, SyntaxError, Recovery => e
514
561
  # Lexer encountered an illegal token or the parser encountered
@@ -528,13 +575,13 @@ module RDF::Turtle
528
575
  end
529
576
  end
530
577
  raise EOFError, "End of input found when recovering" if @lexer.first.nil?
531
- log_debug("recovery", "current token: #{@lexer.first.inspect}", depth: options[:depth])
578
+ debug("recovery", "current token: #{@lexer.first.inspect}", depth: options[:depth])
532
579
 
533
580
  unless e.is_a?(Recovery)
534
581
  # Get the list of follows for this sequence, this production and the stacked productions.
535
- log_debug("recovery", "stack follows:", depth: options[:depth])
582
+ debug("recovery", "stack follows:", depth: options[:depth])
536
583
  @prod_stack.reverse.each do |prod|
537
- log_debug("recovery", level: 4, depth: options[:depth]) {" #{prod[:prod]}: #{prod[:recover_to].inspect}"}
584
+ debug("recovery", level: 4, depth: options[:depth]) {" #{prod[:prod]}: #{prod[:recover_to].inspect}"}
538
585
  end
539
586
  end
540
587
 
@@ -544,9 +591,9 @@ module RDF::Turtle
544
591
  # Skip tokens until one is found in follows
545
592
  while (token = (@lexer.first rescue @lexer.recover)) && follows.none? {|t| token === t}
546
593
  skipped = @lexer.shift
547
- log_debug("recovery", depth: options[:depth]) {"skip #{skipped.inspect}"}
594
+ debug("recovery", depth: options[:depth]) {"skip #{skipped.inspect}"}
548
595
  end
549
- log_debug("recovery", depth: options[:depth]) {"found #{token.inspect} in follows"}
596
+ debug("recovery", depth: options[:depth]) {"found #{token.inspect} in follows"}
550
597
 
551
598
  # Re-raise the error unless token is a follows of this production
552
599
  raise Recovery unless Array(recover_to).any? {|t| token === t}
@@ -554,11 +601,35 @@ module RDF::Turtle
554
601
  # Skip that token to get something reasonable to start the next production with
555
602
  @lexer.shift
556
603
  ensure
557
- log_info("#{production}(finish)", depth: options[:depth])
604
+ progress("#{production}(finish)", depth: options[:depth])
558
605
  @options[:depth] -= 1
559
606
  @prod_stack.pop
560
607
  end
561
608
 
609
+ def progress(*args, &block)
610
+ lineno = (options[:token].lineno if options[:token].respond_to?(:lineno)) || (@lexer && @lexer.lineno)
611
+ opts = args.last.is_a?(Hash) ? args.pop : {}
612
+ opts[:level] ||= 1
613
+ opts[:lineno] ||= lineno
614
+ log_info(*args, **opts, &block)
615
+ end
616
+
617
+ def recover(*args, &block)
618
+ lineno = (options[:token].lineno if options[:token].respond_to?(:lineno)) || (@lexer && @lexer.lineno)
619
+ opts = args.last.is_a?(Hash) ? args.pop : {}
620
+ opts[:level] ||= 1
621
+ opts[:lineno] ||= lineno
622
+ log_recover(*args, **opts, &block)
623
+ end
624
+
625
+ def debug(*args, &block)
626
+ lineno = (options[:token].lineno if options[:token].respond_to?(:lineno)) || (@lexer && @lexer.lineno)
627
+ opts = args.last.is_a?(Hash) ? args.pop : {}
628
+ opts[:level] ||= 0
629
+ opts[:lineno] ||= lineno
630
+ log_debug(*args, **opts, &block)
631
+ end
632
+
562
633
  ##
563
634
  # Error information, used as level `0` debug messages.
564
635
  #
@@ -65,7 +65,7 @@ module RDF::Turtle
65
65
 
66
66
  ##
67
67
  # Writer options
68
- # @see https://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Writer#options-class_method
68
+ # @see https://ruby-rdf.github.io/rdf/RDF/Writer#options-class_method
69
69
  def self.options
70
70
  super + [
71
71
  RDF::CLI::Option.new(
@@ -201,7 +201,7 @@ module RDF::Turtle
201
201
  super
202
202
  end
203
203
 
204
- # Return a QName for the URI, or nil. Adds namespace of QName to defined prefixes
204
+ # Return a PName for the URI, or nil. Adds namespace of PName to defined prefixes
205
205
  # @param [RDF::Resource] resource
206
206
  # @return [String, nil] value to use to identify URI
207
207
  def get_pname(resource)
@@ -223,14 +223,15 @@ module RDF::Turtle
223
223
  unless u.to_s.empty?
224
224
  prefix(prefix, u) unless u.to_s.empty?
225
225
  log_debug("get_pname") {"add prefix #{prefix.inspect} => #{u}"}
226
- uri.sub(u.to_s, "#{prefix}:")
226
+ # Escape suffix, as necessary
227
+ RDF::URI(uri).pname(prefixes: {prefix => u})
227
228
  end
228
229
  when @options[:standard_prefixes] && vocab = RDF::Vocabulary.each.to_a.detect {|v| uri.index(v.to_uri.to_s) == 0}
229
230
  prefix = vocab.__name__.to_s.split('::').last.downcase
230
231
  @uri_to_prefix[vocab.to_uri.to_s] = prefix
231
232
  prefix(prefix, vocab.to_uri) # Define for output
232
233
  log_debug("get_pname") {"add standard prefix #{prefix.inspect} => #{vocab.to_uri}"}
233
- uri.sub(vocab.to_uri.to_s, "#{prefix}:")
234
+ RDF::URI(uri).pname(prefixes: {prefix => vocab.to_uri})
234
235
  else
235
236
  nil
236
237
  end
@@ -276,10 +277,23 @@ module RDF::Turtle
276
277
  case literal
277
278
  when RDF::Literal
278
279
  case @options[:literal_shorthand] && literal.valid? ? literal.datatype : false
279
- when RDF::XSD.boolean, RDF::XSD.integer, RDF::XSD.decimal
280
- literal.canonicalize.to_s
280
+ when RDF::XSD.boolean
281
+ %w(true false).include?(literal.value) ? literal.value : literal.canonicalize.to_s
282
+ when RDF::XSD.integer
283
+ literal.value.match?(/^[\+\-]?\d+$/) && !canonicalize? ? literal.value : literal.canonicalize.to_s
284
+ when RDF::XSD.decimal
285
+ literal.value.match?(/^[\+\-]?\d+\.\d+?$/) && !canonicalize? ?
286
+ literal.value :
287
+ literal.canonicalize.to_s
281
288
  when RDF::XSD.double
282
- literal.canonicalize.to_s.sub('E', 'e') # Favor lower case exponent
289
+ in_form = case literal.value
290
+ when /[\+\-]?\d+\.\d*E[\+\-]?\d+$/i then true
291
+ when /[\+\-]?\.\d+E[\+\-]?\d+$/i then true
292
+ when /[\+\-]?\d+E[\+\-]?\d+$/i then true
293
+ else false
294
+ end && !canonicalize?
295
+
296
+ in_form ? literal.value : literal.canonicalize.to_s.sub('E', 'e').to_s
283
297
  else
284
298
  text = quoted(literal.value)
285
299
  text << "@#{literal.language}" if literal.has_language?
@@ -314,12 +328,12 @@ module RDF::Turtle
314
328
  end
315
329
 
316
330
  ##
317
- # Returns an embedded triples
331
+ # Returns an embedded triple.
318
332
  #
319
333
  # @param [RDF::Statement] statement
320
334
  # @param [Hash{Symbol => Object}] options
321
335
  # @return [String]
322
- def format_rdfstar(statement, **options)
336
+ def format_quotedTriple(statement, **options)
323
337
  log_debug("rdfstar") {"#{statement.to_ntriples}"}
324
338
  "<<%s %s %s>>" % statement.to_a.map { |value| format_term(value, **options) }
325
339
  end
@@ -374,16 +388,16 @@ module RDF::Turtle
374
388
  # Mark as seen lists that are part of another list
375
389
  @lists.values.map(&:statements).
376
390
  flatten.each do |st|
377
- seen[st.object] = true if @lists.has_key?(st.object)
391
+ seen[st.object] = true if @lists.key?(st.object)
378
392
  end
379
393
 
380
- # List elements which are bnodes should not be targets for top-level serialization
381
- list_elements = @lists.values.map(&:to_a).flatten.select(&:node?).compact
394
+ # List elements which are bnodes should not be targets for top-level serialization
395
+ list_elements = @lists.values.map(&:to_a).flatten.select(&:node?).compact
382
396
 
383
- # Sort subjects by resources over bnodes, ref_counts and the subject URI itself
397
+ # Sort subjects by resources and statements over bnodes, ref_counts and the subject URI itself
384
398
  recursable = (@subjects.keys - list_elements).
385
399
  select {|s| !seen.include?(s)}.
386
- map {|r| [r.node? ? 1 : 0, ref_count(r), r]}.
400
+ map {|r| [r.node? ? 2 : (r.statement? ? 1 : 0), ref_count(r), r]}.
387
401
  sort
388
402
 
389
403
  subjects + recursable.map{|r| r.last}
@@ -469,7 +483,7 @@ module RDF::Turtle
469
483
  # Can subject be represented as a blankNodePropertyList?
470
484
  def blankNodePropertyList?(resource, position)
471
485
  !resource.statement? && resource.node? &&
472
- !is_valid_list?(resource) &&
486
+ !collection?(resource) &&
473
487
  (!is_done?(resource) || position == :subject) &&
474
488
  ref_count(resource) == (position == :object ? 1 : 0)
475
489
  end
@@ -507,35 +521,33 @@ module RDF::Turtle
507
521
  private
508
522
 
509
523
  # Checks if l is a valid RDF list, i.e. no nodes have other properties.
510
- def is_valid_list?(l)
511
- #log_debug("is_valid_list?") {l.inspect}
512
- return @lists[l] && @lists[l].valid?
513
- end
514
-
515
- def do_list(l, position)
516
- list = @lists[l]
517
- log_debug("do_list") {list.inspect}
518
- subject_done(RDF.nil)
519
- index = 0
520
- list.each_statement do |st|
521
- next unless st.predicate == RDF.first
522
- log_debug {" list this: #{st.subject} first: #{st.object}[#{position}]"}
523
- @output.write(" ") if index > 0
524
- path(st.object, position)
525
- subject_done(st.subject)
526
- position = :object
527
- index += 1
528
- end
524
+ def collection?(l)
525
+ #log_debug("collection?") {l.inspect}
526
+ return @lists.key?(l)
529
527
  end
530
528
 
531
529
  def collection(node, position)
532
- return false if !is_valid_list?(node)
530
+ return false if !collection?(node)
533
531
  return false if position == :subject && ref_count(node) > 0
534
532
  return false if position == :object && prop_count(node) > 0
535
533
  #log_debug("collection") {"#{node.to_ntriples}, #{position}"}
536
534
 
537
535
  @output.write("(")
538
- log_depth {do_list(node, position)}
536
+ log_depth do
537
+ list = @lists[node]
538
+ log_debug("collection") {list.inspect}
539
+ subject_done(RDF.nil)
540
+ index = 0
541
+ list.each_statement do |st|
542
+ next unless st.predicate == RDF.first
543
+ log_debug {" list this: #{st.subject} first: #{st.object}[#{position}]"}
544
+ @output.write(" ") if index > 0
545
+ path(st.object, position)
546
+ subject_done(st.subject)
547
+ position = :object
548
+ index += 1
549
+ end
550
+ end
539
551
  @output.write(')')
540
552
  end
541
553
 
@@ -568,7 +580,7 @@ module RDF::Turtle
568
580
  log_debug("path") do
569
581
  "#{resource.to_ntriples}, " +
570
582
  "pos: #{position}, " +
571
- "()?: #{is_valid_list?(resource)}, " +
583
+ "()?: #{collection?(resource)}, " +
572
584
  "[]?: #{blankNodePropertyList?(resource, position)}, " +
573
585
  "rc: #{ref_count(resource)}"
574
586
  end
@@ -588,7 +600,7 @@ module RDF::Turtle
588
600
  end
589
601
 
590
602
  # Render an objectList having a common subject and predicate
591
- def objectList(objects)
603
+ def objectList(subject, predicate, objects)
592
604
  log_debug("objectList") {objects.inspect}
593
605
  return if objects.empty?
594
606
 
@@ -599,6 +611,15 @@ module RDF::Turtle
599
611
  @output.write ",\n#{indent(4)}"
600
612
  end
601
613
  path(obj, :object)
614
+
615
+ # If subject, predicate, and object are embedded, write those bits out too.
616
+ emb = RDF::Statement(subject, predicate, obj)
617
+ if !@graph.query({subject: emb}).empty?
618
+ @output.write ' {| '
619
+ predicateObjectList(emb, true)
620
+ @output.write ' |}'
621
+ subject_done(emb)
622
+ end
602
623
  end
603
624
  end
604
625
 
@@ -611,17 +632,18 @@ module RDF::Turtle
611
632
  end
612
633
 
613
634
  prop_list = sort_properties(properties)
614
- prop_list -= [RDF.first.to_s, RDF.rest.to_s] if @lists.include?(subject)
635
+ prop_list -= [RDF.first.to_s, RDF.rest.to_s] if @lists.key?(subject)
615
636
  log_debug("predicateObjectList") {prop_list.inspect}
616
637
  return 0 if prop_list.empty?
617
638
 
618
639
  @output.write("\n#{indent(2)}") if properties.keys.length > 1 && from_bpl
619
640
  prop_list.each_with_index do |prop, i|
620
641
  begin
642
+ pred = RDF::URI.intern(prop)
621
643
  @output.write(";\n#{indent(2)}") if i > 0
622
- predicate(RDF::URI.intern(prop))
644
+ predicate(pred)
623
645
  @output.write(" ")
624
- objectList(properties[prop])
646
+ objectList(subject, pred, properties[prop])
625
647
  end
626
648
  end
627
649
  properties.keys.length
data/lib/rdf/turtle.rb CHANGED
@@ -15,7 +15,7 @@ module RDF
15
15
  # end
16
16
  # end
17
17
  #
18
- # @see https://rubydoc.info/github/ruby-rdf/rdf/master/frames
18
+ # @see https://ruby-rdf.github.io/rdf/master/frames
19
19
  # @see https://dvcs.w3.org/hg/rdf/raw-file/default/rdf-turtle/index.html
20
20
  #
21
21
  # @author [Gregg Kellogg](https://greggkellogg.net/)
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.1.2
4
+ version: 3.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregg Kellogg
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-23 00:00:00.000000000 Z
11
+ date: 2022-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdf
@@ -16,146 +16,140 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.1'
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 3.1.2
19
+ version: '3.2'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: '3.1'
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 3.1.2
26
+ version: '3.2'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: ebnf
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
31
  - - "~>"
38
32
  - !ruby/object:Gem::Version
39
- version: '2.0'
33
+ version: '2.3'
40
34
  type: :runtime
41
35
  prerelease: false
42
36
  version_requirements: !ruby/object:Gem::Requirement
43
37
  requirements:
44
38
  - - "~>"
45
39
  - !ruby/object:Gem::Version
46
- version: '2.0'
40
+ version: '2.3'
47
41
  - !ruby/object:Gem::Dependency
48
- name: rspec
42
+ name: erubis
49
43
  requirement: !ruby/object:Gem::Requirement
50
44
  requirements:
51
45
  - - "~>"
52
46
  - !ruby/object:Gem::Version
53
- version: '3.9'
47
+ version: '2.7'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
51
  requirements:
58
52
  - - "~>"
59
53
  - !ruby/object:Gem::Version
60
- version: '3.9'
54
+ version: '2.7'
61
55
  - !ruby/object:Gem::Dependency
62
- name: rspec-its
56
+ name: htmlentities
63
57
  requirement: !ruby/object:Gem::Requirement
64
58
  requirements:
65
59
  - - "~>"
66
60
  - !ruby/object:Gem::Version
67
- version: '1.3'
61
+ version: '4.3'
68
62
  type: :development
69
63
  prerelease: false
70
64
  version_requirements: !ruby/object:Gem::Requirement
71
65
  requirements:
72
66
  - - "~>"
73
67
  - !ruby/object:Gem::Version
74
- version: '1.3'
68
+ version: '4.3'
75
69
  - !ruby/object:Gem::Dependency
76
- name: rdf-isomorphic
70
+ name: rspec
77
71
  requirement: !ruby/object:Gem::Requirement
78
72
  requirements:
79
73
  - - "~>"
80
74
  - !ruby/object:Gem::Version
81
- version: '3.1'
75
+ version: '3.10'
82
76
  type: :development
83
77
  prerelease: false
84
78
  version_requirements: !ruby/object:Gem::Requirement
85
79
  requirements:
86
80
  - - "~>"
87
81
  - !ruby/object:Gem::Version
88
- version: '3.1'
82
+ version: '3.10'
89
83
  - !ruby/object:Gem::Dependency
90
- name: json-ld
84
+ name: rspec-its
91
85
  requirement: !ruby/object:Gem::Requirement
92
86
  requirements:
93
87
  - - "~>"
94
88
  - !ruby/object:Gem::Version
95
- version: '3.1'
89
+ version: '1.3'
96
90
  type: :development
97
91
  prerelease: false
98
92
  version_requirements: !ruby/object:Gem::Requirement
99
93
  requirements:
100
94
  - - "~>"
101
95
  - !ruby/object:Gem::Version
102
- version: '3.1'
96
+ version: '1.3'
103
97
  - !ruby/object:Gem::Dependency
104
- name: rdf-spec
98
+ name: rdf-isomorphic
105
99
  requirement: !ruby/object:Gem::Requirement
106
100
  requirements:
107
101
  - - "~>"
108
102
  - !ruby/object:Gem::Version
109
- version: '3.1'
103
+ version: '3.2'
110
104
  type: :development
111
105
  prerelease: false
112
106
  version_requirements: !ruby/object:Gem::Requirement
113
107
  requirements:
114
108
  - - "~>"
115
109
  - !ruby/object:Gem::Version
116
- version: '3.1'
110
+ version: '3.2'
117
111
  - !ruby/object:Gem::Dependency
118
- name: rdf-vocab
112
+ name: json-ld
119
113
  requirement: !ruby/object:Gem::Requirement
120
114
  requirements:
121
115
  - - "~>"
122
116
  - !ruby/object:Gem::Version
123
- version: '3.1'
117
+ version: '3.2'
124
118
  type: :development
125
119
  prerelease: false
126
120
  version_requirements: !ruby/object:Gem::Requirement
127
121
  requirements:
128
122
  - - "~>"
129
123
  - !ruby/object:Gem::Version
130
- version: '3.1'
124
+ version: '3.2'
131
125
  - !ruby/object:Gem::Dependency
132
- name: rake
126
+ name: rdf-spec
133
127
  requirement: !ruby/object:Gem::Requirement
134
128
  requirements:
135
129
  - - "~>"
136
130
  - !ruby/object:Gem::Version
137
- version: '13.0'
131
+ version: '3.2'
138
132
  type: :development
139
133
  prerelease: false
140
134
  version_requirements: !ruby/object:Gem::Requirement
141
135
  requirements:
142
136
  - - "~>"
143
137
  - !ruby/object:Gem::Version
144
- version: '13.0'
138
+ version: '3.2'
145
139
  - !ruby/object:Gem::Dependency
146
- name: yard
140
+ name: rdf-vocab
147
141
  requirement: !ruby/object:Gem::Requirement
148
142
  requirements:
149
143
  - - "~>"
150
144
  - !ruby/object:Gem::Version
151
- version: 0.9.20
145
+ version: '3.2'
152
146
  type: :development
153
147
  prerelease: false
154
148
  version_requirements: !ruby/object:Gem::Requirement
155
149
  requirements:
156
150
  - - "~>"
157
151
  - !ruby/object:Gem::Version
158
- version: 0.9.20
152
+ version: '3.2'
159
153
  description: RDF::Turtle is an Turtle reader/writer for the RDF.rb library suite.
160
154
  email: public-rdf-ruby@w3.org
161
155
  executables: []
@@ -178,8 +172,13 @@ files:
178
172
  homepage: https://github.com/ruby-rdf/rdf-turtle
179
173
  licenses:
180
174
  - Unlicense
181
- metadata: {}
182
- post_install_message:
175
+ metadata:
176
+ documentation_uri: https://ruby-rdf.github.io/rdf-turtle
177
+ bug_tracker_uri: https://github.com/ruby-rdf/rdf-turtle/issues
178
+ homepage_uri: https://github.com/ruby-rdf/rdf-turtle
179
+ mailing_list_uri: https://lists.w3.org/Archives/Public/public-rdf-ruby/
180
+ source_code_uri: https://github.com/ruby-rdf/rdf-turtle
181
+ post_install_message:
183
182
  rdoc_options: []
184
183
  require_paths:
185
184
  - lib
@@ -187,15 +186,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
187
186
  requirements:
188
187
  - - ">="
189
188
  - !ruby/object:Gem::Version
190
- version: '2.4'
189
+ version: '2.6'
191
190
  required_rubygems_version: !ruby/object:Gem::Requirement
192
191
  requirements:
193
192
  - - ">="
194
193
  - !ruby/object:Gem::Version
195
194
  version: '0'
196
195
  requirements: []
197
- rubygems_version: 3.1.3
198
- signing_key:
196
+ rubygems_version: 3.3.3
197
+ signing_key:
199
198
  specification_version: 4
200
199
  summary: Turtle reader/writer for Ruby.
201
200
  test_files: []