sparql 3.0.1 → 3.1.4

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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +188 -73
  3. data/UNLICENSE +1 -1
  4. data/VERSION +1 -1
  5. data/bin/sparql +44 -24
  6. data/lib/rack/sparql.rb +1 -1
  7. data/lib/rack/sparql/conneg.rb +3 -3
  8. data/lib/sinatra/sparql.rb +5 -5
  9. data/lib/sparql.rb +17 -16
  10. data/lib/sparql/algebra.rb +15 -23
  11. data/lib/sparql/algebra/aggregate.rb +4 -4
  12. data/lib/sparql/algebra/evaluatable.rb +2 -2
  13. data/lib/sparql/algebra/expression.rb +63 -40
  14. data/lib/sparql/algebra/extensions.rb +180 -33
  15. data/lib/sparql/algebra/operator.rb +37 -16
  16. data/lib/sparql/algebra/operator/abs.rb +2 -2
  17. data/lib/sparql/algebra/operator/add.rb +3 -3
  18. data/lib/sparql/algebra/operator/alt.rb +4 -4
  19. data/lib/sparql/algebra/operator/and.rb +7 -7
  20. data/lib/sparql/algebra/operator/asc.rb +3 -3
  21. data/lib/sparql/algebra/operator/ask.rb +4 -14
  22. data/lib/sparql/algebra/operator/avg.rb +6 -4
  23. data/lib/sparql/algebra/operator/base.rb +10 -10
  24. data/lib/sparql/algebra/operator/bgp.rb +1 -1
  25. data/lib/sparql/algebra/operator/bnode.rb +5 -5
  26. data/lib/sparql/algebra/operator/bound.rb +3 -3
  27. data/lib/sparql/algebra/operator/ceil.rb +2 -2
  28. data/lib/sparql/algebra/operator/clear.rb +3 -3
  29. data/lib/sparql/algebra/operator/coalesce.rb +3 -13
  30. data/lib/sparql/algebra/operator/compare.rb +8 -8
  31. data/lib/sparql/algebra/operator/concat.rb +4 -4
  32. data/lib/sparql/algebra/operator/construct.rb +4 -14
  33. data/lib/sparql/algebra/operator/contains.rb +2 -2
  34. data/lib/sparql/algebra/operator/copy.rb +3 -3
  35. data/lib/sparql/algebra/operator/count.rb +3 -3
  36. data/lib/sparql/algebra/operator/create.rb +3 -3
  37. data/lib/sparql/algebra/operator/dataset.rb +6 -17
  38. data/lib/sparql/algebra/operator/datatype.rb +1 -1
  39. data/lib/sparql/algebra/operator/day.rb +1 -1
  40. data/lib/sparql/algebra/operator/delete.rb +9 -7
  41. data/lib/sparql/algebra/operator/delete_data.rb +3 -3
  42. data/lib/sparql/algebra/operator/delete_where.rb +6 -6
  43. data/lib/sparql/algebra/operator/desc.rb +1 -1
  44. data/lib/sparql/algebra/operator/describe.rb +3 -13
  45. data/lib/sparql/algebra/operator/distinct.rb +4 -14
  46. data/lib/sparql/algebra/operator/divide.rb +1 -1
  47. data/lib/sparql/algebra/operator/drop.rb +3 -3
  48. data/lib/sparql/algebra/operator/encode_for_uri.rb +3 -3
  49. data/lib/sparql/algebra/operator/equal.rb +2 -2
  50. data/lib/sparql/algebra/operator/exists.rb +5 -5
  51. data/lib/sparql/algebra/operator/exprlist.rb +3 -13
  52. data/lib/sparql/algebra/operator/extend.rb +19 -18
  53. data/lib/sparql/algebra/operator/filter.rb +6 -16
  54. data/lib/sparql/algebra/operator/floor.rb +2 -2
  55. data/lib/sparql/algebra/operator/graph.rb +6 -17
  56. data/lib/sparql/algebra/operator/greater_than.rb +5 -5
  57. data/lib/sparql/algebra/operator/greater_than_or_equal.rb +5 -5
  58. data/lib/sparql/algebra/operator/group.rb +25 -25
  59. data/lib/sparql/algebra/operator/group_concat.rb +6 -6
  60. data/lib/sparql/algebra/operator/hours.rb +1 -1
  61. data/lib/sparql/algebra/operator/if.rb +5 -15
  62. data/lib/sparql/algebra/operator/in.rb +4 -14
  63. data/lib/sparql/algebra/operator/insert.rb +7 -5
  64. data/lib/sparql/algebra/operator/insert_data.rb +3 -3
  65. data/lib/sparql/algebra/operator/iri.rb +1 -1
  66. data/lib/sparql/algebra/operator/is_blank.rb +1 -1
  67. data/lib/sparql/algebra/operator/is_iri.rb +1 -1
  68. data/lib/sparql/algebra/operator/is_literal.rb +1 -1
  69. data/lib/sparql/algebra/operator/is_numeric.rb +1 -1
  70. data/lib/sparql/algebra/operator/join.rb +12 -10
  71. data/lib/sparql/algebra/operator/lang.rb +1 -1
  72. data/lib/sparql/algebra/operator/lang_matches.rb +3 -3
  73. data/lib/sparql/algebra/operator/lcase.rb +2 -2
  74. data/lib/sparql/algebra/operator/left_join.rb +15 -12
  75. data/lib/sparql/algebra/operator/less_than.rb +5 -5
  76. data/lib/sparql/algebra/operator/less_than_or_equal.rb +5 -5
  77. data/lib/sparql/algebra/operator/load.rb +3 -3
  78. data/lib/sparql/algebra/operator/max.rb +6 -4
  79. data/lib/sparql/algebra/operator/md5.rb +1 -1
  80. data/lib/sparql/algebra/operator/min.rb +6 -4
  81. data/lib/sparql/algebra/operator/minus.rb +12 -11
  82. data/lib/sparql/algebra/operator/minutes.rb +1 -1
  83. data/lib/sparql/algebra/operator/modify.rb +4 -4
  84. data/lib/sparql/algebra/operator/month.rb +1 -1
  85. data/lib/sparql/algebra/operator/move.rb +3 -3
  86. data/lib/sparql/algebra/operator/multiply.rb +1 -1
  87. data/lib/sparql/algebra/operator/negate.rb +1 -1
  88. data/lib/sparql/algebra/operator/not.rb +1 -1
  89. data/lib/sparql/algebra/operator/not_equal.rb +2 -2
  90. data/lib/sparql/algebra/operator/notexists.rb +4 -4
  91. data/lib/sparql/algebra/operator/notin.rb +4 -14
  92. data/lib/sparql/algebra/operator/notoneof.rb +5 -6
  93. data/lib/sparql/algebra/operator/now.rb +1 -1
  94. data/lib/sparql/algebra/operator/or.rb +7 -7
  95. data/lib/sparql/algebra/operator/order.rb +6 -16
  96. data/lib/sparql/algebra/operator/path.rb +6 -5
  97. data/lib/sparql/algebra/operator/path_opt.rb +16 -16
  98. data/lib/sparql/algebra/operator/path_plus.rb +8 -8
  99. data/lib/sparql/algebra/operator/path_star.rb +4 -4
  100. data/lib/sparql/algebra/operator/plus.rb +3 -3
  101. data/lib/sparql/algebra/operator/prefix.rb +10 -10
  102. data/lib/sparql/algebra/operator/project.rb +4 -14
  103. data/lib/sparql/algebra/operator/rand.rb +1 -1
  104. data/lib/sparql/algebra/operator/reduced.rb +4 -14
  105. data/lib/sparql/algebra/operator/regex.rb +6 -6
  106. data/lib/sparql/algebra/operator/replace.rb +4 -4
  107. data/lib/sparql/algebra/operator/reverse.rb +4 -4
  108. data/lib/sparql/algebra/operator/round.rb +2 -2
  109. data/lib/sparql/algebra/operator/same_term.rb +8 -5
  110. data/lib/sparql/algebra/operator/sample.rb +3 -3
  111. data/lib/sparql/algebra/operator/seconds.rb +1 -1
  112. data/lib/sparql/algebra/operator/seq.rb +5 -6
  113. data/lib/sparql/algebra/operator/sequence.rb +4 -4
  114. data/lib/sparql/algebra/operator/sha1.rb +1 -1
  115. data/lib/sparql/algebra/operator/sha256.rb +1 -1
  116. data/lib/sparql/algebra/operator/sha384.rb +1 -1
  117. data/lib/sparql/algebra/operator/sha512.rb +1 -1
  118. data/lib/sparql/algebra/operator/slice.rb +4 -14
  119. data/lib/sparql/algebra/operator/str.rb +1 -1
  120. data/lib/sparql/algebra/operator/strafter.rb +2 -2
  121. data/lib/sparql/algebra/operator/strbefore.rb +2 -2
  122. data/lib/sparql/algebra/operator/strdt.rb +2 -2
  123. data/lib/sparql/algebra/operator/strends.rb +2 -2
  124. data/lib/sparql/algebra/operator/strlang.rb +2 -2
  125. data/lib/sparql/algebra/operator/strlen.rb +2 -2
  126. data/lib/sparql/algebra/operator/strstarts.rb +2 -2
  127. data/lib/sparql/algebra/operator/struuid.rb +1 -1
  128. data/lib/sparql/algebra/operator/substr.rb +4 -4
  129. data/lib/sparql/algebra/operator/subtract.rb +1 -1
  130. data/lib/sparql/algebra/operator/sum.rb +6 -4
  131. data/lib/sparql/algebra/operator/table.rb +3 -3
  132. data/lib/sparql/algebra/operator/timezone.rb +1 -1
  133. data/lib/sparql/algebra/operator/tz.rb +1 -1
  134. data/lib/sparql/algebra/operator/ucase.rb +2 -2
  135. data/lib/sparql/algebra/operator/union.rb +9 -8
  136. data/lib/sparql/algebra/operator/update.rb +4 -4
  137. data/lib/sparql/algebra/operator/using.rb +4 -4
  138. data/lib/sparql/algebra/operator/uuid.rb +1 -1
  139. data/lib/sparql/algebra/operator/with.rb +7 -7
  140. data/lib/sparql/algebra/operator/year.rb +1 -1
  141. data/lib/sparql/algebra/query.rb +2 -2
  142. data/lib/sparql/algebra/update.rb +2 -2
  143. data/lib/sparql/algebra/version.rb +1 -1
  144. data/lib/sparql/extensions.rb +5 -5
  145. data/lib/sparql/grammar.rb +111 -11
  146. data/lib/sparql/grammar/meta.rb +2340 -907
  147. data/lib/sparql/grammar/parser11.rb +69 -69
  148. data/lib/sparql/grammar/terminals11.rb +2 -0
  149. data/lib/sparql/results.rb +73 -46
  150. data/lib/sparql/version.rb +1 -1
  151. metadata +38 -65
