rdf 1.1.17.1 → 1.99.0

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.
@@ -96,13 +96,19 @@ module RDF
96
96
  # @param [Hash{Symbol => Object}] options
97
97
  # @option options [URI, #to_s] :uri (nil)
98
98
  # @option options [String, #to_s] :title (nil)
99
- # @option options [Boolean] :with_context (true)
99
+ # @option options [Boolean] :with_graph_name (true)
100
100
  # Indicates that the repository supports named graphs, otherwise,
101
101
  # only the default graph is supported.
102
+ # @option options [Boolean] :with_context (true)
103
+ # Alias for :with_graph_name. :with_context is deprecated in RDF.rb 2.0.
102
104
  # @yield [repository]
103
105
  # @yieldparam [Repository] repository
104
106
  def initialize(options = {}, &block)
105
- @options = {:with_context => true}.merge(options)
107
+ if options[:with_context]
108
+ warn "[DEPRECATION] the :contexts option to Repository#initialize is deprecated in RDF.rb 2.0, use :graph_name instead. Called from #{Gem.location_of_caller.join(':')}"
109
+ options[:graph_name] ||= options.delete(:with_context)
110
+ end
111
+ @options = {with_graph_name: true}.merge(options)
106
112
  @uri = @options.delete(:uri)
107
113
  @title = @options.delete(:title)
108
114
 
@@ -140,20 +146,20 @@ module RDF
140
146
  #
141
147
  # @example
142
148
  # repository.transaction do |tx|
143
- # tx.insert [RDF::URI("http://rubygems.org/gems/rdf"), RDF::DC.title, "RDF.rb"]
149
+ # tx.insert [RDF::URI("http://rubygems.org/gems/rdf"), RDF::RDFS.label, "RDF.rb"]
144
150
  # end
145
151
  #
146
- # @param [RDF::Resource] context
152
+ # @param [RDF::Resource] graph_name
147
153
  # Context on which to run the transaction, use `false` for the default
148
- # context and `nil` the entire Repository
154
+ # graph_name and `nil` the entire Repository
149
155
  # @yield [tx]
150
156
  # @yieldparam [RDF::Transaction] tx
151
157
  # @yieldreturn [void] ignored
152
- # @return [void] `self`
158
+ # @return [self]
153
159
  # @see RDF::Transaction
154
160
  # @since 0.3.0
155
- def transaction(context = nil, &block)
156
- tx = begin_transaction(context)
161
+ def transaction(graph_name = nil, &block)
162
+ tx = begin_transaction(graph_name)
157
163
  begin
158
164
  case block.arity
159
165
  when 1 then block.call(tx)
@@ -177,11 +183,11 @@ module RDF
177
183
  # to override this method in order to begin a transaction against the
178
184
  # underlying storage.
179
185
  #
180
- # @param [RDF::Resource] context
186
+ # @param [RDF::Resource] graph_name
181
187
  # @return [RDF::Transaction]
182
188
  # @since 0.3.0
183
- def begin_transaction(context)
184
- RDF::Transaction.new(:graph => context)
189
+ def begin_transaction(graph_name)
190
+ RDF::Transaction.new(graph: graph_name)
185
191
  end
186
192
 
187
193
  ##
@@ -215,7 +221,7 @@ module RDF
215
221
  ##
216
222
  # @see RDF::Repository
217
223
  module Implementation
218
- DEFAULT_CONTEXT = false
224
+ DEFAULT_GRAPH = false
219
225
 
220
226
  ##
221
227
  # @private
@@ -229,8 +235,11 @@ module RDF
229
235
  # @see RDF::Enumerable#supports?
230
236
  def supports?(feature)
231
237
  case feature.to_sym
232
- # statement contexts / named graphs
233
- when :context then @options[:with_context]
238
+ #statement named graphs
239
+ when :context
240
+ warn "[DEPRECATION] the :context feature is deprecated in RDF.rb 2.0; use :graph_name instead. Called from #{Gem.location_of_caller.join(':')}"
241
+ @options[:with_context] || @options[:with_graph_name]
242
+ when :graph_name then @options[:with_graph_name]
234
243
  when :inference then false # forward-chaining inference
235
244
  when :validity then @options.fetch(:with_validity, true)
236
245
  else false
@@ -249,7 +258,7 @@ module RDF
249
258
  # @see RDF::Countable#count
250
259
  def count
251
260
  count = 0
