rdf 1.99.1 → 2.0.0.beta1

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/{README → README.md} +9 -44
  3. data/VERSION +1 -1
  4. data/bin/rdf +1 -1
  5. data/lib/rdf.rb +40 -49
  6. data/lib/rdf/changeset.rb +161 -0
  7. data/lib/rdf/cli.rb +195 -33
  8. data/lib/rdf/cli/vocab-loader.rb +13 -3
  9. data/lib/rdf/format.rb +44 -26
  10. data/lib/rdf/mixin/enumerable.rb +133 -97
  11. data/lib/rdf/mixin/enumerator.rb +8 -0
  12. data/lib/rdf/mixin/indexable.rb +1 -1
  13. data/lib/rdf/mixin/mutable.rb +101 -22
  14. data/lib/rdf/mixin/queryable.rb +21 -32
  15. data/lib/rdf/mixin/transactable.rb +94 -0
  16. data/lib/rdf/mixin/writable.rb +12 -3
  17. data/lib/rdf/model/dataset.rb +48 -0
  18. data/lib/rdf/model/graph.rb +73 -43
  19. data/lib/rdf/model/list.rb +61 -33
  20. data/lib/rdf/model/literal.rb +20 -19
  21. data/lib/rdf/model/literal/double.rb +20 -4
  22. data/lib/rdf/model/literal/numeric.rb +15 -13
  23. data/lib/rdf/model/node.rb +15 -16
  24. data/lib/rdf/model/statement.rb +1 -43
  25. data/lib/rdf/model/term.rb +10 -8
  26. data/lib/rdf/model/uri.rb +35 -34
  27. data/lib/rdf/model/value.rb +1 -1
  28. data/lib/rdf/nquads.rb +2 -11
  29. data/lib/rdf/ntriples.rb +1 -1
  30. data/lib/rdf/ntriples/reader.rb +33 -46
  31. data/lib/rdf/ntriples/writer.rb +42 -5
  32. data/lib/rdf/query.rb +6 -40
  33. data/lib/rdf/query/pattern.rb +4 -17
  34. data/lib/rdf/query/solutions.rb +6 -6
  35. data/lib/rdf/reader.rb +65 -14
  36. data/lib/rdf/repository.rb +365 -229
  37. data/lib/rdf/transaction.rb +211 -84
  38. data/lib/rdf/util.rb +1 -0
  39. data/lib/rdf/util/cache.rb +5 -5
  40. data/lib/rdf/util/file.rb +12 -9
  41. data/lib/rdf/util/logger.rb +272 -0
  42. data/lib/rdf/version.rb +2 -2
  43. data/lib/rdf/vocab/owl.rb +82 -77
  44. data/lib/rdf/vocab/rdfs.rb +22 -17
  45. data/lib/rdf/vocab/xsd.rb +5 -0
  46. data/lib/rdf/vocabulary.rb +50 -56
  47. data/lib/rdf/writer.rb +104 -52
  48. metadata +45 -90
  49. data/lib/rdf/mixin/inferable.rb +0 -5
  50. data/lib/rdf/vocab/cc.rb +0 -128
  51. data/lib/rdf/vocab/cert.rb +0 -245
  52. data/lib/rdf/vocab/dc.rb +0 -948
  53. data/lib/rdf/vocab/dc11.rb +0 -167
  54. data/lib/rdf/vocab/dcat.rb +0 -214
  55. data/lib/rdf/vocab/doap.rb +0 -337
  56. data/lib/rdf/vocab/exif.rb +0 -941
  57. data/lib/rdf/vocab/foaf.rb +0 -614
  58. data/lib/rdf/vocab/geo.rb +0 -157
  59. data/lib/rdf/vocab/gr.rb +0 -1501
  60. data/lib/rdf/vocab/ht.rb +0 -236
  61. data/lib/rdf/vocab/ical.rb +0 -528
  62. data/lib/rdf/vocab/ma.rb +0 -513
  63. data/lib/rdf/vocab/mo.rb +0 -2412
  64. data/lib/rdf/vocab/og.rb +0 -222
  65. data/lib/rdf/vocab/ogc.rb +0 -58
  66. data/lib/rdf/vocab/prov.rb +0 -1550
  67. data/lib/rdf/vocab/rsa.rb +0 -72
  68. data/lib/rdf/vocab/rss.rb +0 -66
  69. data/lib/rdf/vocab/schema.rb +0 -10569
  70. data/lib/rdf/vocab/sioc.rb +0 -669
  71. data/lib/rdf/vocab/skos.rb +0 -238
  72. data/lib/rdf/vocab/skosxl.rb +0 -57
  73. data/lib/rdf/vocab/v.rb +0 -383
  74. data/lib/rdf/vocab/vcard.rb +0 -841
  75. data/lib/rdf/vocab/vmd.rb +0 -383
  76. data/lib/rdf/vocab/void.rb +0 -186
  77. data/lib/rdf/vocab/vs.rb +0 -28
  78. data/lib/rdf/vocab/wdrs.rb +0 -134
  79. data/lib/rdf/vocab/wot.rb +0 -167
  80. data/lib/rdf/vocab/xhtml.rb +0 -8
  81. data/lib/rdf/vocab/xhv.rb +0 -505