@@ -18,7 +18,7 @@ module Rack
18
18
  # @param [Hash{Symbol => Object}] options
19
19
  # @option options [Boolean] :overwrite (false)
20
20
  # @return [void]
21
- def self.register_mime_types!(options = {})
21
+ def self.register_mime_types!(**options)
22
22
  if defined?(Rack::Mime::MIME_TYPES)
23
23
  RDF::Format.each do |format|
24
24
  if !Rack::Mime::MIME_TYPES.has_key?(file_ext = ".#{format.to_sym}") || options[:overwrite]
@@ -43,7 +43,7 @@ module Rack; module SPARQL
43
43
  #
44
44
  # @param [Hash{String => String}] env
45
45
  # @return [Array(Integer, Hash, #each)]
46
- # @see http://www.rubydoc.info/github/rack/rack/Rack/Runtime#call-instance_method
46
+ # @see https://www.rubydoc.info/github/rack/rack/Rack/Runtime#call-instance_method
47
47
  def call(env)
48
48
  env['ORDERED_CONTENT_TYPES'] = parse_accept_header(env['HTTP_ACCEPT']) if env.has_key?('HTTP_ACCEPT')
49
49
  response = app.call(env)
@@ -71,7 +71,7 @@ module Rack; module SPARQL
71
71
  serialize_options = {}