252
- @data.each do |c, ss|
261
+ @data.each do |g, ss|
253
262
  ss.each do |s, ps|
254
263
  ps.each do |p, os|
255
264
  count += os.size
@@ -263,12 +272,12 @@ module RDF
263
272
  # @private
264
273
  # @see RDF::Enumerable#has_statement?
265
274
  def has_statement?(statement)
266
- s, p, o, c = statement.to_quad
267
- c ||= DEFAULT_CONTEXT
268
- @data.has_key?(c) &&
269
- @data[c].has_key?(s) &&
270
- @data[c][s].has_key?(p) &&
271
- @data[c][s][p].include?(o)
275
+ s, p, o, g = statement.to_quad
276
+ g ||= DEFAULT_GRAPH
277
+ @data.has_key?(g) &&
278
+ @data[g].has_key?(s) &&
279
+ @data[g][s].has_key?(p) &&
280
+ @data[g][s][p].include?(o)
272
281
  end
273
282
 
274
283
  ##
@@ -280,12 +289,12 @@ module RDF
280
289
  # possible concurrent mutations to `@data`, we use `#dup` to make
281
290
  # shallow copies of the nested hashes before beginning the
282
291
  # iteration over their keys and values.
283
- @data.dup.each do |c, ss|
292
+ @data.dup.each do |g, ss|
284
293
  ss.dup.each do |s, ps|
285
294
  ps.dup.each do |p, os|
286
295
  os.dup.each do |o|
287
296
  # FIXME: yield has better performance, but broken in MRI 2.2: See https://bugs.ruby-lang.org/issues/11451.
288
- block.call(RDF::Statement.new(s, p, o, :context => c.equal?(DEFAULT_CONTEXT) ? nil : c))
297
+ block.call(RDF::Statement.new(s, p, o, graph_name: g.equal?(DEFAULT_GRAPH) ? nil : g))
289
298
  end
290
299
  end
291
300
  end
@@ -298,48 +307,87 @@ module RDF
298
307
  ##
299
308
  # @private
300
309
  # @see RDF::Enumerable#has_context?
310
+ # @deprecated Use {#has_graph?} instead.
301
311
  def has_context?(value)
312
+ warn "[DEPRECATION] Repository#has_context? is deprecated in RDF.rb 2.0, use Repository#has_graph? instead. Called from #{Gem.location_of_caller.join(':')}"
313
+ has_graph?(value)
314
+ end
315
+
316
+ ##
317
+ # @private
318
+ # @see RDF::Enumerable#has_graph?
319
+ def has_graph?(value)
302
320
  @data.keys.include?(value)
303
321
  end
322
+ ##
323
+ # @private
324
+ # @see RDF::Enumerable#each_graph
325
+ def graph_names(options = nil, &block)
326
+ @data.keys.reject {|g| g == DEFAULT_GRAPH}
327
+ end
304
328
 
305
329
  ##
306
330
  # @private
307
331
  # @see RDF::Enumerable#each_context
332
+ # @deprecated Use {#each_graph} instead.
308
333
  def each_context(&block)
334
+ warn "[DEPRECATION] Repository#each_context is deprecated in RDF.rb 2.0, use Repository#each_graph instead. Called from #{Gem.location_of_caller.join(':')}"
309
335
  if block_given?
310
336
  contexts = @data.keys
311
- contexts.delete(DEFAULT_CONTEXT)
337
+ contexts.delete(DEFAULT_GRAPH)
312
338
  contexts.each(&block)
313
339
  end
314
340
  enum_context
315
341
  end
316
342
 
343
+ ##
344
+ # @private
345
+ # @see RDF::Enumerable#each_graph
346
+ def each_graph(&block)
347
+ if block_given?
348
+ @data.each_key do |gn|
349
+ yield RDF::Graph.new(gn == DEFAULT_GRAPH ? nil : gn, data: self)
350
+ end
351
+ end
352
+ enum_graph
353
+ end
354
+
317
355
  protected
318
356
 
319
357
  ##
320
358
  # Match elements with eql?, not ==
321
- # Context of `false` matches default context. Unbound variable matches non-false context
359
+ # Context of `false` matches default graph. Unbound variable matches non-false graph name
322
360
  # @private
323
361
  # @see RDF::Queryable#query
324
362
  def query_pattern(pattern, &block)
325
- context = pattern.context
363
+ graph_name = pattern.graph_name
326
364
  subject = pattern.subject