@@ -2,70 +2,136 @@ module RDF
2
2
  ##
3
3
  # An RDF transaction.
4
4
  #
5
- # Transactions consist of a sequence of RDF statements to delete from and
6
- # a sequence of RDF statements to insert into a given named graph.
5
+ # Transactions provide an ACID scope for queries and mutations.
7
6
  #
8
- # Repository implementations may choose to sub-class this class
9
- # to provide transactional support for repository updates, when
10
- # accessed through {RDF::Repository#begin_transaction}.
7
+ # Repository implementations may provide support for transactional updates
8
+ # by providing an atomic implementation of {Mutable#apply_changeset} and
9
+ # responding to `#supports?(:atomic_write)` with `true`.
10
+ #
11
+ # We carefully distinguish between read-only and read/write transactions,
12
+ # in order to enable repository implementations to take out the
13
+ # appropriate locks for concurrency control. Transactions are read-only
14
+ # by default; mutability must be explicitly requested on construction in
15
+ # order to obtain a read/write transaction.
11
16
  #
12
- # @example Executing a transaction against a repository
13
- # repository = ...
14
- # RDF::Transaction.execute(repository) do |tx|
17
+ # Individual repositories may make their own sets of guarantees within the
18
+ # transaction's scope. In case repository implementations should be unable
19
+ # to provide full ACID guarantees for transactions, that must be clearly
20
+ # indicated in their documentation. If update atomicity is not provided,
21
+ # `#supports?(:atomic_write)` must respond `false`.
22
+ #
23
+ # @example Executing a read-only transaction
24
+ # repository = RDF::Repository.new
25
+ #
26
+ # RDF::Transaction.begin(repository) do |tx|
27
+ # tx.query(predicate: RDF::Vocab::DOAP.developer) do |statement|
28
+ # puts statement.inspect
29
+ # end
30
+ # end
31
+ #
32
+ # @example Executing a read/write transaction
33
+ # repository = RDF::Repository.new
34
+ #
35
+ # RDF::Transaction.begin(repository, mutable: true) do |tx|
15
36
  # subject = RDF::URI("http://example.org/article")
16
37
  # tx.delete [subject, RDF::RDFS.label, "Old title"]
17
38
  # tx.insert [subject, RDF::RDFS.label, "New title"]
18
39
  # end
40
+ #
41
+ # The base class provides an atomic write implementation depending on
42
+ # `RDF::Changeset` and using `Changeset#apply`. Custom `Repositories`
43
+ # can implement a minimial write-atomic transactions by overriding
44
+ # `#apply_changeset`.
19
45
  #