72
72
  serialize_options[:content_types] = env['ORDERED_CONTENT_TYPES'] if env['ORDERED_CONTENT_TYPES']
73
73
  serialize_options.merge!(@options)
74
- results = ::SPARQL.serialize_results(body, serialize_options)
74
+ results = ::SPARQL.serialize_results(body, **serialize_options)
75
75
  raise RDF::WriterError, "can't serialize results" unless results
76
76
  headers = headers.merge(VARY).merge('Content-Type' => results.content_type) # FIXME: don't overwrite existing Vary headers
77
77
  [status, headers, [results]]
@@ -89,7 +89,7 @@ module Rack; module SPARQL
89
89
  #
90
90
  # @param [String, #to_s] header
91
91
  # @return [Array<String>]
92
- # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
92
+ # @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
93
93
  def parse_accept_header(header)
94
94
  entries = header.to_s.split(',')
95
95
  entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
@@ -8,7 +8,7 @@ module Sinatra
8
8
  #
9
9
  # To override negotiation on Content-Type, set :format in `sparql_options` to a RDF Format class, or symbol identifying a format.
10
10
  #
11
- # @see http://www.sinatrarb.com/extensions.html
11
+ # @see https://www.sinatrarb.com/extensions.html
12
12
  module SPARQL
13
13
  ##
14
14
  # Helper methods.
@@ -25,9 +25,9 @@ module Sinatra
25
25
  # URI of the service endpoint, defaults to "/sparql" in the current realm.
26
26
  # @return [RDF::Graph]
27
27
  #
28
- # @see http://www.w3.org/TR/sparql11-service-description
29
- # @see http://www.w3.org/TR/void/
30
- def service_description(options = {})
28
+ # @see https://www.w3.org/TR/sparql11-service-description
29
+ # @see https://www.w3.org/TR/void/
30
+ def service_description(**options)
31
31
  repository = options[:repository]
32
32
 
33
33
  g = RDF::Graph.new
@@ -103,7 +103,7 @@ module Sinatra
103
103
  app.helpers(Sinatra::SPARQL::Helpers)
104
104
  app.send(:include, ::SPARQL)
105
105
  app.send(:include, ::RDF)
