openlogic-rdf 0.3.6

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 (80) hide show
  1. data/AUTHORS +3 -0
  2. data/CREDITS +9 -0
  3. data/README +361 -0
  4. data/UNLICENSE +24 -0
  5. data/VERSION +1 -0
  6. data/bin/rdf +18 -0
  7. data/etc/doap.nt +62 -0
  8. data/lib/df.rb +1 -0
  9. data/lib/rdf/cli.rb +200 -0
  10. data/lib/rdf/format.rb +383 -0
  11. data/lib/rdf/mixin/countable.rb +39 -0
  12. data/lib/rdf/mixin/durable.rb +31 -0
  13. data/lib/rdf/mixin/enumerable.rb +637 -0
  14. data/lib/rdf/mixin/indexable.rb +26 -0
  15. data/lib/rdf/mixin/inferable.rb +5 -0
  16. data/lib/rdf/mixin/mutable.rb +191 -0
  17. data/lib/rdf/mixin/queryable.rb +265 -0
  18. data/lib/rdf/mixin/readable.rb +15 -0
  19. data/lib/rdf/mixin/type_check.rb +21 -0
  20. data/lib/rdf/mixin/writable.rb +152 -0
  21. data/lib/rdf/model/graph.rb +263 -0
  22. data/lib/rdf/model/list.rb +731 -0
  23. data/lib/rdf/model/literal/boolean.rb +121 -0
  24. data/lib/rdf/model/literal/date.rb +73 -0
  25. data/lib/rdf/model/literal/datetime.rb +72 -0
  26. data/lib/rdf/model/literal/decimal.rb +86 -0
  27. data/lib/rdf/model/literal/double.rb +189 -0
  28. data/lib/rdf/model/literal/integer.rb +126 -0
  29. data/lib/rdf/model/literal/numeric.rb +184 -0
  30. data/lib/rdf/model/literal/time.rb +87 -0
  31. data/lib/rdf/model/literal/token.rb +47 -0
  32. data/lib/rdf/model/literal/xml.rb +39 -0
  33. data/lib/rdf/model/literal.rb +373 -0
  34. data/lib/rdf/model/node.rb +156 -0
  35. data/lib/rdf/model/resource.rb +28 -0
  36. data/lib/rdf/model/statement.rb +296 -0
  37. data/lib/rdf/model/term.rb +77 -0
  38. data/lib/rdf/model/uri.rb +570 -0
  39. data/lib/rdf/model/value.rb +133 -0
  40. data/lib/rdf/nquads.rb +152 -0
  41. data/lib/rdf/ntriples/format.rb +48 -0
  42. data/lib/rdf/ntriples/reader.rb +239 -0
  43. data/lib/rdf/ntriples/writer.rb +219 -0
  44. data/lib/rdf/ntriples.rb +104 -0
  45. data/lib/rdf/query/pattern.rb +329 -0
  46. data/lib/rdf/query/solution.rb +252 -0
  47. data/lib/rdf/query/solutions.rb +237 -0
  48. data/lib/rdf/query/variable.rb +221 -0
  49. data/lib/rdf/query.rb +404 -0
  50. data/lib/rdf/reader.rb +511 -0
  51. data/lib/rdf/repository.rb +389 -0
  52. data/lib/rdf/transaction.rb +161 -0
  53. data/lib/rdf/util/aliasing.rb +63 -0
  54. data/lib/rdf/util/cache.rb +139 -0
  55. data/lib/rdf/util/file.rb +38 -0
  56. data/lib/rdf/util/uuid.rb +36 -0
  57. data/lib/rdf/util.rb +6 -0
  58. data/lib/rdf/version.rb +19 -0
  59. data/lib/rdf/vocab/cc.rb +18 -0
  60. data/lib/rdf/vocab/cert.rb +13 -0
  61. data/lib/rdf/vocab/dc.rb +63 -0
  62. data/lib/rdf/vocab/dc11.rb +23 -0
  63. data/lib/rdf/vocab/doap.rb +45 -0
  64. data/lib/rdf/vocab/exif.rb +168 -0
  65. data/lib/rdf/vocab/foaf.rb +69 -0
  66. data/lib/rdf/vocab/geo.rb +13 -0
  67. data/lib/rdf/vocab/http.rb +26 -0
  68. data/lib/rdf/vocab/owl.rb +59 -0
  69. data/lib/rdf/vocab/rdfs.rb +17 -0
  70. data/lib/rdf/vocab/rsa.rb +12 -0
  71. data/lib/rdf/vocab/rss.rb +14 -0
  72. data/lib/rdf/vocab/sioc.rb +93 -0
  73. data/lib/rdf/vocab/skos.rb +36 -0
  74. data/lib/rdf/vocab/wot.rb +21 -0
  75. data/lib/rdf/vocab/xhtml.rb +9 -0
  76. data/lib/rdf/vocab/xsd.rb +58 -0
  77. data/lib/rdf/vocab.rb +215 -0
  78. data/lib/rdf/writer.rb +475 -0
  79. data/lib/rdf.rb +192 -0
  80. metadata +173 -0
