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