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.
- data/AUTHORS +3 -0
- data/CREDITS +9 -0
- data/README +361 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/bin/rdf +18 -0
- data/etc/doap.nt +62 -0
- data/lib/df.rb +1 -0
- data/lib/rdf/cli.rb +200 -0
- data/lib/rdf/format.rb +383 -0
- data/lib/rdf/mixin/countable.rb +39 -0
- data/lib/rdf/mixin/durable.rb +31 -0
- data/lib/rdf/mixin/enumerable.rb +637 -0
- data/lib/rdf/mixin/indexable.rb +26 -0
- data/lib/rdf/mixin/inferable.rb +5 -0
- data/lib/rdf/mixin/mutable.rb +191 -0
- data/lib/rdf/mixin/queryable.rb +265 -0
- data/lib/rdf/mixin/readable.rb +15 -0
- data/lib/rdf/mixin/type_check.rb +21 -0
- data/lib/rdf/mixin/writable.rb +152 -0
- data/lib/rdf/model/graph.rb +263 -0
- data/lib/rdf/model/list.rb +731 -0
- data/lib/rdf/model/literal/boolean.rb +121 -0
- data/lib/rdf/model/literal/date.rb +73 -0
- data/lib/rdf/model/literal/datetime.rb +72 -0
- data/lib/rdf/model/literal/decimal.rb +86 -0
- data/lib/rdf/model/literal/double.rb +189 -0
- data/lib/rdf/model/literal/integer.rb +126 -0
- data/lib/rdf/model/literal/numeric.rb +184 -0
- data/lib/rdf/model/literal/time.rb +87 -0
- data/lib/rdf/model/literal/token.rb +47 -0
- data/lib/rdf/model/literal/xml.rb +39 -0
- data/lib/rdf/model/literal.rb +373 -0
- data/lib/rdf/model/node.rb +156 -0
- data/lib/rdf/model/resource.rb +28 -0
- data/lib/rdf/model/statement.rb +296 -0
- data/lib/rdf/model/term.rb +77 -0
- data/lib/rdf/model/uri.rb +570 -0
- data/lib/rdf/model/value.rb +133 -0
- data/lib/rdf/nquads.rb +152 -0
- data/lib/rdf/ntriples/format.rb +48 -0
- data/lib/rdf/ntriples/reader.rb +239 -0
- data/lib/rdf/ntriples/writer.rb +219 -0
- data/lib/rdf/ntriples.rb +104 -0
- data/lib/rdf/query/pattern.rb +329 -0
- data/lib/rdf/query/solution.rb +252 -0
- data/lib/rdf/query/solutions.rb +237 -0
- data/lib/rdf/query/variable.rb +221 -0
- data/lib/rdf/query.rb +404 -0
- data/lib/rdf/reader.rb +511 -0
- data/lib/rdf/repository.rb +389 -0
- data/lib/rdf/transaction.rb +161 -0
- data/lib/rdf/util/aliasing.rb +63 -0
- data/lib/rdf/util/cache.rb +139 -0
- data/lib/rdf/util/file.rb +38 -0
- data/lib/rdf/util/uuid.rb +36 -0
- data/lib/rdf/util.rb +6 -0
- data/lib/rdf/version.rb +19 -0
- data/lib/rdf/vocab/cc.rb +18 -0
- data/lib/rdf/vocab/cert.rb +13 -0
- data/lib/rdf/vocab/dc.rb +63 -0
- data/lib/rdf/vocab/dc11.rb +23 -0
- data/lib/rdf/vocab/doap.rb +45 -0
- data/lib/rdf/vocab/exif.rb +168 -0
- data/lib/rdf/vocab/foaf.rb +69 -0
- data/lib/rdf/vocab/geo.rb +13 -0
- data/lib/rdf/vocab/http.rb +26 -0
- data/lib/rdf/vocab/owl.rb +59 -0
- data/lib/rdf/vocab/rdfs.rb +17 -0
- data/lib/rdf/vocab/rsa.rb +12 -0
- data/lib/rdf/vocab/rss.rb +14 -0
- data/lib/rdf/vocab/sioc.rb +93 -0
- data/lib/rdf/vocab/skos.rb +36 -0
- data/lib/rdf/vocab/wot.rb +21 -0
- data/lib/rdf/vocab/xhtml.rb +9 -0
- data/lib/rdf/vocab/xsd.rb +58 -0
- data/lib/rdf/vocab.rb +215 -0
- data/lib/rdf/writer.rb +475 -0
- data/lib/rdf.rb +192 -0
- metadata +173 -0
data/lib/rdf/ntriples.rb
ADDED
@@ -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
|