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/reader.rb CHANGED
@@ -1,14 +1,17 @@
1
1
  module RDF
2
2
  ##
3
- # An RDF parser.
3
+ # The base class for RDF parsers.
4
+ #
5
+ # @example Loading an RDF reader implementation
6
+ # require 'rdf/ntriples'
4
7
  #
5
8
  # @example Iterating over known RDF reader classes
6
9
  # RDF::Reader.each { |klass| puts klass.name }
7
10
  #
8
11
  # @example Obtaining an RDF reader class
9
12
  # RDF::Reader.for(:ntriples) #=> RDF::NTriples::Reader
10
- # RDF::Reader.for("spec/data/test.nt")
11
- # RDF::Reader.for(:file_name => "spec/data/test.nt")
13
+ # RDF::Reader.for("etc/doap.nt")
14
+ # RDF::Reader.for(:file_name => "etc/doap.nt")
12
15
  # RDF::Reader.for(:file_extension => "nt")
13
16
  # RDF::Reader.for(:content_type => "text/plain")
14
17
  #
@@ -16,14 +19,14 @@ module RDF
16
19
  # RDF::Reader.for(:ntriples).new($stdin) { |reader| ... }
17
20
  #
18
21
  # @example Parsing RDF statements from a file
19
- # RDF::Reader.open("spec/data/test.nt") do |reader|
22
+ # RDF::Reader.open("etc/doap.nt") do |reader|
20
23
  # reader.each_statement do |statement|
21
24
  # puts statement.inspect
22
25
  # end
23
26
  # end
24
27
  #
25
28
  # @example Parsing RDF statements from a string
26
- # data = StringIO.new(File.read("spec/data/test.nt"))
29
+ # data = StringIO.new(File.read("etc/doap.nt"))
27
30
  # RDF::Reader.for(:ntriples).new(data) do |reader|
28
31
  # reader.each_statement do |statement|
29
32
  # puts statement.inspect
@@ -35,14 +38,15 @@ module RDF
35
38
  # @see RDF::Writer
36
39
  class Reader
37
40
  extend ::Enumerable
41
+ extend RDF::Util::Aliasing::LateBound
38
42
  include RDF::Readable
39
- include ::Enumerable
43
+ include RDF::Enumerable # @since 0.3.0
40
44
 
41
45
  ##
42
46
  # Enumerates known RDF reader classes.
43
47
  #
44
48
  # @yield [klass]
45
- # @yieldparam [Class]
49
+ # @yieldparam [Class] klass
46
50
  # @return [Enumerator]
47
51
  def self.each(&block)
48
52
  @@subclasses.each(&block)
@@ -99,63 +103,192 @@ module RDF
99
103
  end
100
104
 
101
105
  ##
102
- # @param [String] filename
106
+ # Parses input from the given file name or URL.
107
+ #
108
+ # @param [String, #to_s] filename
109
+ # @param [Hash{Symbol => Object}] options
110
+ # any additional options (see {RDF::Reader#initialize})
103
111
  # @option options [Symbol] :format (:ntriples)
104
112
  # @yield [reader]
105
- # @yieldparam [Reader]
106
- # @raise [FormatError] if no reader available for the specified format
113
+ # @yieldparam [RDF::Reader] reader
114
+ # @yieldreturn [void] ignored
115
+ # @raise [RDF::FormatError] if no reader found for the specified format
107
116
  def self.open(filename, options = {}, &block)
108
- if reader = self.for(options[:format] || filename)
109
- Kernel.open(filename, 'rb') do |file|
117
+ Util::File.open_file(filename, options) do |file|
118
+ reader = self.for(options[:format]) if options[:format]
119
+ content_type = file.content_type if file.respond_to?(:content_type)
120
+ reader ||= self.for(options.merge(:file_name => filename, :content_type => content_type))
121
+ if reader
110
122
  reader.new(file, options, &block)
123
+ else
124
+ raise FormatError, "unknown RDF format: #{options[:format] || {:file_name => filename, :content_type => content_type}.inspect}"
111
125
  end
112
- else
113
- raise FormatError.new("unknown RDF format: #{options[:format] || filename}")
114
126
  end
115
127
  end
116
128
 
117
129
  ##
130
+ # Initializes the reader.
131
+ #
118
132
  # @param [IO, File, String] input