46
+ # Reads within a transaction run against the live repository by default
47
+ # (`#isolation_level' is `:read_committed`). Repositories may provide support
48
+ # for snapshots by implementing `Repository#snapshot` and responding `true` to
49
+ # `#supports?(:snapshots)`. In this case, the transaction will use the
50
+ # `RDF::Dataset` returned by `#snapshot` for reads (`:repeatable_read`).
51
+ #
52
+ # For datastores that support transactions natively, implementation of a
53
+ # custom `Transaction` subclass is recommended. The `Repository` is
54
+ # responsible for specifying snapshot support and isolation level as
55
+ # appropriate. Note that repositories may provide the snapshot isolation level
56
+ # without implementing `#snapshot`.
57
+ #
58
+ # @example A repository with a custom transaction class
59
+ # class MyRepository < RDF::Repository
60
+ # DEFAULT_TX_CLASS = MyTransaction
61
+ # # ...
62
+ # # custom repository logic
63
+ # # ...
64
+ # end
65
+ #
66
+ # @see RDF::Changeset
67
+ # @see RDF::Mutable#apply_changeset
20
68
  # @since 0.3.0
21
69
  class Transaction
22
70
  include RDF::Mutable
71
+ include RDF::Enumerable
72
+ include RDF::Queryable
73
+
74
+ ##
75
+ # @see RDF::Enumerable#each
76
+ def each(*args, &block)
77
+ read_target.each(*args, &block)
78
+ end
23
79
 
24
80
  ##
25
81
  # Executes a transaction against the given RDF repository.
26
82
  #
27
83
  # @param [RDF::Repository] repository
84
+ # @param [Boolean] mutable (false)
85
+ # Whether this is a read-only or read/write transaction.
28
86
  # @param [Hash{Symbol => Object}] options
29
87
  # @yield [tx]
30
88
  # @yieldparam [RDF::Transaction] tx
31
89
  # @return [void]
32
- def self.execute(repository, options = {}, &block)
33
- self.new(&block).execute(repository, options)
90
+ def self.begin(repository, mutable: false, **options, &block)
91
+ self.new(repository, options.merge(mutable: mutable), &block)
34
92
  end
35
93
 
36
94
  ##
37
- # Name of this graph, if it is part of an {RDF::Repository}
38
- # @!attribute [rw] graph_name
39
- # @return [RDF::Resource]
40
- # @since 1.1.0
41
- attr_accessor :graph_name
42
-
43
- # @deprecated Use {#graph_name} instead.
44
- def context
45
- warn "[DEPRECATION] Statement#context is being replaced with Statement@graph_name in RDF.rb 2.0. Called from #{Gem.location_of_caller.join(':')}"
46
- graph_name
47
- end
95
+ # The repository being operated upon.
96
+ #
97
+ # @return [RDF::Repository]
98
+ # @since 2.0.0
99
+ attr_reader :repository
48
100
 
49
- # @deprecated Use {#graph_name=} instead.
50
- def context=(value)
51
- warn "[DEPRECATION] Statement#context= is being replaced with Statement@graph_name= in RDF.rb 2.0. Called from #{Gem.location_of_caller.join(':')}"
52
- self.graph_name = value
53
- end
101
+ ##
102
+ # The default graph name to apply to statements inserted or deleted by the
103
+ # transaction.
104
+ #
105
+ # @return [RDF::Resource, nil]
106
+ # @since 2.0.0
107
+ attr_reader :graph_name
54
108
 
55
- alias_method :graph, :graph_name
56
- alias_method :graph=, :graph_name=
109
+ ##
110
+ # RDF statement mutations to apply when executed.
111
+ #
112
+ # @return [RDF::Changeset]
113
+ # @since 2.0.0
114
+ attr_reader :changes
57
115
 
58
116
  ##
59
117
  # RDF statements to delete when executed.
60
118
  #
119
+ # @deprecated
61
120
  # @return [RDF::Enumerable]
62
121
  attr_reader :deletes
122
+ def deletes
123
+ self.changes.deletes
124
+ end
63
125
 
64
126
  ##
65
127
  # RDF statements to insert when executed.
66
128
  #
129
+ # @deprecated
67
130
  # @return [RDF::Enumerable]
68
131
  attr_reader :inserts
132
+ def inserts
133
+ self.changes.inserts
134
+ end
69
135
 
70
136
  ##
71
137
  # Any additional options for this transaction.
@@ -77,76 +143,70 @@ module RDF
77
143
  # Initializes this transaction.
