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/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