rdf 0.2.3 → 0.3.0.pre

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 (44) hide show
  1. data/AUTHORS +1 -0
  2. data/{CONTRIBUTORS → CREDITS} +3 -1
  3. data/README +17 -8
  4. data/VERSION +1 -1
  5. data/etc/doap.nt +28 -10
  6. data/lib/rdf/format.rb +55 -47
  7. data/lib/rdf/mixin/countable.rb +3 -6
  8. data/lib/rdf/mixin/enumerable.rb +69 -69
  9. data/lib/rdf/mixin/indexable.rb +26 -0
  10. data/lib/rdf/mixin/mutable.rb +2 -2
  11. data/lib/rdf/mixin/queryable.rb +50 -12
  12. data/lib/rdf/mixin/writable.rb +8 -19
  13. data/lib/rdf/model/literal/boolean.rb +42 -6
  14. data/lib/rdf/model/literal/date.rb +17 -5
  15. data/lib/rdf/model/literal/datetime.rb +18 -6
  16. data/lib/rdf/model/literal/decimal.rb +32 -5
  17. data/lib/rdf/model/literal/double.rb +32 -5
  18. data/lib/rdf/model/literal/integer.rb +16 -5
  19. data/lib/rdf/model/literal/time.rb +6 -6
  20. data/lib/rdf/model/literal/token.rb +5 -5
  21. data/lib/rdf/model/literal/xml.rb +5 -5
  22. data/lib/rdf/model/literal.rb +24 -11
  23. data/lib/rdf/model/node.rb +14 -13
  24. data/lib/rdf/model/uri.rb +315 -42
  25. data/lib/rdf/model/value.rb +1 -1
  26. data/lib/rdf/ntriples/reader.rb +23 -15
  27. data/lib/rdf/ntriples/writer.rb +1 -1
  28. data/lib/rdf/query/pattern.rb +131 -15
  29. data/lib/rdf/query/solution.rb +94 -29
  30. data/lib/rdf/query/solutions.rb +202 -0
  31. data/lib/rdf/query/variable.rb +42 -18
  32. data/lib/rdf/query.rb +210 -160
  33. data/lib/rdf/reader.rb +300 -112
  34. data/lib/rdf/repository.rb +88 -6
  35. data/lib/rdf/transaction.rb +161 -0
  36. data/lib/rdf/util/cache.rb +5 -0
  37. data/lib/rdf/util/file.rb +31 -0
  38. data/lib/rdf/util/uuid.rb +36 -0
  39. data/lib/rdf/util.rb +2 -0
  40. data/lib/rdf/version.rb +3 -3
  41. data/lib/rdf/vocab.rb +43 -35
  42. data/lib/rdf/writer.rb +105 -50
  43. data/lib/rdf.rb +29 -27
  44. metadata +26 -17
data/lib/rdf/model/uri.rb CHANGED
@@ -43,7 +43,7 @@ module RDF
43
43
  # The maximum number of cached interned URI references is given by the
44
44
  # `CACHE_SIZE` constant. This value is unlimited by default, in which
45
45
  # case an interned URI object will be purged only when the last strong
46
- # reference to it is garbage collection (i.e., when its finalizer runs).
46
+ # reference to it is garbage collected (i.e., when its finalizer runs).
47
47
  #
48
48
  # Excepting special memory-limited circumstances, it should always be
49
49
  # safe and preferred to construct new URI references using
@@ -52,9 +52,9 @@ module RDF
52
52
  # to returning a freshly-allocated one.
53
53
  #
54
54
  # @param [String, #to_s] str
55
- # @return [RDF::URI]
55
+ # @return [RDF::URI] an immutable, frozen URI object
56
56
  def self.intern(str)
57
- cache[str = str.to_s] ||= self.new(str)
57
+ (cache[str = str.to_s] ||= self.new(str)).freeze
58
58
  end
59
59
 
60
60
  ##
@@ -71,7 +71,7 @@ module RDF
71
71
 
72
72
  ##
73
73
  # @overload URI.new(uri)
