rdf 0.2.3 → 0.3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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
  ##