119
- # @yield [reader]
120
- # @yieldparam [Reader] reader
133
+ # the input stream to read
134
+ # @param [Hash{Symbol => Object}] options
135
+ # any additional options
136
+ # @option options [Encoding] :encoding (Encoding::UTF_8)
137
+ # the encoding of the input stream (Ruby 1.9+)
138
+ # @option options [Boolean] :validate (false)
139
+ # whether to validate the parsed statements and values
140
+ # @option options [Boolean] :canonicalize (false)
141
+ # whether to canonicalize parsed literals
142
+ # @option options [Boolean] :intern (true)
143
+ # whether to intern all parsed URIs
144
+ # @option options [Hash] :prefixes (Hash.new)
145
+ # the prefix mappings to use (not supported by all readers)
146
+ # @option options [#to_s] :base_uri (nil)
147
+ # the base URI to use when resolving relative URIs (not supported by
148
+ # all readers)
149
+ # @yield [reader] `self`
150
+ # @yieldparam [RDF::Reader] reader
151
+ # @yieldreturn [void] ignored
121
152
  def initialize(input = $stdin, options = {}, &block)
122
- @options = options
123
- @nodes = {}
124
- @input = case input
153
+ @options = options.dup
154
+ @options[:validate] ||= false
155
+ @options[:canonicalize] ||= false
156
+ @options[:intern] ||= true
157
+ @options[:prefixes] ||= Hash.new
158
+
159
+ @input = case input
125
160
  when String then StringIO.new(input)
126
161
  else input
127
162
  end
128
- block.call(self) if block_given?
163
+
164
+ if block_given?
165
+ case block.arity
166
+ when 0 then instance_eval(&block)
167
+ else block.call(self)
168
+ end
169
+ end
129
170
  end
130
171
 
131
172
  ##
132
- # @yield [statement]
133
- # @yieldparam [Statement]
134
- # @return [Reader]
135
- def each(&block)
136
- each_statement(&block)
173
+ # Any additional options for this reader.
174
+ #
175
+ # @return [Hash]
176
+ # @since 0.3.0
177
+ attr_reader :options
178
+
179
+ ##
180
+ # Returns the URI prefixes currently defined for this reader.
181
+ #
182
+ # @example
183
+ # reader.prefixes[:dc] #=> RDF::URI('http://purl.org/dc/terms/')
184
+ #
185
+ # @return [Hash{Symbol => RDF::URI}]
186
+ # @since 0.3.0
187
+ def prefixes
188
+ @options[:prefixes] ||= {}
137
189
  end
138
190
 
139
191
  ##
140
- # @yield [statement]
141
- # @yieldparam [Statement]
142
- # @return [Enumerator]
192
+ # Defines the given URI prefixes for this reader.
193
+ #
194
+ # @example
195
+ # reader.prefixes = {
196
+ # :dc => RDF::URI('http://purl.org/dc/terms/'),
197
+ # }
198
+ #
199
+ # @param [Hash{Symbol => RDF::URI}] prefixes
200
+ # @return [Hash{Symbol => RDF::URI}]
201
+ # @since 0.3.0
202
+ def prefixes=(prefixes)
203
+ @options[:prefixes] = prefixes
204
+ end
205
+
206
+ ##
207
+ # Defines the given named URI prefix for this reader.
208
+ #
209
+ # @example Defining a URI prefix
210
+ # reader.prefix :dc, RDF::URI('http://purl.org/dc/terms/')
211
+ #
212
+ # @example Returning a URI prefix
213
+ # reader.prefix(:dc) #=> RDF::URI('http://purl.org/dc/terms/')
214
+ #
215
+ # @overload prefix(name, uri)
216
+ # @param [Symbol, #to_s] name
217
+ # @param [RDF::URI, #to_s] uri
218
+ #
219
+ # @overload prefix(name)
220
+ # @param [Symbol, #to_s] name
221
+ #
222
+ # @return [RDF::URI]
223
+ def prefix(name, uri = nil)
224
+ name = name.respond_to?(:to_sym) ? name.to_sym : name.to_s.to_sym
225
+ uri.nil? ? prefixes[name] : prefixes[name] = RDF::URI(uri)
226
+ end
227
+ alias_method :prefix!, :prefix
228
+
229
+ ##
230
+ # Iterates the given block for each RDF statement.
231
+ #
232
+ # If no block was given, returns an enumerator.
233
+ #
234
+ # Statements are yielded in the order that they are read from the input
235
+ # stream.
236
+ #
237
+ # @overload each_statement
238
+ # @yield [statement]
239
+ # each statement
240
+ # @yieldparam [RDF::Statement] statement
241
+ # @yieldreturn [void] ignored
242
+ # @return [void]
243
+ #
244
+ # @overload each_statement
245
+ # @return [Enumerator]
246
+ #
247
+ # @return [void]
248
+ # @see RDF::Enumerable#each_statement
143
249
  def each_statement(&block)