74
- # @param [URI, String, #to_s] uri
74
+ # @param [RDF::URI, String, #to_s] uri
75
75
  #
76
76
  # @overload URI.new(options = {})
77
77
  # @param [Hash{Symbol => Object} options
@@ -89,7 +89,7 @@ module RDF
89
89
  ##
90
90
  # Returns `false`.
91
91
  #
92
- # @return [Boolean]
92
+ # @return [Boolean] `true` or `false`
93
93
  def anonymous?
94
94
  false
95
95
  end
@@ -97,7 +97,7 @@ module RDF
97
97
  ##
98
98
  # Returns `true`.
99
99
  #
100
- # @return [Boolean]
100
+ # @return [Boolean] `true` or `false`
101
101
  # @see http://en.wikipedia.org/wiki/Uniform_Resource_Identifier
102
102
  def uri?
103
103
  true
@@ -106,41 +106,198 @@ module RDF
106
106
  ##
107
107
  # Returns `true` if this URI is a URN.
108
108
  #
109
- # @return [Boolean]
109
+ # @example
110
+ # RDF::URI('http://example.org/').urn? #=> false
111
+ #
112
+ # @return [Boolean] `true` or `false`
110
113
  # @see http://en.wikipedia.org/wiki/Uniform_Resource_Name
111
114
  # @since 0.2.0
112
115
  def urn?
113
- to_s.index('urn:') == 0
116
+ self.start_with?('urn:')
114
117
  end
115
118
 
116
119
  ##
117
120
  # Returns `true` if this URI is a URL.
118
121
  #
119
- # @return [Boolean]
122
+ # @example
123
+ # RDF::URI('http://example.org/').url? #=> true
124
+ #
125
+ # @return [Boolean] `true` or `false`
120
126
  # @see http://en.wikipedia.org/wiki/Uniform_Resource_Locator
121
127
  # @since 0.2.0
122
128
  def url?
123
129
  !urn?
124
130
  end
125
131
 
132
+ ##
133
+ # Returns the string length of this URI.
134
+ #
135
+ # @example
136
+ # RDF::URI('http://example.org/').length #=> 19
137
+ #
138
+ # @return [Integer]
139
+ # @since 0.3.0
140
+ def length
141
+ to_s.length
142
+ end
143
+ alias_method :size, :length
144
+
145
+ ##
146
+ # Validates this URI, raising an error if it is invalid.
147
+ #
148
+ # @return [RDF::URI] `self`
149
+ # @raise [ArgumentError] if the URI is invalid
150
+ # @since 0.3.0
151
+ def validate!
152
+ # TODO: raise error if the URI fails validation
153
+ self
154
+ end
155
+ alias_method :validate, :validate!
156
+
157
+ ##
158
+ # Returns a copy of this URI converted into its canonical lexical
159
+ # representation.
160
+ #
161
+ # @return [RDF::URI]
162
+ # @since 0.3.0
163
+ def canonicalize
164
+ self.dup.canonicalize!
165
+ end
166
+
167
+ ##
168
+ # Converts this URI into its canonical lexical representation.
169
+ #
170
+ # @return [RDF::URI] `self`
171
+ # @since 0.3.0
172
+ def canonicalize!
173
+ # TODO: canonicalize this URI
174
+ self
175
+ end
176
+
126
177
  ##
127
178
  # Joins several URIs together.
128
179
  #
129
- # @param [Array<String, URI, #to_str>] uris
130
- # @return [URI]
180
+ # This method conforms to join normalization semantics as per RFC3986,
181
+ # section 5.2. This method normalizes URIs, removes some duplicate path
182
+ # information, such as double slashes, and other behavior specified in the
183
+ # RFC.
184
+ #
185
+ # Other URI building methods are `#/` and `#+`.
186
+ #
187
+ # For an up-to-date list of edge case behavior, see the shared examples for
188
+ # RDF::URI in the rdf-spec project.
189
+ #
190
+ # @example Joining two URIs
191
+ # RDF::URI.new('http://example.org/foo/bar').join('/foo')
192
+ # #=> RDF::URI('http://example.org/foo')
193
+ # @see <http://github.com/bendiken/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
194
+ # @see <http://tools.ietf.org/html/rfc3986#section-5.2>
195
+ # @see RDF::URI#/
196
+ # @see RDF::URI#+
197
+ # @param [Array<String, RDF::URI, #to_s>] uris
198
+ # @return [RDF::URI]
131
199
  def join(*uris)