106
- app.send(:include, ::LinkedData)
106
+ app.send(:include, ::LinkedData) if defined?(::LinkedData)
107
107
  end
108
108
  end
109
109
  end
@@ -1,16 +1,18 @@
1
+ require 'sxp'
1
2
  require 'sparql/extensions'
3
+ require 'sparql/algebra/sxp_extensions'
2
4
 
3
5
  ##
4
6
  # A SPARQL for RDF.rb.
5
7
  #
6
- # @see http://www.w3.org/TR/sparql11-query
8
+ # @see https://www.w3.org/TR/sparql11-query
7
9
  module SPARQL
8
10
  autoload :Algebra, 'sparql/algebra'
9
11
  autoload :Grammar, 'sparql/grammar'
10
12
  autoload :Results, 'sparql/results'
11
13
  autoload :VERSION, 'sparql/version'
12
14
 
13
- # @see http://rubygems.org/gems/sparql-client
15
+ # @see https://rubygems-client
14
16
  autoload :Client, 'sparql/client'
15
17
 
16
18
  ##
@@ -28,8 +30,8 @@ module SPARQL
28
30
  # a `queryable` object such as an RDF::Graph
29
31
  # or RDF::Repository.
30
32
  # @raise [Parser::Error] on invalid input
31
- def self.parse(query, options = {})
32
- query = Grammar::Parser.new(query, options).parse(options[:update] ? :UpdateUnit : :QueryUnit)
33
+ def self.parse(query, **options)
34
+ query = Grammar::Parser.new(query, **options).parse(options[:update] ? :UpdateUnit : :QueryUnit)
33
35
  end
34
36
 
35
37
  ##
@@ -52,12 +54,13 @@ module SPARQL
52
54
  # results = SPARQL.execute("SELECT * WHERE { ?s ?p ?o }", repository)
53
55
  #
54
56
  # @param [IO, StringIO, String, #to_s] query
57
+ # @param [RDF::Queryable] queryable
55
58
  # @param [Hash{Symbol => Object}] options
56
- # @option options [RDF::Queryable] :queryable
57
- # @option options [RDF::URI, String, Array<RDF::URI, String>] :load_datasets
58
- # One or more URIs used to initialize a new instance of `queryable` in the default graph.
59
+ # @option options [Boolean] :optimize
60
+ # Optimize query before execution.
59
61
  # @option options [RDF::URI, String, Array<RDF::URI, String>] :default_graph_uri
60
- # One or more URIs used to initialize a new instance of `queryable` in the default graph.
62
+ # @option options [RDF::URI, String, Array<RDF::URI, String>] :load_datasets
63
+ # One or more URIs used to initialize a new instance of `queryable` in the default graph. One or more URIs used to initialize a new instance of `queryable` in the default graph.
61
64
  # @option options [RDF::URI, String, Array<RDF::URI, String>] :named_graph_uri
62
65
  # One or more URIs used to initialize the `queryable` as a named graph.
63
66
  # @yield [solution]
@@ -67,15 +70,13 @@ module SPARQL
67
70
  # @return [RDF::Graph, Boolean, RDF::Query::Solutions::Enumerator]
68
71
  # Note, results may be used with {SPARQL.serialize_results} to obtain appropriate output encoding.
69
72
  # @raise [SPARQL::MalformedQuery] on invalid input
70
- def self.execute(query, queryable, options = {}, &block)
71
- query = self.parse(query, options)
73
+ def self.execute(query, queryable, **options, &block)
74
+ query = self.parse(query, **options)
75
+ query = query.optimize(**options) if options[:optimize]
72
76
  queryable = queryable || RDF::Repository.new
73
-
74
- case options.fetch(:debug, nil)
75
- when TrueClass
76
- puts query.to_sxp
77
- when Array
78
- options[:debug] << query.to_sxp
77
+
78
+ if options[:logger]
79
+ options[:logger].debug("SPARQL.execute") {SXP::Generator.string query.to_sxp_bin}
79
80
  end
80
81
 
81
82
  if options.has_key?(:load_datasets)
@@ -1,4 +1,4 @@
1
- require 'rdf' # @see http://rubygems.org/gems/rdf
1
+ require 'rdf' # @see https://rubygems.org/gems/rdf
2
2
  require 'rdf/xsd'
3
3
 
4
4
  module SPARQL
@@ -141,21 +141,21 @@ module SPARQL
141
141
  # ## Constructing operator expressions manually
142
142
  #
143
143
  # Operator(:isBlank).new(RDF::Node(:foobar)).to_sxp #=> "(isBlank _:foobar)"