78
144
  #
79
145
  # @param [Hash{Symbol => Object}] options
80
- # @option options [RDF::Resource] :context (nil)
81
- # Alias for `:graph_name`. Deprected in RDF.rb 2.0.
82
- # @option options [RDF::Resource] :graph_name (nil)
83
- # Name of named graph to be affected if `inserts` or `deletes`
84
- # do not have a `graph_name`.
85
- # @option options [RDF::Resource] :graph (nil)
86
- # Alias for `:graph_name`.
87
- # @option options [RDF::Enumerable] :insert (RDF::Graph.new)
88
- # @option options [RDF::Enumerable] :delete (RDF::Graph.new)
146
+ # @param [Boolean] mutable (false)
147
+ # Whether this is a read-only or read/write transaction.
89
148
  # @yield [tx]
90
149
  # @yieldparam [RDF::Transaction] tx
91
- def initialize(options = {}, &block)
92
- @options = options.dup
93
- if @options.has_key?(:context)
94
- warn "[DEPRECATION] the :contexts option to Mutable#load is deprecated in RDF.rb 2.0, use :graph_name instead. Called from #{Gem.location_of_caller.join(':')}"
95
- @options[:graph_name] ||= @options.delete(:context)
96
- end
97
- @graph_name = @options.delete(:graph) || @options.delete(:graph_name)
98
- @inserts = @options.delete(:insert) || []
99
- @deletes = @options.delete(:delete) || []
100
- @inserts.extend(RDF::Enumerable) unless @inserts.kind_of?(RDF::Enumerable)
101
- @deletes.extend(RDF::Enumerable) unless @deletes.kind_of?(RDF::Enumerable)
150
+ def initialize(repository, graph_name: nil, mutable: false, **options, &block)
151
+ @repository = repository
152
+ @snapshot =
153
+ repository.supports?(:snapshots) ? repository.snapshot : repository
154
+ @options = options.dup
155
+ @mutable = mutable
156
+ @graph_name = graph_name
102
157
 
158
+ raise TransactionError,
159
+ 'Tried to open a mutable transaction on an immutable repository' if
160
+ @mutable && !@repository.mutable?
161
+
162
+ @changes = RDF::Changeset.new
163
+
103
164
  if block_given?
104
165
  case block.arity
105
166
  when 1 then block.call(self)
106
- else instance_eval(&block)
167
+ else self.instance_eval(&block)
107
168
  end
108
169
  end
109
170
  end
171
+
172
+ ##
173
+ # @see RDF::Dataset#isolation_level
174
+ def isolation_level
175
+ return :repeatable_read if repository.supports?(:snapshots)
176
+ :read_committed
177
+ end
110
178
 
111
179
  ##
112
- # Returns `false` to indicate that this transaction is append-only.
113
- #
114
- # Transactions do not support the `RDF::Enumerable` protocol directly.
115
- # To enumerate the RDF statements to be inserted or deleted, use the
116
- # {RDF::Transaction#inserts} and {RDF::Transaction#deletes} accessors.
180
+ # Returns `true` if this is a read/write transaction, `false` otherwise.
117
181
  #
118
182
  # @return [Boolean]
119
- # @see RDF::Readable#readable?
120
- def readable?
121
- false
183
+ # @see RDF::Writable#writable?
184
+ def writable?
185
+ @mutable
122
186
  end
123
187
 
124
188
  ##
125
- # Executes this transaction against the given RDF repository.
189
+ # Returns `true` if this is a read/write transaction, `false` otherwise.
126
190
  #
127
- # @param [RDF::Repository] repository
128
- # @param [Hash{Symbol => Object}] options
129
- # @return [void]
130
- def execute(repository, options = {})
131
- before_execute(repository, options) if respond_to?(:before_execute)
191
+ # @return [Boolean]
192
+ # @see RDF::Writable#mutable?
193
+ def mutable?
194
+ @mutable
195
+ end
132
196
 
133
- dels = deletes.map do |s|
134
- statement = s.dup
135
- statement.graph_name ||= graph_name
136
- statement
137
- end
197
+ ##
198
+ # Returns `true` to indicate that this transaction is readable.
199
+ #
200
+ # @return [Boolean]
201
+ # @see RDF::Readable#readable?
202
+ def readable?
203
+ true
204
+ end
138
205
 