327
365
  predicate = pattern.predicate
328
366
  object = pattern.object
329
367
 
330
- cs = @data.has_key?(context) ? {context => @data[context]} : @data.dup
368
+ cs = @data.has_key?(graph_name) ? {graph_name => @data[graph_name]} : @data.dup
331
369
  cs.each do |c, ss|
332
- next unless context.nil? || context == false && !c || context.eql?(c)
333
- ss = ss.has_key?(subject) ? {subject => ss[subject]} : ss.dup
370
+ next unless graph_name.nil? || graph_name == false && !c || graph_name.eql?(c)
371
+ ss = if ss.has_key?(subject)
372
+ { subject => ss[subject] }
373
+ elsif subject.nil? || subject.is_a?(RDF::Query::Variable)
374
+ ss.dup
375
+ else
376
+ []
377
+ end
334
378
  ss.each do |s, ps|
335
- next unless subject.nil? || subject.eql?(s)
336
- ps = ps.has_key?(predicate) ? {predicate => ps[predicate]} : ps.dup
379
+ ps = if ps.has_key?(predicate)
380
+ { predicate => ps[predicate] }
381
+ elsif predicate.nil? || predicate.is_a?(RDF::Query::Variable)
382
+ ps.dup
383
+ else
384
+ []
385
+ end
337
386
  ps.each do |p, os|
338
- next unless predicate.nil? || predicate.eql?(p)
339
387
  os = os.dup # TODO: is this really needed?
340
388
  os.each do |o|
341
389
  next unless object.nil? || object.eql?(o)
342
- block.call(RDF::Statement.new(s, p, o, :context => c.equal?(DEFAULT_CONTEXT) ? nil : c))
390
+ block.call(RDF::Statement.new(s, p, o, graph_name: c.equal?(DEFAULT_GRAPH) ? nil : c))
343
391
  end
344
392
  end
345
393
  end
@@ -350,10 +398,11 @@ module RDF
350
398
  # @private
351
399
  # @see RDF::Mutable#insert
352
400
  def insert_statement(statement)
401
+ raise ArgumentError, "Statement #{statement.inspect} is incomplete" if statement.incomplete?
353
402
  unless has_statement?(statement)
354
403
  s, p, o, c = statement.to_quad
355
- c = DEFAULT_CONTEXT unless supports?(:context)
356
- c ||= DEFAULT_CONTEXT
404
+ c = DEFAULT_GRAPH unless supports?(:graph_name)
405
+ c ||= DEFAULT_GRAPH
357
406
  @data[c] ||= {}
358
407
  @data[c][s] ||= {}
359
408
  @data[c][s][p] ||= []
@@ -367,8 +416,8 @@ module RDF
367
416
  def delete_statement(statement)
368
417
  if has_statement?(statement)
369
418
  s, p, o, c = statement.to_quad
370
- c = DEFAULT_CONTEXT unless supports?(:context)
371
- c ||= DEFAULT_CONTEXT
419
+ c = DEFAULT_GRAPH unless supports?(:graph_name)
420
+ c ||= DEFAULT_GRAPH
372
421
  @data[c][s][p].delete(o)
373
422
  @data[c][s].delete(p) if @data[c][s][p].empty?
374
423
  @data[c].delete(s) if @data[c][s].empty?
@@ -13,8 +13,8 @@ module RDF
13
13
  # repository = ...
14
14
  # RDF::Transaction.execute(repository) do |tx|
15
15
  # subject = RDF::URI("http://example.org/article")
16
- # tx.delete [subject, RDF::DC.title, "Old title"]
17
- # tx.insert [subject, RDF::DC.title, "New title"]
16
+ # tx.delete [subject, RDF::RDFS.label, "Old title"]
17
+ # tx.insert [subject, RDF::RDFS.label, "New title"]
18
18
  # end
19
19
  #
20
20
  # @since 0.3.0
@@ -35,13 +35,25 @@ module RDF
35
35
 
36
36
  ##
37
37
  # Name of this graph, if it is part of an {RDF::Repository}
38
- # @!attribute [rw] context
38
+ # @!attribute [rw] graph_name
39
39
  # @return [RDF::Resource]
40
40
  # @since 1.1.0
41
- attr_accessor :context
41
+ attr_accessor :graph_name
42
42
 