132
- result = @uri
200
+ result = @uri.dup
133
201
  uris.each do |uri|
134
- result.path += '/' unless result.path[-1] == ?/ # '/'
135
202
  result = result.join(uri)
136
203
  end
137
204
  self.class.new(result)
138
205
  end
139
206
 
207
+ ##
208
+ # 'Smart separator' URI builder
209
+ #
210
+ # This method attempts to use some understanding of the most common use
211
+ # cases for URLs and URNs to create a simple method for building new URIs
212
+ # from fragments. This means that it will always insert a separator of
213
+ # some sort, will remove duplicate seperators, will always assume that a
214
+ # fragment argument represents a relative and not absolute path, and throws
215
+ # an exception when an absolute URI is received for a fragment argument.
216
+ #
217
+ # This is separate from the semantics for `#join`, which are well-defined by
218
+ # RFC3986 section 5.2 as part of the merging and normalization process;
219
+ # this method does not perform any normalization, removal of spurious
220
+ # paths, or removal of parent directory references `(/../)`.
221
+ #
222
+ # See also `#+`, which concatenates the string forms of two URIs without
223
+ # any sort of checking or processing.
224
+ #
225
+ # For an up-to-date list of edge case behavior, see the shared examples for
226
+ # RDF::URI in the rdf-spec project.
227
+ #
228
+ # @param [Any] fragment A URI fragment to be appended to this URI
229
+ # @return [RDF::URI]
230
+ # @see RDF::URI#+
231
+ # @see RDF::URI#join
232
+ # @see <http://tools.ietf.org/html/rfc3986#section-5.2>
233
+ # @see <http://github.com/bendiken/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
234
+ # @example Building a HTTP URL
235
+ # RDF::URI.new('http://example.org') / 'jhacker' / 'foaf.ttl'
236
+ # #=> RDF::URI('http://example.org/jhacker/foaf.ttl')
237
+ # @example Building a HTTP URL
238
+ # RDF::URI.new('http://example.org/') / '/jhacker/' / '/foaf.ttl'
239
+ # #=> RDF::URI('http://example.org/jhacker/foaf.ttl')
240
+ # @example Using an anchored base URI
241
+ # RDF::URI.new('http://example.org/users#') / 'jhacker'
242
+ # #=> RDF::URI('http://example.org/users#jhacker')
243
+ # @example Building a URN
244
+ # RDF::URI.new('urn:isbn') / 125235111
245
+ # #=> RDF::URI('urn:isbn:125235111')
246
+ def /(fragment)
247
+ fragment = fragment.respond_to?(:to_uri) ? fragment.to_uri : RDF::URI.intern(fragment.to_s)
248
+ raise ArgumentError, "Non-absolute URI or string required, got #{fragment}" unless fragment.relative?
249
+ if urn?
250
+ RDF::URI.intern(to_s.sub(/:+$/,'') + ':' + fragment.to_s.sub(/^:+/,''))
251
+ else # !urn?
252
+ case to_s[-1].chr
253
+ when '#'
254
+ case fragment.to_s[0].chr
255
+ when '/' then # Base ending with '#', fragment beginning with '/'. The fragment wins, we use '/'.
256
+ RDF::URI.intern(to_s.sub(/#+$/,'') + '/' + fragment.to_s.sub(/^\/+/,''))
257
+ else
258
+ RDF::URI.intern(to_s.sub(/#+$/,'') + '#' + fragment.to_s.sub(/^#+/,''))
259
+ end
260
+ else # includes '/'. Results from bases ending in '/' are the same as if there were no trailing slash.
261
+ case fragment.to_s[0].chr
262
+ when '#' then # Base ending with '/', fragment beginning with '#'. The fragment wins, we use '#'.
263
+ RDF::URI.intern(to_s.sub(/\/+$/,'') + '#' + fragment.to_s.sub(/^#+/,''))
264
+ else
265
+ RDF::URI.intern(to_s.sub(/\/+$/,'') + '/' + fragment.to_s.sub(/^\/+/,''))
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ ##
272
+ # Simple concatenation operator. Returns a URI formed from concatenating
273
+ # the string form of two elements.
274
+ #
275
+ # For building URIs from fragments, you may want to use the smart
276
+ # separator, `#/`. `#join` implements another set of URI building
277
+ # semantics.
278
+ #
279
+ # @example Concatenating a string to a URI
280
+ # RDF::URI.new('http://example.org/test') + 'test'
281
+ # #=> RDF::URI('http://example.org/testtest')
282
+ # @example Concatenating two URIs
283
+ # RDF::URI.new('http://example.org/test') + RDF::URI.new('test')
284
+ # #=> RDF::URI('http://example.org/testtest')
285
+ # @see RDF::URI#/
286
+ # @see RDF::URI#join
287
+ # @param [Any]
288
+ # @return [RDF::URI]
289
+ def +(other)
290
+ RDF::URI.intern(self.to_s + other.to_s)
291
+ end
292
+
140
293
  ##
141
294
  # Returns `true` if this URI's path component is equal to `/`.
142
295
  #
143
- # @return [Boolean]
296
+ # @example
297
+ # RDF::URI('http://example.org/').root? #=> true
298
+ # RDF::URI('http://example.org/path/').root? #=> false
299
+ #
300
+ # @return [Boolean] `true` or `false`
144
301
  def root?
145
302
  self.path == '/' || self.path.empty?
146
303
  end
@@ -148,7 +305,11 @@ module RDF
148
305
  ##
149
306
  # Returns a copy of this URI with the path component set to `/`.
150
307
  #
151
- # @return [URI]
308
+ # @example
309
+ # RDF::URI('http://example.org/').root #=> RDF::URI('http://example.org/')
310
+ # RDF::URI('http://example.org/path/').root #=> RDF::URI('http://example.org/')
311
+ #
312
+ # @return [RDF::URI]
152
313
  def root
153
314
  if root?
154
315
  self
@@ -162,7 +323,11 @@ module RDF
162
323
  ##
163
324
  # Returns `true` if this URI's path component isn't equal to `/`.
164
325
  #
165
- # @return [Boolean]
326
+ # @example
327
+ # RDF::URI('http://example.org/').has_parent? #=> false
328
+ # RDF::URI('http://example.org/path/').has_parent? #=> true
329
+ #
330
+ # @return [Boolean] `true` or `false`
166
331
  def has_parent?
167
332
  !root?
168
333
  end
@@ -171,7 +336,11 @@ module RDF
171
336
  # Returns a copy of this URI with the path component ascended to the
172
337
  # parent directory, if any.
173
338
  #
174
- # @return [URI]
339
+ # @example
340
+ # RDF::URI('http://example.org/').parent #=> nil
341
+ # RDF::URI('http://example.org/path/').parent #=> RDF::URI('http://example.org/')
342
+ #
343
+ # @return [RDF::URI]
175
344
  def parent
176
345
  case
177
346
  when root? then nil
@@ -189,33 +358,90 @@ module RDF
189
358
  ##
190
359
  # Returns a qualified name (QName) for this URI, if possible.
191
360
  #
192
- # @return [Array(Symbol, Symbol)]
361
+ # @example
362
+ # RDF::URI('http://purl.org/dc/terms/').qname #=> [:dc, nil]
363
+ # RDF::URI('http://purl.org/dc/terms/title').qname #=> [:dc, :title]
364
+ # RDF::DC.title.qname #=> [:dc, :title]
365
+ #
366
+ # @return [Array(Symbol, Symbol)] or `nil` if no QName found
193
367
  def qname
194
- Vocabulary.each do |vocab|
195
- if to_s.index(vocab.to_uri.to_s) == 0
196
- vocab_name = vocab.__name__.split('::').last.downcase
197
- local_name = to_s[vocab.to_uri.to_s.size..-1]
198
- unless vocab_name.empty? || local_name.empty?
199
- return [vocab_name.to_sym, local_name.to_sym]
368
+ if self.to_s =~ %r([:/#]([^:/#]*)$)
369
+ local_name = $1
370
+ vocab_uri = local_name.empty? ? self.to_s : self.to_s[0...-(local_name.length)]
371
+ Vocabulary.each do |vocab|
372
+ if vocab.to_uri == vocab_uri
373
+ return [vocab.__prefix__, local_name.empty? ? nil : local_name.to_sym]
374
+ end
375
+ end
376
+ else
377
+ Vocabulary.each do |vocab|
378
+ vocab_uri = vocab.to_uri
379
+ if self.start_with?(vocab_uri)
380
+ local_name = self.to_s[vocab_uri.length..-1]
381
+ return [vocab.__prefix__, local_name.empty? ? nil : local_name.to_sym]
200
382
  end
201
383
  end
202
384
  end
203
- nil # no QName found
385
+ return nil # no QName found
204
386
  end
205
387
 
206
388
  ##
207
389
  # Returns a duplicate copy of `self`.
208
390
  #
209
- # @return [URI]
391
+ # @return [RDF::URI]
210
392
  def dup
211
393
  self.class.new(@uri.dup)
212
394
  end
213
395
 
396
+ ##
397
+ # @private
398
+ def freeze
399
+ @uri.freeze
400
+ super
401
+ end
402
+
403
+ ##
404
+ # Returns `true` if this URI starts with the given `string`.
405
+ #
406
+ # @example
407
+ # RDF::URI('http://example.org/').start_with?('http') #=> true
408
+ # RDF::URI('http://example.org/').start_with?('ftp') #=> false
409
+ #
410
+ # @param [String, #to_s] string
411
+ # @return [Boolean] `true` or `false`
412
+ # @see String#start_with?
413
+ # @since 0.3.0
414
+ def start_with?(string)
415
+ to_s.start_with?(string.to_s)
416
+ end
417
+ alias_method :starts_with?, :start_with?
418
+
419
+ ##
420
+ # Returns `true` if this URI ends with the given `string`.
421
+ #
422
+ # @example
423
+ # RDF::URI('http://example.org/').end_with?('/') #=> true
424
+ # RDF::URI('http://example.org/').end_with?('#') #=> false
425
+ #
426
+ # @param [String, #to_s] string
427
+ # @return [Boolean] `true` or `false`
428
+ # @see String#end_with?
429
+ # @since 0.3.0
430
+ def end_with?(string)
431
+ to_s.end_with?(string.to_s)
432
+ end
433
+ alias_method :ends_with?, :end_with?
434
+
214
435
  ##
215
436
  # Checks whether this URI is equal to `other`.
216
437
  #
217
- # @param [URI] other
218
- # @return [Boolean]
438
+ # @example
439
+ # RDF::URI('http://t.co/').eql?(RDF::URI('http://t.co/')) #=> true
440
+ # RDF::URI('http://t.co/').eql?('http://t.co/') #=> false
441
+ # RDF::URI('http://purl.org/dc/terms/').eql?(RDF::DC) #=> false
442
+ #
443
+ # @param [RDF::URI] other
444
+ # @return [Boolean] `true` or `false`
219
445
  def eql?(other)
220
446
  other.is_a?(URI) && self == other
221
447
  end
@@ -223,32 +449,78 @@ module RDF
223
449
  ##
224
450
  # Checks whether this URI is equal to `other`.
225
451
  #
452
+ # @example
453
+ # RDF::URI('http://t.co/') == RDF::URI('http://t.co/') #=> true
454
+ # RDF::URI('http://t.co/') == 'http://t.co/' #=> true
455
+ # RDF::URI('http://purl.org/dc/terms/') == RDF::DC #=> true
456
+ #
226
457
  # @param [Object] other
227
- # @return [Boolean]
458
+ # @return [Boolean] `true` or `false`
228
459
  def ==(other)
229
460
  case other
230
- when Addressable::URI
231
- to_s == other.to_s
232
- else
233
- other.respond_to?(:to_uri) && to_s == other.to_uri.to_s
461
+ when String then to_s == other
462
+ when URI, Addressable::URI then to_s == other.to_s
463
+ else other.respond_to?(:to_uri) && to_s == other.to_uri.to_s
464
+ end
465
+ end
466
+
467
+ ##
468
+ # Checks for case equality to the given `other` object.
469
+ #
470
+ # @example
471
+ # RDF::URI('http://example.org/') === /example/ #=> true
472
+ # RDF::URI('http://example.org/') === /foobar/ #=> false
473
+ # RDF::URI('http://t.co/') === RDF::URI('http://t.co/') #=> true
474
+ # RDF::URI('http://t.co/') === 'http://t.co/' #=> true
475
+ # RDF::URI('http://purl.org/dc/terms/') === RDF::DC #=> true
476
+ #
477
+ # @param [Object] other
478
+ # @return [Boolean] `true` or `false`
479
+ # @since 0.3.0
480
+ def ===(other)
481
+ case other
482
+ when Regexp then other === to_s
483
+ else self == other
484
+ end
485
+ end
486
+
487
+ ##
488
+ # Performs a pattern match using the given regular expression.
489
+ #
490
+ # @example
491
+ # RDF::URI('http://example.org/') =~ /example/ #=> 7
492
+ # RDF::URI('http://example.org/') =~ /foobar/ #=> nil
493
+ #
494
+ # @param [Regexp] pattern
495
+ # @return [Integer] the position the match starts
496
+ # @see String#=~
497
+ # @since 0.3.0
498
+ def =~(pattern)
499
+ case pattern
500
+ when Regexp then to_s =~ pattern
501
+ else super # `Object#=~` returns `false`
234
502
  end
235
503
  end
236
504
 
237
505
  ##
238
506
  # Returns `self`.
239
507
  #
240
- # @return [URI]
508
+ # @return [RDF::URI] `self`
241
509
  def to_uri
242
510
  self
243
511
  end
244
512
 
245
513
  ##
246
- # Returns a string representation of this URI.
514
+ # Returns the string representation of this URI.
515
+ #
516
+ # @example
517
+ # RDF::URI('http://example.org/').to_str #=> 'http://example.org/'
247
518
  #
248
519
  # @return [String]
249
- def to_s
520
+ def to_str
250
521
  @uri.to_s
251
522
  end
523
+ alias_method :to_s, :to_str
252
524
 
253
525
  ##
254
526
  # Returns a hash code for this URI.
@@ -262,15 +534,18 @@ module RDF
262
534
  # Returns `true` if this URI instance supports the `symbol` method.
263
535
  #
264
536
  # @param [Symbol, String, #to_s] symbol
265
- # @return [Boolean]
537
+ # @return [Boolean] `true` or `false`
266
538
  def respond_to?(symbol)
267
539
  @uri.respond_to?(symbol) || super
268
540
  end
269
541
 
542
+ protected
543
+
270
544
  ##
271
545
  # @param [Symbol, String, #to_s] symbol
272
- # @param [Array<Object>] args
546
+ # @param [Array<Object>] args
273
547
  # @yield
548
+ # @return [Object]
274
549
  # @private
275
550
  def method_missing(symbol, *args, &block)
276
551
  if @uri.respond_to?(symbol)
@@ -283,7 +558,5 @@ module RDF
283
558
  super
284
559
  end
285
560
  end
286
-
287
- protected :method_missing
288
- end
289
- end
561
+ end # URI
562
+ end # RDF
@@ -104,7 +104,7 @@ module RDF
104
104
  #
105
105
  # @abstract
106
106
  # @param [Object] other
107
- # @return [Integer] -1, 0, 1
107
+ # @return [Integer] `-1`, `0`, or `1`
108
108
  def <=>(other)
109
109
  self.to_s <=> other.to_s
110
110
  end
@@ -33,9 +33,9 @@ module RDF::NTriples
33
33
  NODEID = /^_:([A-Za-z][A-Za-z0-9]*)/.freeze
34
34
  URIREF = /^<([^>]+)>/.freeze
35
35
  LITERAL_PLAIN = /^"((?:\\"|[^"])*)"/.freeze
36
- LITERAL_WITH_LANGUAGE = /^"((?:\\"|[^"])*)"@([a-z]+[\-a-z0-9]*)/.freeze
36
+ LITERAL_WITH_LANGUAGE = /^"((?:\\"|[^"])*)"@([a-z]+[\-A-Za-z0-9]*)/.freeze
37
37
  LITERAL_WITH_DATATYPE = /^"((?:\\"|[^"])*)"\^\^<([^>]+)>/.freeze
38
- LANGUAGE_TAG = /^@([a-z]+[\-a-z0-9]*)/.freeze
38
+ LANGUAGE_TAG = /^@([a-z]+[\-A-Za-z0-9]*)/.freeze
39
39
  DATATYPE_URI = /^\^\^<([^>]+)>/.freeze
40
40
  LITERAL = Regexp.union(LITERAL_WITH_LANGUAGE, LITERAL_WITH_DATATYPE, LITERAL_PLAIN).freeze
41
41
  SUBJECT = Regexp.union(URIREF, NODEID).freeze
@@ -197,8 +197,12 @@ module RDF::NTriples
197
197
  # @return [RDF::URI]
198
198
  # @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (uriref)
199
199
  def read_uriref(options = {})
200
- if uri = match(URIREF)
201
- RDF::URI.send(options[:intern] ? :intern : :new, uri)
200
+ if uri_str = match(URIREF)
201
+ uri_str = self.class.unescape(uri_str)
202
+ uri = RDF::URI.send(intern? && options[:intern] ? :intern : :new, uri_str)
203
+ uri.validate! if validate?
204
+ uri.canonicalize! if canonicalize?
205
+ uri
202
206
  end
203
207
  end
204
208
 
@@ -207,6 +211,7 @@ module RDF::NTriples
207
211
  # @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (nodeID)
208
212
  def read_node
209
213
  if node_id = match(NODEID)
214
+ @nodes ||= {}
210
215
  @nodes[node_id] ||= RDF::Node.new(node_id)
211
216
  end
212
217
  end
@@ -215,17 +220,20 @@ module RDF::NTriples
215
220
  # @return [RDF::Literal]
216
221
  # @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (literal)
217
222
  def read_literal
218
- if literal = match(LITERAL_PLAIN)
219
- literal = self.class.unescape(literal)
220
-
221
- if language = match(LANGUAGE_TAG)
222
- RDF::Literal.new(literal, :language => language)
223
- elsif datatype = match(/^(\^\^)/) # FIXME
224
- RDF::Literal.new(literal, :datatype => read_uriref || fail_object)
225
- else
226
- RDF::Literal.new(literal) # plain string literal
223
+ if literal_str = match(LITERAL_PLAIN)
224
+ literal_str = self.class.unescape(literal_str)
225
+ literal = case
226
+ when language = match(LANGUAGE_TAG)
227
+ RDF::Literal.new(literal_str, :language => language)
228
+ when datatype = match(/^(\^\^)/) # FIXME
229
+ RDF::Literal.new(literal_str, :datatype => read_uriref || fail_object)
230
+ else
231
+ RDF::Literal.new(literal_str) # plain string literal
227
232
  end
233
+ literal.validate! if validate?
234
+ literal.canonicalize! if canonicalize?
235
+ literal
228
236
  end
229
237
  end
230
- end
231
- end
238
+ end # Reader
239
+ end # RDF::NTriples
@@ -185,7 +185,7 @@ module RDF::NTriples
185
185
  # @param [Hash{Symbol => Object}] options
186
186
  # @return [String]
187
187
  def format_uri(uri, options = {})
188
- "<%s>" % uri_for(uri)
188
+ "<%s>" % escaped(uri_for(uri))
189
189
  end
190
190
 
191
191
  ##