144
- # Operator(:isIRI).new(RDF::URI('http://rubygems.org/gems/rdf/')).to_sxp #=> "(isIRI <http://rubygems.org/gems/rdf/>)"
145
- # Operator(:isLiteral).new(RDF::Literal(3.1415)).to_sxp #=> "(isLiteral 3.1415)"
146
- # Operator(:str).new(Operator(:datatype).new(RDF::Literal(3.1415))).to_sxp #=> "(str (datatype 3.1415))"
144
+ # Operator(:isIRI).new(RDF::URI('https://rubygems.org/gems/rdf/')).to_sxp #=> "(isIRI <https://rubygems.org/gems/rdf/>)"
145
+ # Operator(:isLiteral).new(RDF::Literal(3.1415)).to_sxp #=> "(isLiteral 3.1415e0)"
146
+ # Operator(:str).new(Operator(:datatype).new(RDF::Literal(3.1415))).to_sxp #=> "(str (datatype 3.1415e0))"
147
147
  #
148
148
  # ## Constructing operator expressions using SSE forms
149
149
  #
150
150
  # SPARQL::Algebra::Expression[:isBlank, RDF::Node(:foobar)].to_sxp #=> "(isBlank _:foobar)"
151
- # SPARQL::Algebra::Expression[:isIRI, RDF::URI('http://rubygems.org/gems/rdf/')].to_sxp #=> "(isIRI <http://rubygems.org/gems/rdf/>)"
152
- # SPARQL::Algebra::Expression[:isLiteral, RDF::Literal(3.1415)].to_sxp #=> "(isLiteral 3.1415)"
153
- # SPARQL::Algebra::Expression[:str, [:datatype, RDF::Literal(3.1415)]].to_sxp #=> "(str (datatype 3.1415))"
151
+ # SPARQL::Algebra::Expression[:isIRI, RDF::URI('https://rubygems.org/gems/rdf/')].to_sxp #=> "(isIRI <https://rubygems.org/gems/rdf/>)"
152
+ # SPARQL::Algebra::Expression[:isLiteral, RDF::Literal(3.1415)].to_sxp #=> "(isLiteral 3.1415e0)"
153
+ # SPARQL::Algebra::Expression[:str, [:datatype, RDF::Literal(3.1415)]].to_sxp #=> "(str (datatype 3.1415e0))"
154
154
  #
155
155
  # ## Constructing operator expressions using SSE strings
156
156
  #
157
157
  # SPARQL::Algebra::Expression.parse('(isBlank _:foobar)')
158
- # SPARQL::Algebra::Expression.parse('(isIRI <http://rubygems.org/gems/rdf/>)')
158
+ # SPARQL::Algebra::Expression.parse('(isIRI <https://rubygems.org/gems/rdf/>)')
159
159
  # SPARQL::Algebra::Expression.parse('(isLiteral 3.1415)')
160
160
  # SPARQL::Algebra::Expression.parse('(str (datatype 3.1415))')
161
161
  #
@@ -165,11 +165,6 @@ module SPARQL
165
165
  # Operator(:isIRI).evaluate(RDF::Vocab::DC.title) #=> RDF::Literal::TRUE
166
166
  # Operator(:isLiteral).evaluate(RDF::Literal(3.1415)) #=> RDF::Literal::TRUE
167
167
  #
168
- # ## Optimizing expressions containing constant subexpressions
169
- #
170
- # SPARQL::Algebra::Expression.parse('(sameTerm ?var ?var)').optimize #=> RDF::Literal::TRUE
171
- # SPARQL::Algebra::Expression.parse('(* -2 (- (* (+ 1 2) (+ 3 4))))').optimize #=> RDF::Literal(42)
172
- #
173
168
  # ## Evaluating expressions on a solution sequence
174
169
  #
175
170
  # # Find all people and their names & e-mail addresses:
@@ -372,8 +367,8 @@ module SPARQL
372
367
  # @param [Hash{Symbol => Object}] options
373
368
  # any additional options (see {Operator#initialize})
374
369
  # @return [SPARQL::Algebra::Operator]
375
- def parse(sse, options = {})
376
- Expression.parse(sse, options)
370
+ def parse(sse, **options)
371
+ Expression.parse(sse, **options)
377
372
  end
378
373
  module_function :parse
379
374
 
@@ -390,8 +385,8 @@ module SPARQL
390
385
  # @yieldparam [SPARQL::Algebra::Expression] expression
391
386
  # @yieldreturn [void] ignored
392
387
  # @return [Expression]
393
- def open(sse, options = {})
394
- Expression.open(sse, options)
388
+ def open(sse, **options)
389
+ Expression.open(sse, **options)
395
390
  end
396
391
  module_function :open
397
392
 
@@ -405,8 +400,7 @@ module SPARQL
405
400
  def Expression(*sse)
406
401
  Expression.for(*sse)
407
402
  end
408
- alias_method :Expr, :Expression
409
- module_function :Expr, :Expression
403
+ module_function :Expression
410
404
 
411
405
  ##
412
406
  # @example
@@ -417,8 +411,7 @@ module SPARQL
417
411
  def Operator(name, arity = nil)