43
- alias_method :graph, :context
44
- alias_method :graph=, :context=
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
48
+
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
54
+
55
+ alias_method :graph, :graph_name
56
+ alias_method :graph=, :graph_name=
45
57
 
46
58
  ##
47
59
  # RDF statements to delete when executed.
@@ -66,19 +78,25 @@ module RDF
66
78
  #
67
79
  # @param [Hash{Symbol => Object}] options
68
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)
69
83
  # Name of named graph to be affected if `inserts` or `deletes`
70
- # do not have a `context`.
84
+ # do not have a `graph_name`.
71
85
  # @option options [RDF::Resource] :graph (nil)
72
- # Alias for `:context`.
86
+ # Alias for `:graph_name`.
73
87
  # @option options [RDF::Enumerable] :insert (RDF::Graph.new)
74
88
  # @option options [RDF::Enumerable] :delete (RDF::Graph.new)
75
89
  # @yield [tx]
76
90
  # @yieldparam [RDF::Transaction] tx
77
91
  def initialize(options = {}, &block)
78
92
  @options = options.dup
79
- @context = @options.delete(:graph) || @options.delete(:context)
80
- @inserts = @options.delete(:insert) || RDF::Graph.new
81
- @deletes = @options.delete(:delete) || RDF::Graph.new
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) || []
82
100
  @inserts.extend(RDF::Enumerable) unless @inserts.kind_of?(RDF::Enumerable)
83
101
  @deletes.extend(RDF::Enumerable) unless @deletes.kind_of?(RDF::Enumerable)
84
102
 
@@ -112,18 +130,21 @@ module RDF
112
130
  def execute(repository, options = {})
113
131
  before_execute(repository, options) if respond_to?(:before_execute)
114
132
 
115
- deletes.each_statement do |statement|
116
- statement = statement.dup
117
- statement.context ||= graph
118
- repository.delete(statement)
133
+ dels = deletes.map do |s|
134
+ statement = s.dup
135
+ statement.graph_name ||= graph_name
136
+ statement
119
137
  end
120
138
 
121
- inserts.each_statement do |statement|
122
- statement = statement.dup
123
- statement.context ||= graph
124
- repository.insert(statement)
139
+ ins = inserts.map do |s|
140
+ statement = s.dup
141
+ statement.graph_name ||= graph_name
142
+ statement
125
143
  end
126
-
144
+
145
+ repository.delete(*dels) unless dels.empty?
146
+ repository.insert(*ins) unless ins.empty?
147
+
127
148
  after_execute(repository, options) if respond_to?(:after_execute)
128
149
  self
129
150
  end
data/lib/rdf/util/file.rb CHANGED
@@ -100,7 +100,6 @@ module RDF; module Util
100
100
  # If a Location is returned, it defines the base resource for this file, not it's actual ending location
101
101
  document_options = {
102
102
  base_uri: RDF::URI(response.headers.fetch(:location, base_uri)),
103
- charset: Encoding::UTF_8,
104
103
  code: response.code.to_i,
105
104
  headers: response.headers
106
105
  }
@@ -159,7 +158,6 @@ module RDF; module Util
159
158
  # If a Location is returned, it defines the base resource for this file, not it's actual ending location