144
- begin
145
- loop { block.call(read_statement) }
146
- rescue EOFError => e
250
+ if block_given?
251
+ begin
252
+ loop { block.call(read_statement) }
253
+ rescue EOFError => e
254
+ rewind rescue nil
255
+ end
147
256
  end
257
+ enum_for(:each_statement)
148
258
  end
259
+ alias_method :each, :each_statement
149
260
 
150
261
  ##
151
- # @yield [triple]
152
- # @yieldparam [Array(RDF::Value)]
153
- # @return [Enumerator]
262
+ # Iterates the given block for each RDF triple.
263
+ #
264
+ # If no block was given, returns an enumerator.
265
+ #
266
+ # Triples are yielded in the order that they are read from the input
267
+ # stream.
268
+ #
269
+ # @overload each_triple
270
+ # @yield [subject, predicate, object]
271
+ # each triple
272
+ # @yieldparam [RDF::Resource] subject
273
+ # @yieldparam [RDF::URI] predicate
274
+ # @yieldparam [RDF::Value] object
275
+ # @yieldreturn [void] ignored
276
+ # @return [void]
277
+ #
278
+ # @overload each_triple
279
+ # @return [Enumerator]
280
+ #
281
+ # @return [void]
282
+ # @see RDF::Enumerable#each_triple
154
283
  def each_triple(&block)
155
- begin
156
- loop { block.call(*read_triple) }
157
- rescue EOFError => e
284
+ if block_given?
285
+ begin
286
+ loop { block.call(*read_triple) }
287
+ rescue EOFError => e
288
+ rewind rescue nil
289
+ end
158
290
  end
291
+ enum_for(:each_triple)
159
292
  end
160
293
 
161
294
  ##
@@ -167,10 +300,13 @@ module RDF
167
300
  def rewind
168
301
  @input.rewind
169
302
  end
303
+ alias_method :rewind!, :rewind
170
304
 
171
305
  ##
172
- # Closes the input stream. An `IOError` is raised for further read
173
- # attempts.
306
+ # Closes the input stream, after which an `IOError` will be raised for
307
+ # further read attempts.
308
+ #
309
+ # If the input stream is already closed, does nothing.
174
310
  #
175
311
  # @return [void]
176
312
  # @since 0.2.2
@@ -178,93 +314,145 @@ module RDF
178
314
  def close
179
315
  @input.close unless @input.closed?
180
316
  end
317
+ alias_method :close!, :close
181
318
 
182
- protected
319
+ protected
183
320
 
184
- ##
185
- # @raise [NotImplementedError] unless implemented in subclass
186
- def read_statement
187
- Statement.new(*read_triple)
188
- end
321
+ ##
322
+ # Reads a statement from the input stream.
323
+ #
324
+ # @return [RDF::Statement] a statement
325
+ # @raise [NotImplementedError] unless implemented in subclass
326
+ # @abstract
327
+ def read_statement
328
+ Statement.new(*read_triple)
329
+ end
189
330
 
190
- ##
191
- # @raise [NotImplementedError] unless implemented in subclass
192
- def read_triple
193
- raise NotImplementedError.new("#{self.class}#read_triple") # override in subclasses
194
- end
331
+ ##
332
+ # Reads a triple from the input stream.
333
+ #
334
+ # @return [Array(RDF::Value)] a triple
335
+ # @raise [NotImplementedError] unless implemented in subclass
336
+ # @abstract
337
+ def read_triple
338
+ raise NotImplementedError, "#{self.class}#read_triple" # override in subclasses
339
+ end
195
340
 
196
- ##
197
- # @raise [ReaderError]
198
- def fail_subject
199
- raise RDF::ReaderError, "expected subject in #{@input.inspect} line #{lineno}"
200
- end
341
+ ##
342
+ # Raises an "expected subject" parsing error on the current line.
343
+ #
344
+ # @return [void]
345
+ # @raise [RDF::ReaderError]
346
+ def fail_subject
347
+ raise RDF::ReaderError, "expected subject in #{@input.inspect} line #{lineno}"
348
+ end
201
349
 
