rdf 3.0.13 → 3.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
- require 'uri'
2
+ require 'cgi'
3
3
 
4
4
  module RDF
5
5
  ##
@@ -141,8 +141,8 @@ module RDF
141
141
  #
142
142
  # (see #initialize)
143
143
  # @return [RDF::URI] an immutable, frozen URI object
144
- def self.intern(str, *args)
145
- (cache[(str = str.to_s).to_sym] ||= self.new(str, *args)).freeze
144
+ def self.intern(str, *args, **options)
145
+ (cache[(str = str.to_s).to_sym] ||= self.new(str, *args, **options)).freeze
146
146
  end
147
147
 
148
148
  ##
@@ -225,11 +225,9 @@ module RDF
225
225
  @mutex = Mutex.new
226
226
  uri = args.first
227
227
  if uri
228
- @value = uri.to_s
229
- if @value.encoding != Encoding::UTF_8
230
- @value.dup.force_encoding(Encoding::UTF_8)
231
- @value.freeze
232
- end
228
+ @value = uri.to_s.dup
229
+ @value.dup.force_encoding(Encoding::UTF_8) if @value.encoding != Encoding::UTF_8
230
+ @value.freeze
233
231
  else
234
232
  %w(
235
233
  scheme
@@ -402,7 +400,7 @@ module RDF
402
400
  # @example Joining two URIs
403
401
  # RDF::URI.new('http://example.org/foo/bar').join('/foo')
404
402
  # #=> RDF::URI('http://example.org/foo')
405
- # @see <http://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
403
+ # @see <https://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
406
404
  # @see <http://tools.ietf.org/html/rfc3986#section-5.2>
407
405
  # @see RDF::URI#/
408
406
  # @see RDF::URI#+
@@ -441,7 +439,7 @@ module RDF
441
439
  end
442
440
 
443
441
  # Return joined URI
444
- RDF::URI.new(joined_parts)
442
+ RDF::URI.new(**joined_parts)
445
443
  end
446
444
 
447
445
  ##
@@ -473,7 +471,7 @@ module RDF
473
471
  # @see RDF::URI#+
474
472
  # @see RDF::URI#join
475
473
  # @see <http://tools.ietf.org/html/rfc3986#section-5.2>
476
- # @see <http://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
474
+ # @see <https://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
477
475
  # @example Building a HTTP URL
478
476
  # RDF::URI.new('http://example.org') / 'jhacker' / 'foaf.ttl'
479
477
  # #=> RDF::URI('http://example.org/jhacker/foaf.ttl')
@@ -509,7 +507,7 @@ module RDF
509
507
  case fragment.to_s[0,1]
510
508
  when '#'
511
509
  # Base ending with '/', fragment beginning with '#'. The fragment wins, we use '#'.
512
- res.path = res.path.to_s.sub!(/\/*$/, '')
510
+ res.path = res.path.to_s.sub(/\/*$/, '')
513
511
  # Add fragment
514
512
  res.fragment = fragment.to_s.sub(/^#+/,'')
515
513
  else
@@ -574,7 +572,7 @@ module RDF
574
572
  self
575
573
  else
576
574
  RDF::URI.new(
577
- object.merge(path: '/').
575
+ **object.merge(path: '/').
578
576
  keep_if {|k, v| [:scheme, :authority, :path].include?(k)})
579
577
  end
580
578
  end
@@ -659,7 +657,7 @@ module RDF
659
657
  #
660
658
  # @return [RDF::URI]
661
659
  def dup
662
- self.class.new((@value || @object).dup)
660
+ self.class.new(@value, **(@object || {}))
663
661
  end
664
662
 
665
663
  ##
@@ -848,7 +846,7 @@ module RDF
848
846
  parts[:user] = (user.dup.force_encoding(Encoding::UTF_8) if user)
849
847
  parts[:password] = (password.dup.force_encoding(Encoding::UTF_8) if password)
850
848
  parts[:host] = (host.dup.force_encoding(Encoding::UTF_8) if host)
851
- parts[:port] = (::URI.decode(port).to_i if port)
849
+ parts[:port] = (CGI.unescape(port).to_i if port)
852
850
  parts[:path] = (path.to_s.dup.force_encoding(Encoding::UTF_8) unless path.empty?)
853
851
  parts[:query] = (query[1..-1].dup.force_encoding(Encoding::UTF_8) if query)
854
852
  parts[:fragment] = (fragment[1..-1].dup.force_encoding(Encoding::UTF_8) if fragment)
@@ -904,7 +902,7 @@ module RDF
904
902
  # Normalized version of user
905
903
  # @return [String]
906
904
  def normalized_user
907
- ::URI.encode(::URI.decode(user), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/) if user
905
+ URI.encode(CGI.unescape(user), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/) if user
908
906
  end
909
907
 
910
908
  ##
@@ -930,7 +928,7 @@ module RDF
930
928
  # Normalized version of password
931
929
  # @return [String]
932
930
  def normalized_password
933
- ::URI.encode(::URI.decode(password), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/) if password
931
+ URI.encode(CGI.unescape(password), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/) if password
934
932
  end
935
933
 
936
934
  HOST_FROM_AUTHORITY_RE = /(?:[^@]+@)?([^:]+)(?::.*)?$/.freeze
@@ -939,7 +937,7 @@ module RDF
939
937
  # @return [String]
940
938
  def host
941
939
  object.fetch(:host) do
942
- @object[:host] = ($1 if HOST_FROM_AUTHORITY_RE.match(@object[:authority]))
940
+ @object[:host] = ($1 if @object[:authority] && HOST_FROM_AUTHORITY_RE.match(@object[:authority]))
943
941
  end
944
942
  end
945
943
 
@@ -967,7 +965,7 @@ module RDF
967
965
  # @return [String]
968
966
  def port
969
967
  object.fetch(:port) do
970
- @object[:port] = ($1 if PORT_FROM_AUTHORITY_RE.match(@object[:authority]))
968
+ @object[:port] = ($1 if @object[:authority] && PORT_FROM_AUTHORITY_RE.match(@object[:authority]))
971
969
  end
972
970
  end
973
971
 
@@ -1182,8 +1180,8 @@ module RDF
1182
1180
  inject(return_type == Hash ? {} : []) do |memo,kv|
1183
1181
  k,v = kv.to_s.split('=', 2)
1184
1182
  next if k.to_s.empty?
1185
- k = ::URI.decode(k)
1186
- v = ::URI.decode(v) if v
1183
+ k = CGI.unescape(k)
1184
+ v = CGI.unescape(v) if v
1187
1185
  if return_type == Hash
1188
1186
  case memo[k]
1189
1187
  when nil then memo[k] = v
@@ -1295,9 +1293,9 @@ module RDF
1295
1293
  def normalize_segment(value, expr, downcase = false)
1296
1294
  if value
1297
1295
  value = value.dup.force_encoding(Encoding::UTF_8)
1298
- decoded = ::URI.decode(value)
1296
+ decoded = CGI.unescape(value)
1299
1297
  decoded.downcase! if downcase
1300
- ::URI.encode(decoded, /[^(?:#{expr})]/)
1298
+ URI.encode(decoded, /[^(?:#{expr})]/)
1301
1299
  end
1302
1300
  end
1303
1301
 
@@ -1316,6 +1314,27 @@ module RDF
1316
1314
  ""
1317
1315
  end
1318
1316
  end
1317
+
1318
+ # URI encode matching characters in value
1319
+ # From URI gem, as this is now generally deprecated
1320
+ def self.encode(str, expr)
1321
+ str.gsub(expr) do
1322
+ us = $&
1323
+ tmp = ''
1324
+ us.each_byte do |uc|
1325
+ tmp << sprintf('%%%02X', uc)
1326
+ end
1327
+ tmp
1328
+ end.force_encoding(Encoding::US_ASCII)
1329
+ end
1330
+
1331
+ # URI decode escape sequences in value
1332
+ # From URI gem, as this is now generally deprecated
1333
+ def self.decode(str)
1334
+ enc = str.encoding
1335
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
1336
+ str.gsub(PCT_ENCODED) { [$&[1, 2]].pack('H2').force_encoding(enc) }
1337
+ end
1319
1338
  end
1320
1339
 
1321
1340
  # RDF::IRI is a synonym for RDF::URI
@@ -69,9 +69,9 @@ module RDF
69
69
 
70
70
  begin
71
71
  unless blank? || read_comment
72
- subject = read_uriref || read_node || fail_subject
72
+ subject = read_uriref || read_node || read_rdfstar || fail_subject
73
73
  predicate = read_uriref(intern: true) || fail_predicate
74
- object = read_uriref || read_node || read_literal || fail_object
74
+ object = read_uriref || read_node || read_literal || read_rdfstar || fail_object
75
75
  graph_name = read_uriref || read_node
76
76
  if validate? && !read_eos
77
77
  log_error("Expected end of statement (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
@@ -96,7 +96,7 @@ module RDF
96
96
  # @param [RDF::Term] object
97
97
  # @return [void]
98
98
  def write_quad(subject, predicate, object, graph_name)
99
- puts format_quad(subject, predicate, object, graph_name, @options)
99
+ puts format_quad(subject, predicate, object, graph_name, **@options)
100
100
  end
101
101
 
102
102
  ##
@@ -107,7 +107,7 @@ module RDF
107
107
  # @return [String]
108
108
  # @since 0.4.0
109
109
  def format_statement(statement, **options)
110
- format_quad(*statement.to_quad, options)
110
+ format_quad(*statement.to_quad, **options)
111
111
  end
112
112
 
113
113
  ##
@@ -120,8 +120,8 @@ module RDF
120
120
  # @param [Hash{Symbol => Object}] options = ({})
121
121
  # @return [String]
122
122
  def format_quad(subject, predicate, object, graph_name, **options)
123
- s = "%s %s %s " % [subject, predicate, object].map { |value| format_term(value, options) }
124
- s += format_term(graph_name, options) + " " if graph_name
123
+ s = "%s %s %s " % [subject, predicate, object].map { |value| format_term(value, **options) }
124
+ s += format_term(graph_name, **options) + " " if graph_name
125
125
  s + "."
126
126
  end
127
127
  end # Writer
@@ -15,15 +15,17 @@ module RDF
15
15
  #
16
16
  # <https://rubygems.org/gems/rdf> <http://purl.org/dc/terms/title> "rdf" .
17
17
  #
18
- # Installation
19
- # ------------
18
+ # ## RDFStar (RDF*)
19
+ #
20
+ # Supports statements as resources using `<<s p o>>`.
21
+ #
22
+ # ## Installation
20
23
  #
21
24
  # This is the only RDF serialization format that is directly supported by
22
25
  # RDF.rb. Support for other formats is available in the form of add-on
23
26
  # gems, e.g. 'rdf-xml' or 'rdf-json'.
24
27
  #
25
- # Documentation
26
- # -------------
28
+ # ## Documentation
27
29
  #
28
30
  # * {RDF::NTriples::Format}
29
31
  # * {RDF::NTriples::Reader}
@@ -1,4 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
3
+ require 'strscan'
4
+
2
5
  module RDF::NTriples
3
6
  ##
4
7
  # N-Triples parser.
@@ -25,6 +28,10 @@ module RDF::NTriples
25
28
  # end
26
29
  # end
27
30
  #
31
+ # ** RDFStar (RDF*)
32
+ #
33
+ # Supports statements as resources using `<<s p o>>`.
34
+ #
28
35
  # @see http://www.w3.org/TR/rdf-testcases/#ntriples
29
36
  # @see http://www.w3.org/TR/n-triples/
30
37
  class Reader < RDF::Reader
@@ -70,6 +77,10 @@ module RDF::NTriples
70
77
  # 22
71
78
  STRING_LITERAL_QUOTE = /"((?:[^\"\\\n\r]|#{ECHAR}|#{UCHAR})*)"/.freeze
72
79
 
80
+ # RDF*
81
+ ST_START = /^<</.freeze
82
+ ST_END = /^\s*>>/.freeze
83
+
73
84
  # @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar
74
85
  COMMENT = /^#\s*(.*)$/.freeze
75
86
  NODEID = /^#{BLANK_NODE_LABEL}/.freeze
@@ -96,7 +107,7 @@ module RDF::NTriples
96
107
  def self.unserialize(input, **options)
97
108
  case input
98
109
  when nil then nil
99
- else self.new(input, {logger: []}.merge(options)).read_value
110
+ else self.new(input, logger: [], **options).read_value
100
111
  end
101
112
  end
102
113
 
@@ -104,20 +115,20 @@ module RDF::NTriples
104
115
  # (see unserialize)
105
116
  # @return [RDF::Resource]
106
117
  def self.parse_subject(input, **options)
107
- parse_uri(input, options) || parse_node(input, options)
118
+ parse_uri(input, **options) || parse_node(input, **options)
108
119
  end
109
120
 
110
121
  ##
111
122
  # (see unserialize)
112
123
  # @return [RDF::URI]
113
- def self.parse_predicate(input, *options)
124
+ def self.parse_predicate(input, **options)
114
125
  parse_uri(input, intern: true)
115
126
  end
116
127
 
117
128
  ##
118
129
  # (see unserialize)
119
130
  def self.parse_object(input, **options)
120
- parse_uri(input, options) || parse_node(input, options) || parse_literal(input, options)
131
+ parse_uri(input, **options) || parse_node(input, **options) || parse_literal(input, **options)
121
132
  end
122
133
 
123
134
  ##
@@ -202,7 +213,7 @@ module RDF::NTriples
202
213
  begin
203
214
  read_statement
204
215
  rescue RDF::ReaderError
205
- value = read_uriref || read_node || read_literal
216
+ value = read_uriref || read_node || read_literal || read_rdfstar
206
217
  log_recover
207
218
  value
208
219
  end
@@ -218,9 +229,9 @@ module RDF::NTriples
218
229
 
219
230
  begin
220
231
  unless blank? || read_comment
221
- subject = read_uriref || read_node || fail_subject
232
+ subject = read_uriref || read_node || read_rdfstar || fail_subject
222
233
  predicate = read_uriref(intern: true) || fail_predicate
223
- object = read_uriref || read_node || read_literal || fail_object
234
+ object = read_uriref || read_node || read_literal || read_rdfstar || fail_object
224
235
 
225
236
  if validate? && !read_eos
226
237
  log_error("Expected end of statement (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
@@ -234,6 +245,20 @@ module RDF::NTriples
234
245
  end
235
246
  end
236
247
 
248
+ ##
249
+ # @return [RDF::Statement]
250
+ def read_rdfstar
251
+ if @options[:rdfstar] && match(ST_START)
252
+ subject = read_uriref || read_node || read_rdfstar || fail_subject
253
+ predicate = read_uriref(intern: true) || fail_predicate
254
+ object = read_uriref || read_node || read_literal || read_rdfstar || fail_object
255
+ if !match(ST_END)
256
+ log_error("Expected end of statement (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
257
+ end
258
+ RDF::Statement.new(subject, predicate, object)
259
+ end
260
+ end
261
+
237
262
  ##
238
263
  # @return [Boolean]
239
264
  # @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (comment)
@@ -208,7 +208,7 @@ module RDF::NTriples
208
208
  # @param [RDF::Term] object
209
209
  # @return [void]
210
210
  def write_triple(subject, predicate, object)
211
- puts format_triple(subject, predicate, object, @options)
211
+ puts format_triple(subject, predicate, object, **@options)
212
212
  end
213
213
 
214
214
  ##
@@ -221,6 +221,15 @@ module RDF::NTriples
221
221
  format_triple(*statement.to_triple, **options)
222
222
  end
223
223
 
224
+ ##
225
+ # Returns the N-Triples representation of an RDF* reified statement.
226
+ #
227
+ # @param [RDF::Statement] statement
228
+ # @param [Hash{Symbol => Object}] options ({})
229
+ # @return [String]
230
+ def format_rdfstar(statement, **options)
231
+ "<<%s %s %s>>" % statement.to_a.map { |value| format_term(value, **options) }
232
+ end
224
233
  ##
225
234
  # Returns the N-Triples representation of a triple.
226
235
  #
@@ -90,7 +90,7 @@ module RDF
90
90
  # the resulting solution sequence
91
91
  # @see RDF::Query#execute
92
92
  def self.execute(queryable, patterns = {}, options = {}, &block)
93
- self.new(patterns, options, &block).execute(queryable, options)
93
+ self.new(patterns, **options, &block).execute(queryable, **options)
94
94
  end
95
95
 
96
96
  ##
@@ -134,6 +134,12 @@ module RDF
134
134
  # @return [Hash]
135
135
  attr_reader :options
136
136
 
137
+ ##
138
+ # Scope the query to named graphs matching value
139
+ #
140
+ # @return [RDF::Resource, RDF::Query::Variable, false] graph_name
141
+ attr_accessor :graph_name
142
+
137
143
  ##
138
144
  # Initializes a new basic graph pattern query.
139
145
  #
@@ -180,6 +186,7 @@ module RDF
180
186
  @options = options.dup
181
187
  @solutions = Query::Solutions(solutions)
182
188
  graph_name = name if graph_name.nil?
189
+ @graph_name = graph_name
183
190
 
184
191
  patterns << @options if patterns.empty?
185
192
 
@@ -189,8 +196,6 @@ module RDF
189
196
  else patterns
190
197
  end
191
198
 
192
- self.graph_name = graph_name
193
-
194
199
  if block_given?
195
200
  case block.arity
196
201
  when 1 then block.call(self)
@@ -223,7 +228,7 @@ module RDF
223
228
  # whether this is an optional pattern
224
229
  # @return [void] self
225
230
  def pattern(pattern, **options)
226
- @patterns << Pattern.from(pattern, options)
231
+ @patterns << Pattern.from(pattern, **options)
227
232
  self
228
233
  end
229
234
 
@@ -235,7 +240,7 @@ module RDF
235
240
  # @return [RDF::Query] a copy of `self`
236
241
  # @since 0.3.0
237
242
  def optimize(**options)
238
- self.dup.optimize!(options)
243
+ self.dup.optimize!(**options)
239
244
  end
240
245
 
241
246
  ##
@@ -284,6 +289,8 @@ module RDF
284
289
  # any additional keyword options
285
290
  # @option options [Hash{Symbol => RDF::Term}] bindings
286
291
  # optional variable bindings to use
292
+ # @option options [Boolean] :optimize
293
+ # Optimize query before execution.
287
294
  # @option options [RDF::Query::Solutions] solutions
288
295
  # optional initial solutions for chained queries
289
296
  # @yield [solution]
@@ -294,9 +301,7 @@ module RDF
294
301
  # the resulting solution sequence
295
302
  # @see http://www.holygoat.co.uk/blog/entry/2005-10-25-1
296
303
  # @see http://www.w3.org/TR/sparql11-query/#emptyGroupPattern
297
- def execute(queryable, solutions: Solution.new, graph_name: nil, name: nil, **options, &block)
298
- options = {bindings: {}}.merge(options)
299
-
304
+ def execute(queryable, bindings: {}, solutions: Solution.new, graph_name: nil, name: nil, **options, &block)
300
305
  # Use provided solutions to allow for query chaining
301
306
  # Otherwise, a quick empty solution simplifies the logic below; no special case for
302
307
  # the first pattern
@@ -308,17 +313,17 @@ module RDF
308
313
  return @solutions
309
314
  end
310
315
 
316
+ self.optimize! if options[:optimize]
311
317
  patterns = @patterns
312
318
  graph_name = name if graph_name.nil?
313
- graph_name = self.graph_name if graph_name.nil?
314
- options[:graph_name] = graph_name unless graph_name.nil?
319
+ @graph_name = graph_name unless graph_name.nil?
315
320
 
316
321
  # Add graph_name to pattern, if necessary
317
- unless graph_name.nil?
322
+ unless @graph_name.nil?
318
323
  if patterns.empty?
319
- patterns = [Pattern.new(nil, nil, nil, graph_name: graph_name)]
324
+ patterns = [Pattern.new(nil, nil, nil, graph_name: @graph_name)]
320
325
  else
321
- apply_graph_name(graph_name)
326
+ apply_graph_name(@graph_name)
322
327
  end
323
328
  end
324
329
 
@@ -326,15 +331,15 @@ module RDF
326
331
 
327
332
  old_solutions, @solutions = @solutions, Query::Solutions()
328
333
 
329
- options[:bindings].each_key do |variable|
334
+ bindings.each_key do |variable|
330
335
  if pattern.variables.include?(variable)
331
336
  unbound_solutions, old_solutions = old_solutions, Query::Solutions()
332
- options[:bindings][variable].each do |binding|
337
+ bindings[variable].each do |binding|
333
338
  unbound_solutions.each do |solution|
334
339
  old_solutions << solution.merge(variable => binding)
335
340
  end
336
341
  end
337
- options[:bindings].delete(variable)
342
+ bindings.delete(variable)
338
343
  end
339
344
  end
340
345
 
@@ -407,38 +412,26 @@ module RDF
407
412
  # Is this query scoped to a named graph?
408
413
  # @return [Boolean]
409
414
  def named?
410
- !!options[:graph_name]
415
+ !!graph_name
411
416
  end
412
417
 
413
418
  # Is this query scoped to the default graph?
414
419
  # @return [Boolean]
415
420
  def default?
416
- options[:graph_name] == false
421
+ graph_name == false
417
422
  end
418
423
 
419
424
  # Is this query unscoped? This indicates that it can return results from
420
425
  # either a named graph or the default graph.
421
426
  # @return [Boolean]
422
427
  def unnamed?
423
- options[:graph_name].nil?
424
- end
425
-
426
- # Scope the query to named graphs matching value
427
- # @param [RDF::IRI, RDF::Query::Variable] value
428
- # @return [RDF::IRI, RDF::Query::Variable]
429
- def graph_name=(value)
430
- options[:graph_name] = value
431
- end
432
-
433
- # Scope of this query, if any
434
- # @return [RDF::IRI, RDF::Query::Variable]
435
- def graph_name
436
- options[:graph_name]
428
+ graph_name.nil?
437
429
  end
438
430
 
439
431
  # Apply the graph name specified (or configured) to all patterns that have no graph name
440
432
  # @param [RDF::IRI, RDF::Query::Variable] graph_name (self.graph_name)
441
- def apply_graph_name(graph_name = options[:graph_name])
433
+ def apply_graph_name(graph_name = nil)
434
+ graph_name ||= self.graph_name
442
435
  patterns.each {|pattern| pattern.graph_name = graph_name if pattern.graph_name.nil?} unless graph_name.nil?
443
436
  end
444
437
 
@@ -515,8 +508,7 @@ module RDF
515
508
  # @return [RDF::Query]
516
509
  def dup
517
510
  patterns = @patterns.map {|p| p.dup}
518
- patterns << @options.merge(solutions: @solutions.dup)
519
- Query.new(*patterns)
511
+ Query.new(patterns, graph_name: graph_name, solutions: @solutions.dup, **options)
520
512
  end
521
513
 
522
514
  ##