418
412
  Operator.for(name, arity)
419
413
  end
420
- alias_method :Op, :Operator
421
- module_function :Op, :Operator
414
+ module_function :Operator
422
415
 
423
416
  ##
424
417
  # @example
@@ -430,8 +423,7 @@ module SPARQL
430
423
  def Variable(name)
431
424
  Variable.new(name)
432
425
  end
433
- alias_method :Var, :Variable
434
- module_function :Var, :Variable
426
+ module_function :Variable
435
427
 
436
428
  Variable = RDF::Query::Variable
437
429
  end # Algebra
@@ -6,8 +6,8 @@ module SPARQL; module Algebra
6
6
  # or more operands which are `Enumerable` lists of `RDF::Term`
7
7
  # and return a single `RDF::Term` or `TypeError`.
8
8
  #
9
- # @see http://www.w3.org/TR/sparql11-query/#setFunctions
10
- # @see http://www.w3.org/TR/sparql11-query/#aggregates
9
+ # @see https://www.w3.org/TR/sparql11-query/#setFunctions
10
+ # @see https://www.w3.org/TR/sparql11-query/#aggregates
11
11
  #
12
12
  # @abstract
13
13
  module Aggregate
@@ -24,12 +24,12 @@ module SPARQL; module Algebra
24
24
  # @return [RDF::Term]
25
25
  # @raise [TypeError]
26
26
  # @abstract
27
- def aggregate(solutions = [], options = {})
27
+ def aggregate(solutions = [], **options)
28
28
  operands.shift if distinct = (operands.first == :distinct)
29
29
  args_enum = solutions.map do |solution|
30
30
  operands.map do |operand|
31
31
  begin
32
- operand.evaluate(solution, options.merge(depth: options[:depth].to_i + 1))
32
+ operand.evaluate(solution, depth: options[:depth].to_i + 1, **options)
33
33
  rescue TypeError
34
34
  # Ignore errors
35
35
  nil
@@ -13,8 +13,8 @@ module SPARQL; module Algebra
13
13
  # options passed from query
14
14
  # @return [RDF::Term]
15
15
  # @abstract
16
- def evaluate(bindings, options = {})
17
- args = operands.map { |operand| operand.evaluate(bindings, options.merge(depth: options[:depth].to_i + 1)) }
16
+ def evaluate(bindings, **options)
17
+ args = operands.map { |operand| operand.evaluate(bindings, depth: options[:depth].to_i + 1, **options) }
18
18
  options[:memoize] ? memoize(*args) : apply(*args)
19
19
  end
20
20
 
@@ -19,14 +19,7 @@ module SPARQL; module Algebra
19
19
  # @yieldparam [SPARQL::Algebra::Expression] expression
20
20
  # @yieldreturn [void] ignored
21
21
  # @return [Expression]
22
- def self.parse(sse, options = {}, &block)
23
- begin
24
- require 'sxp' # @see http://rubygems.org/gems/sxp
25
- rescue LoadError
26
- abort "SPARQL::Algebra::Expression.parse requires the SXP gem (hint: `gem install sxp')."
27
- end
28
- require 'sparql/algebra/sxp_extensions'
29
-
22
+ def self.parse(sse, **options, &block)
30
23
  sse = sse.encode(Encoding::UTF_8)
31
24
  sxp = SXP::Reader::SPARQL.new(sse) do |reader|
32
25
  # Set base_uri if we have one
@@ -38,7 +31,7 @@ module SPARQL; module Algebra
38
31
  Operator.base_uri = options.delete(:base_uri) if options.has_key?(:base_uri)
39
32
  Operator.prefixes = sxp.prefixes || {}
40
33
 
41
- expression = self.new(sxp_result, options)
34
+ expression = self.new(sxp_result, **options)
42
35
 
43
36
  yield(expression) if block_given?
44
37
  expression
@@ -57,8 +50,8 @@ module SPARQL; module Algebra
57
50
  # @yieldparam [SPARQL::Algebra::Expression] expression
58
51
  # @yieldreturn [void] ignored
59
52
  # @return [Expression]
60
- def self.open(filename, options = {}, &block)
61
- RDF::Util::File.open_file(filename, options) do |file|
53
+ def self.open(filename, **options, &block)
54
+ RDF::Util::File.open_file(filename, **options) do |file|
62
55
  options[:base_uri] ||= filename
63
56
  Expression.parse(file, options, &block)
64
57
  end
@@ -87,7 +80,7 @@ module SPARQL; module Algebra
87
80
  # any additional options (see {Operator#initialize})
88
81
  # @return [Expression]
89
82
  # @raise [TypeError] if any of the operands is invalid
90
- def self.new(sse, options = {})
83
+ def self.new(sse, **options)
91
84
  raise ArgumentError, "invalid SPARQL::Algebra::Expression form: #{sse.inspect}" unless sse.is_a?(Array)