@@ -0,0 +1,104 @@
1
+ module RDF
2
+ ##
3
+ # **`RDF::NTriples`** provides support for the N-Triples serialization
4
+ # format.
5
+ #
6
+ # N-Triples is a line-based plain-text format for encoding an RDF graph.
7
+ # It is a very restricted, explicit and well-defined subset of both
8
+ # [Turtle](http://www.w3.org/TeamSubmission/turtle/) and
9
+ # [Notation3](http://www.w3.org/TeamSubmission/n3/) (N3).
10
+ #
11
+ # The MIME content type for N-Triples files is `text/plain` and the
12
+ # recommended file extension is `.nt`.
13
+ #
14
+ # An example of an RDF statement in N-Triples format:
15
+ #
16
+ # <http://rubyforge.org/> <http://purl.org/dc/terms/title> "RubyForge" .
17
+ #
18
+ # Installation
19
+ # ------------
20
+ #
21
+ # This is the only RDF serialization format that is directly supported by
22
+ # RDF.rb. Support for other formats is available in the form of add-on
23
+ # gems, e.g. 'rdf-xml' or 'rdf-json'.
24
+ #
25
+ # Documentation
26
+ # -------------
27
+ #
28
+ # * {RDF::NTriples::Format}
29
+ # * {RDF::NTriples::Reader}
30
+ # * {RDF::NTriples::Writer}
31
+ #
32
+ # @example Requiring the `RDF::NTriples` module explicitly
33
+ # require 'rdf/ntriples'
34
+ #
35
+ # @see http://www.w3.org/TR/rdf-testcases/#ntriples
36
+ # @see http://en.wikipedia.org/wiki/N-Triples
37
+ # @see http://librdf.org/ntriples/
38
+ #
39
+ # @author [Arto Bendiken](http://ar.to/)
40
+ module NTriples
41
+ require 'iconv' unless "".respond_to?(:encode )# needed on Ruby 1.8.x
42
+ require 'rdf/ntriples/format'
43
+ autoload :Reader, 'rdf/ntriples/reader'
44
+ autoload :Writer, 'rdf/ntriples/writer'
45
+
46
+ ##
47
+ # Reconstructs an RDF value from its serialized N-Triples
48
+ # representation.
49
+ #
50
+ # @param [String] data
51
+ # @return [RDF::Value]
52
+ # @see RDF::NTriples::Reader.unserialize
53
+ # @since 0.1.5
54
+ def self.unserialize(data)
55
+ Reader.unserialize(data)
56
+ end
57
+
58
+ ##
59
+ # Returns the serialized N-Triples representation of the given RDF
60
+ # value.
61
+ #
62
+ # @param [RDF::Value] value
63
+ # @return [String]
64
+ # @see RDF::NTriples::Writer.serialize
65
+ # @since 0.1.5
66
+ def self.serialize(value)
67
+ Writer.serialize(value)
68
+ end
69
+
70
+ ##
71
+ # @param [String] string
72
+ # @return [String]
73
+ # @see RDF::NTriples::Reader.unescape
74
+ # @since 0.2.2
75
+ def self.unescape(string)
76
+ Reader.unescape(string)
77
+ end
78
+
79
+ ##
80
+ # @param [String] string
81
+ # @return [String]
82
+ # @see RDF::NTriples::Writer.escape
83
+ # @since 0.2.2
84
+ def self.escape(string)
85
+ Writer.escape(string)
86
+ end
87
+ end # NTriples
88
+
89
+ ##
90
+ # Extensions for `RDF::Value`.
91
+ module Value
92
+ ##
93
+ # Returns the N-Triples representation of this value.
94
+ #
95
+ # This method is only available when the 'rdf/ntriples' serializer has
96
+ # been explicitly required.
97
+ #
98
+ # @return [String]
99
+ # @since 0.2.1
100
+ def to_ntriples
101
+ RDF::NTriples.serialize(self)
102
+ end
103
+ end # Value
104
+ end # RDF
@@ -0,0 +1,329 @@
1
+ module RDF; class Query
2
+ ##
3
+ # An RDF query pattern.
4
+ class Pattern < RDF::Statement
5
+ ##
6
+ # @private
7
+ # @since 0.2.2
8
+ def self.from(pattern, options = {})
9
+ case pattern
10
+ when Pattern then pattern
11
+ when Array, Statement
12
+ self.new(pattern[0], pattern[1], pattern[2], options.merge(:context => pattern[3]))
13
+ when Hash then self.new(options.merge(pattern))
14
+ else raise ArgumentError, "expected RDF::Query::Pattern, RDF::Statement, Hash, or Array, but got #{pattern.inspect}"
15
+ end
16
+ end
17
+
18
+ ##
19
+ # @overload initialize(options = {})
20
+ # @param [Hash{Symbol => Object}] options
21
+ # @option options [Variable, Resource] :subject (nil)
22
+ # @option options [Variable, URI] :predicate (nil)
23
+ # @option options [Variable, Term] :object (nil)
24
+ # @option options [Variable, Resource] :context (nil)
25
+ # A context of nil matches any context, a context of false, matches only the default context.
26
+ # @option options [Boolean] :optional (false)
27
+ #
28
+ # @overload initialize(subject, predicate, object, options = {})
29
+ # @param [Variable, Resource] subject
30
+ # @param [Variable, URI] predicate
31
+ # @param [Variable, Term] object
32
+ # @param [Hash{Symbol => Object}] options
33
+ # @option options [Variable, Resource] :context (nil)
34
+ # @option options [Boolean] :optional (false)
35
+ def initialize(subject = nil, predicate = nil, object = nil, options = {})
36
+ super
37
+ end
38
+
39
+ ##
40
+ # @private
41
+ def initialize!
42
+ @context = Variable.new(@context) if @context.is_a?(Symbol)
43
+ @subject = Variable.new(@subject) if @subject.is_a?(Symbol)
44
+ @predicate = Variable.new(@predicate) if @predicate.is_a?(Symbol)
45
+ @object = Variable.new(@object) if @object.is_a?(Symbol)
46
+ super
47
+ end
48
+
49
+ ##
50
+ # Any additional options for this pattern.
51
+ #
52
+ # @return [Hash]
53
+ attr_reader :options
54
+
55
+ ##
56
+ # The estimated cost of this pattern (for query optimization).
57
+ #
58
+ # @return [Numeric]
59
+ attr_accessor :cost
60
+
61
+ ##
62
+ # Returns `true` if this is a blank pattern, with all terms being `nil`.
63
+ #
64
+ # @return [Boolean] `true` or `false`
65
+ # @since 0.3.0
66
+ def blank?
67
+ subject.nil? && predicate.nil? && object.nil? && context.nil?
68
+ end
69
+
70
+ ##
71
+ # Returns `true` if this is a constant pattern, with all terms being
72
+ # either URIs, blank nodes, or literals.
73
+ #
74
+ # A constant pattern is structurally and functionally equivalent to an
75
+ # RDF statement.
76
+ #
77
+ # @return [Boolean] `true` or `false`
78
+ # @since 0.3.0
79
+ def constant?
80
+ !(variable?)
81
+ end
82
+
83
+ ##
84
+ # Returns `true` if this is a variable pattern, with any term being
85
+ # `nil` or a variable.
86
+ #
87
+ # @return [Boolean] `true` or `false`
88
+ # @since 0.3.0
89
+ def variable?
90
+ subject.nil? || predicate.nil? || object.nil? || context.nil? || has_variables?
91
+ end
92
+
93
+ ##
94
+ # Returns `true` if this pattern contains any variables.
95
+ #
96
+ # @return [Boolean] `true` or `false`
97
+ # @since 0.3.0
98
+ def has_variables?
99
+ subject.is_a?(Variable) ||
100
+ predicate.is_a?(Variable) ||
101
+ object.is_a?(Variable) ||
102
+ context.is_a?(Variable)
103
+ end
104
+ alias_method :variables?, :has_variables?
105
+
106
+ ##
107
+ # Returns `true` if this is an optional pattern.
108
+ #
109
+ # @example
110
+ # Pattern.new(:s, :p, :o).optional? #=> false
111
+ # Pattern.new(:s, :p, :o, :optional => true).optional? #=> true
112
+ #
113
+ # @return [Boolean] `true` or `false`
114
+ # @since 0.3.0
115
+ def optional?
116
+ !!options[:optional]
117
+ end
118
+
119
+ ##
120
+ # Executes this query pattern on the given `queryable` object.
121
+ #
122
+ # Values are matched using using Queryable#query_pattern.
123
+ #
124
+ # If the optional `bindings` are given, variables will be substituted with their values
125
+ # when executing the query.
126
+ #
127
+ # To match triples only in the default context, set context to `false'.
128
+ #
129
+ # @example
130
+ # Pattern.new(:s, :p, :o).execute(RDF::Repository.load('data.nt'))
131
+ #
132
+ # @param [RDF::Queryable] queryable
133
+ # the graph or repository to query
134
+ # @param [Hash{Symbol => RDF::Term}] bindings
135
+ # optional variable bindings to use
136
+ # @yield [statement]
137
+ # each matching statement
138
+ # @yieldparam [RDF::Statement] statement
139
+ # an RDF statement matching this pattern
140
+ # @return [Enumerator]
141
+ # an enumerator yielding matching statements
142
+ # @see RDF::Queryable#query
143
+ # @since 0.3.0
144
+ def execute(queryable, bindings = {}, &block)
145
+ query = {
146
+ :subject => subject.is_a?(Variable) && bindings[subject.to_sym] ? bindings[subject.to_sym] : subject,
147
+ :predicate => predicate.is_a?(Variable) && bindings[predicate.to_sym] ? bindings[predicate.to_sym] : predicate,
148
+ :object => object.is_a?(Variable) && bindings[object.to_sym] ? bindings[object.to_sym] : object,
149
+ :context => context.is_a?(Variable) && bindings[context.to_sym] ? bindings[context.to_sym] : context,
150
+ }.delete_if{|k,v| v.nil?}
151
+
152
+ # Do all the variable terms refer to distinct variables?
153
+ variables = self.variables
154
+ if variable_count == variables.size
155
+ # If so, we can just let the repository implementation handle
156
+ # everything and yield matching statements directly:
157
+ queryable.query(query, &block)
158
+
159
+ # No, some terms actually refer to the same variable...
160
+ else
161
+ # Figure out which terms refer to the same variable:
162
+ terms = variables.each_key.find do |name|
163
+ terms = variable_terms(name)
164
+ break terms if terms.size > 1
165
+ end
166
+ queryable.query(query) do |statement|
167
+ # Only yield those matching statements where the variable
168
+ # constraint is also satisfied:
169
+ # FIXME: `Array#uniq` uses `#eql?` and `#hash`, not `#==`
170
+ if matches = terms.map { |term| statement.send(term) }.uniq.size.equal?(1)
171
+ block.call(statement)
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ ##
178
+ # Returns a query solution constructed by binding any variables in this
179
+ # pattern with the corresponding terms in the given `statement`.
180
+ #
181
+ # @example
182
+ # pattern.solution(statement)
183
+ #
184
+ # @param [RDF::Statement] statement
185
+ # an RDF statement to bind terms from
186
+ # @return [RDF::Query::Solution]
187
+ # @since 0.3.0
188
+ def solution(statement)
189
+ RDF::Query::Solution.new do |solution|
190
+ solution[subject.to_sym] = statement.subject if subject.is_a?(Variable)
191
+ solution[predicate.to_sym] = statement.predicate if predicate.is_a?(Variable)
192
+ solution[object.to_sym] = statement.object if object.is_a?(Variable)
193
+ solution[context.to_sym] = statement.context if context.is_a?(Variable)
194
+ end
195
+ end
196
+
197
+ ##
198
+ # Returns the variable terms in this pattern.
199
+ #
200
+ # @example
201
+ # Pattern.new(RDF::Node.new, :p, 123).variable_terms #=> [:predicate]
202
+ #
203
+ # @param [Symbol, #to_sym] name
204
+ # an optional variable name
205
+ # @return [Array<Symbol>]
206
+ # @since 0.3.0
207
+ def variable_terms(name = nil)
208
+ terms = []
209
+ terms << :subject if subject.is_a?(Variable) && (!name || name.eql?(subject.name))
210
+ terms << :predicate if predicate.is_a?(Variable) && (!name || name.eql?(predicate.name))
211
+ terms << :object if object.is_a?(Variable) && (!name || name.eql?(object.name))
212
+ terms << :context if context.is_a?(Variable) && (!name || name.eql?(context.name))
213
+ terms
214
+ end
215
+
216
+ ##
217
+ # Returns the number of variables in this pattern.
218
+ #
219
+ # Note: this does not count distinct variables, and will therefore e.g.
220
+ # return 3 even if two terms are actually the same variable.
221
+ #
222
+ # @return [Integer] (0..3)
223
+ def variable_count
224
+ count = 0
225
+ count += 1 if subject.is_a?(Variable)
226
+ count += 1 if predicate.is_a?(Variable)
227
+ count += 1 if object.is_a?(Variable)
228
+ count += 1 if context.is_a?(Variable)
229
+ count
230
+ end
231
+ alias_method :cardinality, :variable_count
232
+ alias_method :arity, :variable_count
233
+
234
+ ##
235
+ # Returns all variables in this pattern.
236
+ #
237
+ # Note: this returns a hash containing distinct variables only.
238
+ #
239
+ # @return [Hash{Symbol => Variable}]
240
+ def variables
241
+ variables = {}
242
+ variables.merge!(subject.variables) if subject.is_a?(Variable)
243
+ variables.merge!(predicate.variables) if predicate.is_a?(Variable)
244
+ variables.merge!(object.variables) if object.is_a?(Variable)
245
+ variables.merge!(context.variables) if context.is_a?(Variable)
246
+ variables
247
+ end
248
+
249
+ ##
250
+ # Returns `true` if this pattern contains bindings.
251
+ #
252
+ # @return [Boolean] `true` or `false`
253
+ def bindings?
254
+ !bindings.empty?
255
+ end
256
+
257
+ ##
258
+ # Returns the number of bindings in this pattern.
259
+ #
260
+ # @return [Integer] (0..3)
261
+ def binding_count
262
+ bindings.size
263
+ end
264
+
265
+ ##
266
+ # Returns all bindings in this pattern.
267
+ #
268
+ # @return [Hash{Symbol => RDF::Term}]
269
+ def bindings
270
+ bindings = {}
271
+ bindings.merge!(subject.bindings) if subject.is_a?(Variable)
272
+ bindings.merge!(predicate.bindings) if predicate.is_a?(Variable)
273
+ bindings.merge!(object.bindings) if object.is_a?(Variable)
274
+ bindings.merge!(context.bindings) if context.is_a?(Variable)
275
+ bindings
276
+ end
277
+
278
+ ##
279
+ # Returns `true` if all variables in this pattern are bound.
280
+ #
281
+ # @return [Boolean] `true` or `false`
282
+ def bound?
283
+ !variables.empty? && variables.values.all?(&:bound?)
284
+ end
285
+
286
+ ##
287
+ # Returns all bound variables in this pattern.
288
+ #
289
+ # @return [Hash{Symbol => Variable}]
290
+ def bound_variables
291
+ variables.reject { |name, variable| variable.unbound? }
292
+ end
293
+
294
+ ##
295
+ # Returns `true` if all variables in this pattern are unbound.
296
+ #
297
+ # @return [Boolean] `true` or `false`
298
+ def unbound?
299
+ !variables.empty? && variables.values.all?(&:unbound?)
300
+ end
301
+
302
+ ##
303
+ # Returns all unbound variables in this pattern.
304
+ #
305
+ # @return [Hash{Symbol => Variable}]
306
+ def unbound_variables
307
+ variables.reject { |name, variable| variable.bound? }
308
+ end
309
+
310
+ ##
311
+ # Returns a string representation of this pattern.
312
+ #
313
+ # @return [String]
314
+ def to_s
315
+ StringIO.open do |buffer| # FIXME in RDF::Statement
316
+ buffer << 'OPTIONAL ' if optional?
317
+ buffer << [subject, predicate, object].map do |r|
318
+ r.is_a?(RDF::Query::Variable) ? r.to_s : RDF::NTriples.serialize(r)
319
+ end.join(" ")
320
+ buffer << case context
321
+ when nil, false then " ."
322
+ when Variable then " #{context.to_s} ."
323
+ else " #{RDF::NTriples.serialize(context)} ."
324
+ end
325
+ buffer.string
326
+ end
327
+ end
328
+ end # Pattern
329
+ end; end # RDF::Query
@@ -0,0 +1,252 @@
1
+ class RDF::Query
2
+ ##
3
+ # An RDF query solution.
4
+ #
5
+ # @example Iterating over every binding in the solution
6
+ # solution.each_binding { |name, value| puts value.inspect }
7
+ # solution.each_variable { |variable| puts variable.value.inspect }
8
+ #
9
+ # @example Iterating over every value in the solution
10
+ # solution.each_value { |value| puts value.inspect }
11
+ #
12
+ # @example Checking whether a variable is bound or unbound
13
+ # solution.bound?(:title)
14
+ # solution.unbound?(:mbox)
15
+ #
16
+ # @example Retrieving the value of a bound variable
17
+ # solution[:mbox]
18
+ # solution.mbox
19
+ #
20
+ # @example Retrieving all bindings in the solution as a `Hash`
21
+ # solution.to_hash #=> {:mbox => "jrhacker@example.org", ...}
22
+ #
23
+ class Solution
24
+ # Undefine all superfluous instance methods:
25
+ undef_method(*(instance_methods.map(&:to_sym) - [:__id__, :__send__, :__class__, :__eval__,
26
+ :object_id, :dup, :instance_eval, :inspect, :to_s,
27
+ :class, :is_a?, :respond_to?, :respond_to_missing?]))
28
+
29
+ include Enumerable
30
+
31
+ ##
32
+ # Initializes the query solution.
33
+ #
34
+ # @param [Hash{Symbol => RDF::Term}] bindings
35
+ # @yield [solution]
36
+ def initialize(bindings = {}, &block)
37
+ @bindings = bindings.to_hash
38
+
39
+ if block_given?
40
+ case block.arity
41
+ when 1 then block.call(self)
42
+ else instance_eval(&block)
43
+ end
44
+ end
45
+ end
46
+
47
+ # @private
48
+ attr_reader :bindings
49
+
50
+ ##
51
+ # Enumerates over every variable binding in this solution.
52
+ #
53
+ # @yield [name, value]
54
+ # @yieldparam [Symbol] name
55
+ # @yieldparam [RDF::Term] value
56
+ # @return [Enumerator]
57
+ def each_binding(&block)
58
+ @bindings.each(&block)
59
+ end
60
+ alias_method :each, :each_binding
61
+
62
+ ##
63
+ # Enumerates over every variable name in this solution.
64
+ #
65
+ # @yield [name]
66
+ # @yieldparam [Symbol] name
67
+ # @return [Enumerator]
68
+ def each_name(&block)
69
+ @bindings.each_key(&block)
70
+ end
71
+ alias_method :each_key, :each_name
72
+
73
+ ##
74
+ # Enumerates over every variable value in this solution.
75
+ #
76
+ # @yield [value]
77
+ # @yieldparam [RDF::Term] value
78
+ # @return [Enumerator]
79
+ def each_value(&block)
80
+ @bindings.each_value(&block)
81
+ end
82
+
83
+ ##
84
+ # Returns `true` if this solution contains bindings for any of the given
85
+ # `variables`.
86
+ #
87
+ # @param [Array<Symbol, #to_sym>] variables
88
+ # an array of variables to check
89
+ # @return [Boolean] `true` or `false`
90
+ # @since 0.3.0
91
+ def has_variables?(variables)
92
+ variables.any? { |variable| bound?(variable) }
93
+ end
94
+
95
+ ##
96
+ # Enumerates over every variable in this solution.
97
+ #
98
+ # @yield [variable]
99
+ # @yieldparam [Variable]
100
+ # @return [Enumerator]
101
+ def each_variable(&block)
102
+ @bindings.each do |name, value|
103
+ block.call(Variable.new(name, value))
104
+ end
105
+ end
106
+
107
+ ##
108
+ # Returns `true` if the variable `name` is bound in this solution.
109
+ #
110
+ # @param [Symbol, #to_sym] name
111
+ # the variable name
112
+ # @return [Boolean] `true` or `false`
113
+ def bound?(name)
114
+ !unbound?(name)
115
+ end
116
+
117
+ ##
118
+ # Returns `true` if the variable `name` is unbound in this solution.
119
+ #
120
+ # @param [Symbol, #to_sym] name
121
+ # the variable name
122
+ # @return [Boolean] `true` or `false`
123
+ def unbound?(name)
124
+ @bindings[name.to_sym].nil?
125
+ end
126
+
127
+ ##
128
+ # Returns the value of the variable `name`.
129
+ #
130
+ # @param [Symbol, #to_sym] name
131
+ # the variable name
132
+ # @return [RDF::Term]
133
+ def [](name)
134
+ @bindings[name.to_sym]
135
+ end
136
+
137
+ ##
138
+ # Binds or rebinds the variable `name` to the given `value`.
139
+ #
140
+ # @param [Symbol, #to_sym] name
141
+ # the variable name
142
+ # @param [RDF::Term] value
143
+ # @return [RDF::Term]
144
+ # @since 0.3.0
145
+ def []=(name, value)
146
+ @bindings[name.to_sym] = value
147
+ end
148
+
149
+ ##
150
+ # Merges the bindings from the given `other` query solution into this
151
+ # one, overwriting any existing ones having the same name.
152
+ #
153
+ # @param [RDF::Query::Solution, #to_hash] other
154
+ # another query solution or hash bindings
155
+ # @return [void] self
156
+ # @since 0.3.0
157
+ def merge!(other)
158
+ @bindings.merge!(other.to_hash)
159
+ self
160
+ end
161
+
162
+ ##
163
+ # Merges the bindings from the given `other` query solution with a copy
164
+ # of this one.
165
+ #
166
+ # @param [RDF::Query::Solution, #to_hash] other
167
+ # another query solution or hash bindings
168
+ # @return [RDF::Query::Solution]
169
+ # @since 0.3.0
170
+ def merge(other)
171
+ self.class.new(@bindings.dup).merge!(other)
172
+ end
173
+
174
+ ##
175
+ # Compatible Mappings
176
+ # Two solution mappings μ1 and μ2 are compatible if, for every variable v in dom(μ1) and in dom(μ2), μ1(v) = μ2(v).
177
+ #
178
+ # @param [RDF::Query::Solution, #to_hash] other
179
+ # another query solution or hash bindings
180
+ # @return [Boolean]
181
+ def compatible?(other)
182
+ @bindings.all? do |k, v|
183
+ !other.to_hash.has_key?(k) || other[k].eql?(v)
184
+ end
185
+ end
186
+
187
+ ##
188
+ # Isomorphic Mappings
189
+ # Two solution mappings μ1 and μ2 are isomorphic if,
190
+ # for every variable v in dom(μ1) and in dom(μ2), μ1(v) = μ2(v).
191
+ #
192
+ # @param [RDF::Query::Solution, #to_hash] other
193
+ # another query solution or hash bindings
194
+ # @return [Boolean]
195
+ def isomorphic_with?(other)
196
+ @bindings.all? do |k, v|
197
+ !other.to_hash.has_key?(k) || other[k].eql?(v)
198
+ end
199
+ end
200
+
201
+ ##
202
+ # @return [Array<Array(Symbol, RDF::Term)>}
203
+ def to_a
204
+ @bindings.to_a
205
+ end
206
+
207
+ ##
208
+ # @return [Hash{Symbol => RDF::Term}}
209
+ def to_hash
210
+ @bindings.dup
211
+ end
212
+
213
+ ##
214
+ # Integer hash of this solution
215
+ # @return [Integer]
216
+ def hash
217
+ @bindings.hash
218
+ end
219
+
220
+ ##
221
+ # Equivalence of solution
222
+ def eql?(other)
223
+ other.is_a?(Solution) && @bindings.eql?(other.bindings)
224
+ end
225
+ alias_method :==, :eql?
226
+
227
+ ##
228
+ # Equals of solution
229
+ def ==(other)
230
+ other.is_a?(Solution) && @bindings == other.bindings
231
+ end
232
+
233
+ ##
234
+ # @return [String]
235
+ def inspect
236
+ sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, @bindings.inspect)
237
+ end
238
+
239
+ protected
240
+
241
+ ##
242
+ # @param [Symbol] name
243
+ # @return [RDF::Term]
244
+ def method_missing(name, *args, &block)
245
+ if args.empty? && @bindings.has_key?(name.to_sym)
246
+ @bindings[name.to_sym]
247
+ else
248
+ super # raises NoMethodError
249
+ end
250
+ end
251
+ end # Solution
252
+ end # RDF::Query