rdf 1.1.17.1 → 1.99.0

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