92
85
 
93
86
  operator = Operator.for(sse.first, sse.length - 1)
@@ -95,7 +88,7 @@ module SPARQL; module Algebra
95
88
  return case sse.first
96
89
  when Array
97
90
  debug(options) {"Map array elements #{sse}"}
98
- sse.map {|s| self.new(s, options.merge(depth: options[:depth].to_i + 1))}
91
+ sse.map {|s| self.new(s, depth: options[:depth].to_i + 1, **options)}
99
92
  else
100
93
  debug(options) {"No operator found for #{sse.first}"}
101
94
  sse.map do |s|
@@ -110,7 +103,7 @@ module SPARQL; module Algebra
110
103
  debug(options) {"Operator=#{operator.inspect}, Operand=#{operand.inspect}"}
111
104
  case operand
112
105
  when Array
113
- self.new(operand, options.merge(depth: options[:depth].to_i + 1))
106
+ self.new(operand, depth: options[:depth].to_i + 1, **options)
114
107
  when Operator, Variable, RDF::Term, RDF::Query, Symbol
115
108
  operand
116
109
  when TrueClass, FalseClass, Numeric, String, DateTime, Date, Time
@@ -120,7 +113,7 @@ module SPARQL; module Algebra
120
113
  end
121
114
 
122
115
  debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"}