202
- ##
203
- # @raise [ReaderError]
204
- def fail_predicate
205
- raise RDF::ReaderError, "expected predicate in #{@input.inspect} line #{lineno}"
206
- end
350
+ ##
351
+ # Raises an "expected predicate" parsing error on the current line.
352
+ #
353
+ # @return [void]
354
+ # @raise [RDF::ReaderError]
355
+ def fail_predicate
356
+ raise RDF::ReaderError, "expected predicate in #{@input.inspect} line #{lineno}"
357
+ end
207
358
 
208
- ##
209
- # @raise [ReaderError]
210
- def fail_object
211
- raise RDF::ReaderError, "expected object in #{@input.inspect} line #{lineno}"
212
- end
359
+ ##
360
+ # Raises an "expected object" parsing error on the current line.
361
+ #
362
+ # @return [void]
363
+ # @raise [RDF::ReaderError]
364
+ def fail_object
365
+ raise RDF::ReaderError, "expected object in #{@input.inspect} line #{lineno}"
366
+ end
213
367
 
214
- private
368
+ ##
369
+ # Returns the encoding of the input stream.
370
+ #
371
+ # _Note: this method requires Ruby 1.9 or newer._
372
+ #
373
+ # @return [Encoding]
374
+ def encoding
375
+ @options[:encoding] ||= Encoding::UTF_8
376
+ end
215
377
 
216
- @@subclasses = [] # @private
378
+ ##
379
+ # Returns `true` if parsed statements and values should be validated.
380
+ #
381
+ # @return [Boolean] `true` or `false`
382
+ # @since 0.3.0
383
+ def validate?
384
+ @options[:validate]
385
+ end
217
386
 
218
- ##
219
- # @private
220
- def self.inherited(child)
221
- @@subclasses << child
222
- super
223
- end
387
+ ##
388
+ # Returns `true` if parsed values should be canonicalized.
389
+ #
390
+ # @return [Boolean] `true` or `false`
391
+ # @since 0.3.0
392
+ def canonicalize?
393
+ @options[:canonicalize]
394
+ end
224
395
 
225
- ##
226
- # @return [Integer]
227
- def lineno
228
- @input.lineno
229
- end
396
+ ##
397
+ # Returns `true` if parsed URIs should be interned.
398
+ #
399
+ # @return [Boolean] `true` or `false`
400
+ # @since 0.3.0
401
+ def intern?
402
+ @options[:intern]
403
+ end
230
404
 
231
- ##
232
- # @return [String]
233
- def readline
234
- @line = @input.readline.chomp
235
- @line.force_encoding(encoding) if @line.respond_to?(:force_encoding) # for Ruby 1.9+
236
- @line
237
- end
405
+ private
238
406
 
239
- ##
240
- # @return [Encoding]
241
- def encoding
242
- @encoding ||= ::Encoding::UTF_8
243
- end
407
+ @@subclasses = [] # @private
244
408
 
245
- ##
246
- # @return [void]
247
- def strip!
248
- @line.strip!
249
- end
409
+ ##
410
+ # @private
411
+ # @return [void]
412
+ def self.inherited(child)
413
+ @@subclasses << child
414
+ super
415
+ end
250
416
 
251
- ##
252
- # @return [Boolean]
253
- def blank?
254
- @line.nil? || @line.empty?
255
- end
417
+ ##
418
+ # @return [Integer]
419
+ def lineno
420
+ @input.lineno
421
+ end
256
422
 
257
- ##
258
- # @param [Regexp] pattern
259
- # @return [Object]
260
- def match(pattern)
261
- if @line =~ pattern
262
- result, @line = $1, $'.lstrip
263
- result || true
264
- end
265
- end
423
+ ##
424
+ # @return [String]
425
+ def readline
426
+ @line = @input.readline.chomp
427
+ @line.force_encoding(encoding) if @line.respond_to?(:force_encoding) # for Ruby 1.9+
428
+ @line
429
+ end
266
430
 
267
- end
431
+ ##
432
+ # @return [void]
433
+ def strip!
434
+ @line.strip!
435
+ end
268
436
 