139
- ins = inserts.map do |s|
140
- statement = s.dup
141
- statement.graph_name ||= graph_name
142
- statement
143
- end
144
-
145
- repository.delete(*dels) unless dels.empty?
146
- repository.insert(*ins) unless ins.empty?
147
-
148
- after_execute(repository, options) if respond_to?(:after_execute)
149
- self
206
+ ##
207
+ # @see RDF::Enumerable#has_statement?
208
+ def has_statement?(statement)
209
+ read_target.has_statement?(statement)
150
210
  end
151
211
 
152
212
  ##
@@ -154,8 +214,8 @@ module RDF
154
214
  #
155
215
  # @return [String]
156
216
  def inspect
157
- sprintf("#<%s:%#0x(graph: %s, deletes: %d, inserts: %d)>", self.class.name, __id__,
158
- graph ? graph.to_s : 'nil', deletes.count, inserts.count)
217
+ sprintf("#<%s:%#0x(changes: -%d/+%d)>", self.class.name,
218
+ self.__id__, self.changes.deletes.count, self.changes.inserts.count)
159
219
  end
160
220
 
161
221
  ##
@@ -164,10 +224,38 @@ module RDF
164
224
  #
165
225
  # @return [void]
166
226
  def inspect!
167
- warn(inspect)
227
+ $stderr.puts(inspect)
228
+ end
229
+
230
+ ##
231
+ # Executes the transaction
232
+ #
233
+ # @return [Boolean] `true` if the changes are successfully applied.
234
+ # @raise [TransactionError] if the transaction can't be applied
235
+ def execute
236
+ raise TransactionError, 'Cannot execute a rolled back transaction. ' \
237
+ 'Open a new one instead.' if @rolledback
238
+ @changes.apply(@repository)
239
+ end
240
+
241
+ ##
242
+ # Rolls back the transaction
243
+ #
244
+ # @note: the base class simply replaces its current `Changeset` with a
245
+ # fresh one. Other implementations may need to explictly rollback
246
+ # at the supporting datastore.
247
+ #
248
+ # @note: clients should not rely on using same transaction instance after
249
+ # rollback.
250
+ #
251
+ # @return [Boolean] `true` if the changes are successfully applied.
252
+ def rollback
253
+ @changes = RDF::Changeset.new
254
+ @rolledback = true
168
255
  end
169
256
 
170
257
  protected
258
+
171
259
  ##
172
260
  # Appends an RDF statement to the sequence to insert when executed.
173
261
  #
@@ -175,7 +263,7 @@ module RDF
175
263
  # @return [void]
176
264
  # @see RDF::Writable#insert_statement
177
265
  def insert_statement(statement)
178
- inserts << statement
266
+ @changes.insert(process_statement(statement))
179
267
  end
180
268
 
181
269
  ##
@@ -185,9 +273,48 @@ module RDF
185
273
  # @return [void]
186
274
  # @see RDF::Mutable#delete_statement
187
275
  def delete_statement(statement)
188
- deletes << statement
276
+ @changes.delete(process_statement(statement))
277
+ end
278
+
279
+ def query_pattern(*args, &block)
280
+ read_target.send(:query_pattern, *args, &block)
189
281
  end
190
282
 
283
+ def query_execute(*args, &block)
284
+ read_target.send(:query_execute, *args, &block)
285
+ end
286
+
191
287
  undef_method :load, :update, :clear
288
+
289
+ private
290
+
291
+ ##
292
+ # @private Adds the default graph_name to the statement, when one it does
293
+ # not already have one.
294
+ #
295
+ # @param statement [RDF::Statement]
296
+ # @return [RDF::Statement]
297
+ def process_statement(statement)
298
+ if graph_name
299
+ statement = statement.dup
300
+ statement.graph_name = graph_name
301
+ end
302
+ statement
303
+ end
304
+
305
+ def read_target
306
+ return @snapshot if graph_name.nil?
307
+ return @snapshot.project_graph(nil) if graph_name == false
308
+ @snapshot.project_graph(graph_name)
309
+ end
310
+
311
+ public
312
+
313
+ ##
314
+ # An error class for transaction failures.
315
+ #
316
+ # This error indicates that the transaction semantics have been violated in
317
+ # some way.
318
+ class TransactionError < RuntimeError; end
192
319
  end # Transaction