123
- options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
116
+ options.delete_if {|k, v| [:debug, :logger, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
124
117
  operands << options unless options.empty?
125
118
  operator.new(*operands)
126
119
  end
@@ -175,8 +168,8 @@ module SPARQL; module Algebra
175
168
  # @param [RDF::URI] function
176
169
  # @param [Array<RDF::Term>] args splat of args to function
177
170
  # @return [RDF::Term]
178
- # @see http://www.w3.org/TR/sparql11-query/#extensionFunctions
179
- # @see http://www.w3.org/TR/sparql11-query/#FunctionMapping
171
+ # @see https://www.w3.org/TR/sparql11-query/#extensionFunctions
172
+ # @see https://www.w3.org/TR/sparql11-query/#FunctionMapping
180
173
  def self.extension(function, *args)
181
174
  if function.to_s.start_with?(RDF::XSD.to_s)
182
175
  self.cast(function, args.first)
@@ -197,7 +190,7 @@ module SPARQL; module Algebra
197
190
  # Value, which should be a typed literal, where the type must be that specified
198
191
  # @raise [TypeError] if datatype is not a URI or value cannot be cast to datatype
199
192
  # @return [RDF::Term]
200
- # @see http://www.w3.org/TR/sparql11-query/#FunctionMapping
193
+ # @see https://www.w3.org/TR/sparql11-query/#FunctionMapping
201
194
  def self.cast(datatype, value)
202
195
  case datatype
203
196
  when RDF::XSD.dateTime
@@ -229,21 +222,45 @@ module SPARQL; module Algebra
229
222
  when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
230
223
  raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
231
224
  else
232
- RDF::Literal.new(!value.to_s.empty?, datatype: datatype, validate: true)
225
+ RDF::Literal::Boolean.new(value.value, datatype: datatype, validate: true)
233
226
  end
234
227
  when RDF::XSD.decimal, RDF::XSD.integer
235
228
  case value
236
229
  when RDF::Literal::Boolean
237
230
  RDF::Literal.new(value.object ? 1 : 0, datatype: datatype)
238
- when RDF::Literal::Integer, RDF::Literal::Decimal
239
- RDF::Literal.new(value, datatype: datatype)
231
+ when RDF::Literal::Numeric
232
+ RDF::Literal.new(value.object, datatype: datatype)
240
233
  when RDF::Literal::DateTime, RDF::Literal::Date, RDF::Literal::Time, RDF::URI, RDF::Node
241
234
  raise TypeError, "Value #{value.inspect} cannot be cast as #{datatype}"
242
235
  else
243
236
  RDF::Literal.new(value.value, datatype: datatype, validate: true)
244
237
  end
245
238
  when RDF::XSD.string
246
- RDF::Literal.new(value, datatype: datatype)
239
+ # Cast to string rules based on https://www.w3.org/TR/xpath-functions/#casting-to-string
240
+ case value
241
+ when RDF::Literal::Integer
242
+ RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
243
+ when RDF::Literal::Decimal
244
+ if value == value.ceil
245
+ RDF::Literal.new(value.ceil, datatype: datatype)
246
+ else
247
+ RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
248
+ end
249
+ when RDF::Literal::Float, RDF::Literal::Double
250
+ if value.abs >= 0.000001 && value.abs < 1000000
251
+ # If SV has an absolute value that is greater than or equal to 0.000001 (one millionth) and less than 1000000 (one million), then the value is converted to an xs:decimal and the resulting xs:decimal is converted to an xs:string according to the rules above, as though using an implementation of xs:decimal that imposes no limits on the totalDigits or fractionDigits facets.
252
+ cast(datatype, RDF::Literal::Decimal.new(value.object))
253
+ elsif value.object.zero?
254
+ # If SV has the value positive or negative zero, TV is "0" or "-0" respectively.
255
+ RDF::Literal.new(value.to_s.start_with?('-') ? '-0' : '0', datatype: datatype)
256
+ else
257
+ # If SV is positive or negative infinity, TV is the string "INF" or "-INF" respectively.
258
+ # In other cases, the result consists of a mantissa, which has the lexical form of an xs:decimal, followed by the letter "E", followed by an exponent which has the lexical form of an xs:integer. Leading zeroes and "+" signs are prohibited in the exponent. For the mantissa, there must be a decimal point, and there must be exactly one digit before the decimal point, which must be non-zero. The "+" sign is prohibited. There must be at least one digit after the decimal point. Apart from this mandatory digit, trailing zero digits are prohibited.
259
+ RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
260
+ end
261
+ else
262
+ RDF::Literal.new(value.canonicalize.to_s, datatype: datatype)
263
+ end
247
264
  else
248
265
  raise TypeError, "Expected datatype (#{datatype}) to be a recognized XPath function"
249
266
  end
@@ -280,12 +297,26 @@ module SPARQL; module Algebra
280
297
  ##
281
298
  # Returns an optimized version of this expression.
282
299
  #
283
- # This is the default implementation, which simply returns `self`.
300
+ # This is the default implementation, which simply returns a copy of `self`.
284
301
  # Subclasses can override this method in order to implement something
285
302
  # more useful.
286
303
  #
287
- # @return [Expression] `self`
288
- def optimize
304
+ # @param [Hash{Symbol => Object}] options
305
+ # any additional options for optimization
306
+ # @return [Expression] a copy of `self`
307
+ # @see RDF::Query#optimize
308
+ def optimize(**options)
309
+ self.deep_dup.optimize!(**options)
310
+ end
311
+
312
+ ##
313
+ # Optimizes this query.
314
+ #
315
+ # @param [Hash{Symbol => Object}] options
316
+ # any additional options for optimization
317
+ # @return [self]
318
+ # @see RDF::Query#optimize!
319
+ def optimize!(**options)
289
320
  self
290
321
  end
291
322
 
@@ -313,7 +344,7 @@ module SPARQL; module Algebra
313
344
  # more useful.
314
345
  #
315
346
  # @return [Array] `self`
316
- # @see http://openjena.org/wiki/SSE
347
+ # @see https://openjena.org/wiki/SSE
317
348
  def to_sxp_bin
318
349
  self
319
350
  end
@@ -349,7 +380,7 @@ module SPARQL; module Algebra
349
380
  # @param [String] node processing node
350
381
  # @param [String] message
351
382
  # @param [Hash{Symbol => Object}] options
352
- # @option options [Boolean] :debug output debug messages to $stderr
383
+ # @option options [Logger] :logger for logging progress
353
384
  # @option options [Integer] :depth (@productions.length)
354
385
  # Processing depth for indenting message output.
355
386
  # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
@@ -357,7 +388,7 @@ module SPARQL; module Algebra
357
388
  # @overload: May be called with node and an option hash
358
389
  # @param [String] node processing node
359
390
  # @param [Hash{Symbol => Object}] options
360
- # @option options [Boolean] :debug output debug messages to $stderr
391
+ # @option options [Logger] :logger for logging progress
361
392
  # @option options [Integer] :depth (@productions.length)
362
393
  # Processing depth for indenting message output.
363
394
  # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
@@ -365,22 +396,14 @@ module SPARQL; module Algebra
365
396
  # @overload: May be called with only options, in which case the block is used to return the output message
366
397
  # @param [String] node processing node
367
398
  # @param [Hash{Symbol => Object}] options
368
- # @option options [Boolean] :debug output debug messages to $stderr
399
+ # @option options [Logger] :logger for logging progress
369
400
  # @option options [Integer] :depth (@productions.length)
370
401
  # Processing depth for indenting message output.
371
402
  # @yieldreturn [String] appended to message, to allow for lazy-evaulation of message
372
- def self.debug(*args)
403
+ def self.debug(*args, &block)
373
404
  options = args.last.is_a?(Hash) ? args.pop : {}
374
- return unless options[:debug]
375
- message = args.join(": ")
376
- message = message + yield if block_given?
377
- depth = options[:depth] || 0
378
- case options[:debug]
379
- when Array
380
- options[:debug] << "#{' ' * depth}#{message}"
381
- else
382
- $stderr.puts("#{' ' * depth}#{message}")
383
- end
405
+ return unless options[:logger]
406
+ options[:logger].debug(*args, **options, &block)
384
407
  end
385
408
 
386
409
  def debug(*args, &block)