269
- class ReaderError < IOError; end
270
- end
437
+ ##
438
+ # @return [Boolean]
439
+ def blank?
440
+ @line.nil? || @line.empty?
441
+ end
442
+
443
+ ##
444
+ # @param [Regexp] pattern
445
+ # @return [Object]
446
+ def match(pattern)
447
+ if @line =~ pattern
448
+ result, @line = $1, $'.lstrip
449
+ result || true
450
+ end
451
+ end
452
+ end # Reader
453
+
454
+ ##
455
+ # The base class for RDF parsing errors.
456
+ class ReaderError < IOError
457
+ end # ReaderError
458
+ end # RDF
@@ -111,6 +111,16 @@ module RDF
111
111
  end
112
112
  end
113
113
 
114
+ ##
115
+ # Returns `true` if this repository supports the given `feature`.
116
+ #
117
+ # @param [Symbol, #to_sym] feature
118
+ # @return [Boolean]
119
+ # @since 0.1.10
120
+ def supports?(feature)
121
+ false
122
+ end
123
+
114
124
  ##
115
125
  # Returns a developer-friendly representation of this object.
116
126
  #
@@ -129,6 +139,80 @@ module RDF
129
139
  nil
130
140
  end
131
141
 
142
+ ##
143
+ # Executes the given block in a transaction.
144
+ #
145
+ # @example
146
+ # repository.transaction do |tx|
147
+ # tx.insert [RDF::URI("http://rdf.rubyforge.org/"), RDF::DC.title, "RDF.rb"]
148
+ # end
149
+ #
150
+ # @param [RDF::Resource] context
151
+ # @yield [tx]
152
+ # @yieldparam [RDF::Transaction] tx
153
+ # @return [void]
154
+ # @see RDF::Transaction
155
+ # @since 0.3.0
156
+ def transaction(context = nil, &block)
157
+ tx = begin_transaction(context)
158
+ begin
159
+ case block.arity
160
+ when 1 then block.call(tx)
161
+ else tx.instance_eval(&block)
162
+ end
163
+ rescue => error
164
+ rollback_transaction(tx)
165
+ raise error
166
+ end
167
+ commit_transaction(tx)
168
+ self
169
+ end
170
+ alias_method :transact, :transaction
171
+
172
+ protected
173
+
174
+ ##
175
+ # Begins a new transaction.
176
+ #
177
+ # Subclasses implementing transaction-capable storage adapters may wish
178
+ # to override this method in order to begin a transaction against the
179
+ # underlying storage.
180
+ #
181
+ # @param [RDF::Resource] context
182
+ # @return [RDF::Transaction]
183
+ # @since 0.3.0
184
+ def begin_transaction(context)
185
+ RDF::Transaction.new(:context => context)
186
+ end
187
+
188
+ ##
189
+ # Rolls back the given transaction.
190
+ #
191
+ # Subclasses implementing transaction-capable storage adapters may wish
192
+ # to override this method in order to roll back the given transaction in
193
+ # the underlying storage.
194
+ #
195
+ # @param [RDF::Transaction] tx
196
+ # @return [void]
197
+ # @since 0.3.0
198
+ def rollback_transaction(tx)
199
+ # nothing to do
200
+ end
201
+
202
+ ##
203
+ # Commits the given transaction.
204
+ #
205
+ # Subclasses implementing transaction-capable storage adapters may wish
206
+ # to override this method in order to commit the given transaction to
207
+ # the underlying storage.
208
+ #
209
+ # @param [RDF::Transaction] tx
210
+ # @return [void]
211
+ # @since 0.3.0
212
+ def commit_transaction(tx)
213
+ tx.execute(self)
214
+ end
215
+
132
216
  ##
133
217
  # @see RDF::Repository
134
218
  module Implementation
@@ -140,11 +224,8 @@ module RDF
140
224
  end
141
225
 
142
226
  ##
143
- # Returns `true` if this repository supports `feature`.
144
- #
145
- # @param [Symbol, #to_sym] feature
146
- # @return [Boolean]
147
- # @since 0.1.10
227
+ # @private
228
+ # @see RDF::Repository#supports?
148
229
  def supports?(feature)
149
230
  case feature.to_sym
150
231
  when :context then true # statement contexts / named graphs
@@ -229,7 +310,8 @@ module RDF
229
310
  # @private
230
311
  # @see RDF::Enumerable#each_context
231
312
  def each_context(&block)
232
- block_given? ? @data.keys.compact.each(&block) : enum_context
313
+ @data.keys.compact.each(&block) if block_given?
314
+ enum_context
233
315
  end
234
316
 
235
317
  protected