160
159
  document_options = {
161
160
  base_uri: RDF::URI(response["Location"] ? response["Location"] : base_uri),
162
- charset: Encoding::UTF_8,
163
161
  code: response.code.to_i,
164
162
  content_type: response.content_type,
165
163
  headers: response_headers
@@ -226,7 +224,6 @@ module RDF; module Util
226
224
  # If a Location is returned, it defines the base resource for this file, not it's actual ending location
227
225
  document_options = {
228
226
  base_uri: RDF::URI(response.headers.fetch(:location, response.env.url)),
229
- charset: Encoding::UTF_8,
230
227
  code: response.status,
231
228
  headers: response.headers
232
229
  }
@@ -279,6 +276,8 @@ module RDF; module Util
279
276
  # Adds Accept header based on available reader content types to allow
280
277
  # for content negotiation based on available readers.
281
278
  #
279
+ # Input received as non-unicode, is transformed to UTF-8. With Ruby >= 2.2, all UTF is normalized to [Unicode Normalization Form C (NFC)](http://unicode.org/reports/tr15/#Norm_Forms).
280
+ #
282
281
  # HTTP resources may be retrieved via proxy using the `proxy` option. If `RestClient` is loaded, they will use the proxy globally by setting something like the following:
283
282
  # `RestClient.proxy = "http://proxy.example.com/"`.
284
283
  # When retrieving documents over HTTP(S), use the mechanism described in [Providing and Discovering URI Documentation](http://www.w3.org/2001/tag/awwsw/issue57/latest/) to pass the appropriate `base_uri` to the block or as the return.
@@ -328,11 +327,11 @@ module RDF; module Util
328
327
  Kernel.open(filename_or_url, "r:utf-8", options) do |file|
329
328
  document_options = {
330
329
  base_uri: filename_or_url.to_s,
331
- charset: file.external_encoding,
330
+ charset: file.external_encoding.to_s,
332
331
  code: 200,
333
332
  content_type: content_type,
334
333
  last_modified:file.mtime,
335
- headers: {'Content-Type' => content_type, 'Last-Modified' => file.mtime.xmlschema}
334
+ headers: {content_type: content_type, last_modified: file.mtime.xmlschema}
336
335
  }
337
336
 
338
337
  remote_document = RemoteDocument.new(file.read, document_options)
@@ -363,8 +362,8 @@ module RDF; module Util
363
362
  # @return [String]
364
363
  attr_reader :content_type
365
364
 
366
- # Encoding of resource (from header), also applied to content
367
- # @return [Encoding}]
365
+ # Encoding of resource (from Content-Type), downcased. Also applied to content if it is UTF
366
+ # @return [String}]
368
367
  attr_reader :charset
369
368
 
370
369
  # Response code
@@ -381,7 +380,7 @@ module RDF; module Util
381
380
  attr_reader :last_modified
382
381
 
383
382
  # Raw headers from response
384
- # @return [Hash{String => Object}]
383
+ # @return [Hash{Symbol => Object}]
385
384
  attr_reader :headers
386
385
 
387
386
  # Originally requested URL
@@ -390,40 +389,58 @@ module RDF; module Util
390
389
 
391
390
  ##
392
391
  # Set content
393
- # @param [String] body entiry content of request.
392
+ # @param [String] body entity content of request.
394
393
  def initialize(body, options = {})
395
- super(body)
396
394
  options.each do |key, value|
397
395
  # de-quote charset
398
396
  matchdata = value.match(/^["'](.*)["']$/.freeze) if key == "charset"
399
397
  value = matchdata[1] if matchdata
398
+ value = value.downcase if value.is_a?(String)
400
399
  instance_variable_set(:"@#{key}", value)
401
400
  end
402
- @headers ||= {}
401
+ @headers = options.fetch(:headers, {})
402
+ @charset = options[:charset].to_s.downcase if options[:charset]
403
403
 
404
404
  # Find Content-Type
405
- if !@content_type && headers[:content_type]
406
- @content_type, *params = headers[:content_type].split(';').map(&:strip)
405
+ if headers[:content_type]
406
+ ct, *params = headers[:content_type].split(';').map(&:strip)
407
+ @content_type ||= ct
407
408
 
408
409
  # Find charset
409
410
  params.each do |param|
410
411
  p, v = param.split('=')
411
412
  next unless p.downcase == 'charset'
412
- @charset = v.sub(/^["']?(.*)["']?$/, '\1')
413
+ @charset ||= v.sub(/^["']?(.*)["']?$/, '\1').downcase
413
414
  end
414
415
  end
415
416
 
416
417
  @etag = headers[:etag]
417
418
  @last_modified = DateTime.parse(headers[:last_modified]) if headers[:last_modified]
419
+ encoding = @charset ||= "utf-8"
420
+
421
+ unless encoding.start_with?("utf")
422
+ body.force_encoding(Encoding::UTF_8)
423
+ encoding = "utf-8"
424
+ end
418
425
 
419
- set_encoding Encoding.find(@charset) if @charset
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?)
433
+
434
+ super(body, "r:#{encoding}")
420
435
  end
421
436
 
422
437
  ##
423
- # Content encoding, based on {#charset} normalized to lower-case
424
- # @return [String]
438
+ # Returns a list of encodings in Content-Encoding field as an array of strings.
439
+ #
440
+ # The encodings are downcased for canonicalization.
441
+ # @return [Array<String>]
425
442
  def content_encoding
426
- charset.to_s.downcase
443
+ headers.fetch(:content_encoding, "").split(',').map(&:strip).map(&:downcase)
427
444
  end
428
445
 
429
446
  ##