193
320
  end # RDF
@@ -2,5 +2,6 @@ module RDF; module Util
2
2
  autoload :Aliasing, 'rdf/util/aliasing'
3
3
  autoload :Cache, 'rdf/util/cache'
4
4
  autoload :File, 'rdf/util/file'
5
+ autoload :Logger, 'rdf/util/logger'
5
6
  autoload :UUID, 'rdf/util/uuid'
6
7
  end; end
@@ -56,12 +56,12 @@ module RDF; module Util
56
56
 
57
57
  ##
58
58
  # This implementation relies on `ObjectSpace#_id2ref` and performs
59
- # optimally on Ruby >= 1.9.x; however, it does not work on JRuby
59
+ # optimally on Ruby >= 2.x; however, it does not work on JRuby
60
60
  # by default since much `ObjectSpace` functionality on that platform is
61
61
  # disabled unless the `-X+O` startup option is given.
62
62
  #
63
- # @see http://www.ruby-doc.org/core-1.9/ObjectSpace.html
64
- # @see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/weakref/rdoc/WeakRef.html
63
+ # @see http://ruby-doc.org/core-2.2.2/ObjectSpace.html
64
+ # @see http://ruby-doc.org/stdlib-2.2.0/libdoc/weakref/rdoc/WeakRef.html
65
65
  class ObjectSpaceCache < Cache
66
66
  ##
67
67
  # @param [Object] key
@@ -100,9 +100,9 @@ module RDF; module Util
100
100
 
101
101
  ##
102
102
  # This implementation uses the `WeakRef` class from Ruby's standard
103
- # library, and provides adequate performance on JRuby and on Ruby 1.9.x.
103
+ # library, and provides adequate performance on JRuby and on Ruby 2.x.
104
104
  #
105
- # @see http://ruby-doc.org/ruby-1.9/classes/WeakRef.html
105
+ # @see http://ruby-doc.org/stdlib-2.2.0/libdoc/weakref/rdoc/WeakRef.html
106
106
  class WeakRefCache < Cache
107
107
  ##
108
108
  # @param [Integer] capacity
@@ -324,7 +324,11 @@ module RDF; module Util
324
324
  content_type = format ? format.content_type.first : 'text/plain'
325
325
  # Open as a file, passing any options
326
326
  begin
327
- Kernel.open(filename_or_url, "r:utf-8", options) do |file|
327
+ url_no_frag_or_query = RDF::URI(filename_or_url)
328
+ url_no_frag_or_query.query = nil
329
+ url_no_frag_or_query.fragment = nil
330
+ options[:encoding] ||= Encoding::UTF_8
331
+ Kernel.open(url_no_frag_or_query, "r", options) do |file|
328
332
  document_options = {
329
333
  base_uri: filename_or_url.to_s,
330
334
  charset: file.external_encoding.to_s,
@@ -421,15 +425,14 @@ module RDF; module Util
421
425
  unless encoding.start_with?("utf")
422
426
  body.force_encoding(Encoding::UTF_8)
423
427
  encoding = "utf-8"
424
- end
425
428
 
426
- # Make sure Unicode is in NFC
427
-
428
- begin
429
- body.unicode_normalize! unless !body.unicode_normalized?
430
- rescue Encoding::CompatibilityError
431
- # Oh, well ...
432
- end if body.respond_to?(:unicode_normalized?)
429
+ # Make sure Unicode is in NFC
430
+ begin
431
+ body.unicode_normalize! unless !body.unicode_normalized?
432
+ rescue Encoding::CompatibilityError
433
+ # Oh, well ...
434
+ end if body.respond_to?(:unicode_normalized?)
435
+ end
433
436
 
434
437
  super(body, "r:#{encoding}")
435
438
  end