rdf 0.3.3 → 0.3.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.
- data/README +107 -20
- data/VERSION +1 -1
- data/bin/rdf +4 -8
- data/etc/doap.nt +8 -8
- data/lib/rdf.rb +1 -0
- data/lib/rdf/cli.rb +146 -19
- data/lib/rdf/format.rb +59 -10
- data/lib/rdf/mixin/type_check.rb +21 -0
- data/lib/rdf/model/graph.rb +1 -0
- data/lib/rdf/model/list.rb +36 -3
- data/lib/rdf/model/literal.rb +113 -74
- data/lib/rdf/model/literal/boolean.rb +15 -5
- data/lib/rdf/model/literal/date.rb +24 -6
- data/lib/rdf/model/literal/datetime.rb +21 -4
- data/lib/rdf/model/literal/decimal.rb +3 -128
- data/lib/rdf/model/literal/double.rb +4 -107
- data/lib/rdf/model/literal/integer.rb +3 -97
- data/lib/rdf/model/literal/numeric.rb +178 -3
- data/lib/rdf/model/literal/time.rb +23 -3
- data/lib/rdf/model/literal/token.rb +2 -2
- data/lib/rdf/model/literal/xml.rb +1 -1
- data/lib/rdf/model/node.rb +35 -5
- data/lib/rdf/model/statement.rb +7 -6
- data/lib/rdf/model/term.rb +32 -0
- data/lib/rdf/model/uri.rb +13 -7
- data/lib/rdf/model/value.rb +10 -0
- data/lib/rdf/nquads.rb +120 -3
- data/lib/rdf/ntriples/format.rb +9 -1
- data/lib/rdf/ntriples/writer.rb +1 -0
- data/lib/rdf/query.rb +121 -13
- data/lib/rdf/query/pattern.rb +28 -15
- data/lib/rdf/query/solution.rb +47 -0
- data/lib/rdf/query/solutions.rb +45 -10
- data/lib/rdf/query/variable.rb +39 -4
- data/lib/rdf/reader.rb +47 -4
- data/lib/rdf/repository.rb +8 -4
- data/lib/rdf/util/file.rb +5 -2
- data/lib/rdf/version.rb +1 -1
- data/lib/rdf/writer.rb +34 -5
- metadata +56 -88
data/lib/rdf/query/pattern.rb
CHANGED
@@ -22,6 +22,7 @@ module RDF; class Query
|
|
22
22
|
# @option options [Variable, URI] :predicate (nil)
|
23
23
|
# @option options [Variable, Term] :object (nil)
|
24
24
|
# @option options [Variable, Resource] :context (nil)
|
25
|
+
# A context of nil matches any context, a context of false, matches only the default context.
|
25
26
|
# @option options [Boolean] :optional (false)
|
26
27
|
#
|
27
28
|
# @overload initialize(subject, predicate, object, options = {})
|
@@ -118,10 +119,12 @@ module RDF; class Query
|
|
118
119
|
##
|
119
120
|
# Executes this query pattern on the given `queryable` object.
|
120
121
|
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
# given, variables will be substituted with their values
|
124
|
-
# the query.
|
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'.
|
125
128
|
#
|
126
129
|
# @example
|
127
130
|
# Pattern.new(:s, :p, :o).execute(RDF::Repository.load('data.nt'))
|
@@ -140,11 +143,11 @@ module RDF; class Query
|
|
140
143
|
# @since 0.3.0
|
141
144
|
def execute(queryable, bindings = {}, &block)
|
142
145
|
query = {
|
143
|
-
:subject => subject && subject.
|
144
|
-
:predicate => predicate && predicate.
|
145
|
-
:object => object && object.
|
146
|
-
|
147
|
-
}
|
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?}
|
148
151
|
|
149
152
|
# Do all the variable terms refer to distinct variables?
|
150
153
|
variables = self.variables
|
@@ -184,9 +187,10 @@ module RDF; class Query
|
|
184
187
|
# @since 0.3.0
|
185
188
|
def solution(statement)
|
186
189
|
RDF::Query::Solution.new do |solution|
|
187
|
-
solution[subject.to_sym] = statement.subject if subject.
|
188
|
-
solution[predicate.to_sym] = statement.predicate if predicate.
|
189
|
-
solution[object.to_sym] = statement.object if object.
|
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)
|
190
194
|
end
|
191
195
|
end
|
192
196
|
|
@@ -205,6 +209,7 @@ module RDF; class Query
|
|
205
209
|
terms << :subject if subject.is_a?(Variable) && (!name || name.eql?(subject.name))
|
206
210
|
terms << :predicate if predicate.is_a?(Variable) && (!name || name.eql?(predicate.name))
|
207
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))
|
208
213
|
terms
|
209
214
|
end
|
210
215
|
|
@@ -220,6 +225,7 @@ module RDF; class Query
|
|
220
225
|
count += 1 if subject.is_a?(Variable)
|
221
226
|
count += 1 if predicate.is_a?(Variable)
|
222
227
|
count += 1 if object.is_a?(Variable)
|
228
|
+
count += 1 if context.is_a?(Variable)
|
223
229
|
count
|
224
230
|
end
|
225
231
|
alias_method :cardinality, :variable_count
|
@@ -236,6 +242,7 @@ module RDF; class Query
|
|
236
242
|
variables.merge!(subject.variables) if subject.is_a?(Variable)
|
237
243
|
variables.merge!(predicate.variables) if predicate.is_a?(Variable)
|
238
244
|
variables.merge!(object.variables) if object.is_a?(Variable)
|
245
|
+
variables.merge!(context.variables) if context.is_a?(Variable)
|
239
246
|
variables
|
240
247
|
end
|
241
248
|
|
@@ -264,6 +271,7 @@ module RDF; class Query
|
|
264
271
|
bindings.merge!(subject.bindings) if subject.is_a?(Variable)
|
265
272
|
bindings.merge!(predicate.bindings) if predicate.is_a?(Variable)
|
266
273
|
bindings.merge!(object.bindings) if object.is_a?(Variable)
|
274
|
+
bindings.merge!(context.bindings) if context.is_a?(Variable)
|
267
275
|
bindings
|
268
276
|
end
|
269
277
|
|
@@ -306,9 +314,14 @@ module RDF; class Query
|
|
306
314
|
def to_s
|
307
315
|
StringIO.open do |buffer| # FIXME in RDF::Statement
|
308
316
|
buffer << 'OPTIONAL ' if optional?
|
309
|
-
buffer <<
|
310
|
-
|
311
|
-
|
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
|
312
325
|
buffer.string
|
313
326
|
end
|
314
327
|
end
|
data/lib/rdf/query/solution.rb
CHANGED
@@ -171,6 +171,33 @@ class RDF::Query
|
|
171
171
|
self.class.new(@bindings.dup).merge!(other)
|
172
172
|
end
|
173
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
|
+
|
174
201
|
##
|
175
202
|
# @return [Array<Array(Symbol, RDF::Term)>}
|
176
203
|
def to_a
|
@@ -182,6 +209,26 @@ class RDF::Query
|
|
182
209
|
def to_hash
|
183
210
|
@bindings.dup
|
184
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
|
185
232
|
|
186
233
|
##
|
187
234
|
# @return [String]
|
data/lib/rdf/query/solutions.rb
CHANGED
@@ -16,13 +16,15 @@ module RDF; class Query
|
|
16
16
|
# solutions.filter { |solution| solution.age.datatype == RDF::XSD.integer }
|
17
17
|
# solutions.filter { |solution| solution.name.language == :es }
|
18
18
|
#
|
19
|
-
# @example Reordering solutions based on a variable
|
19
|
+
# @example Reordering solutions based on a variable or proc
|
20
20
|
# solutions.order_by(:updated)
|
21
21
|
# solutions.order_by(:updated, :created)
|
22
|
+
# solutions.order_by(:updated, lambda {|a, b| b <=> a})
|
22
23
|
#
|
23
|
-
# @example Selecting particular variables only
|
24
|
+
# @example Selecting/Projecting particular variables only
|
24
25
|
# solutions.select(:title)
|
25
26
|
# solutions.select(:title, :description)
|
27
|
+
# solutions.project(:title)
|
26
28
|
#
|
27
29
|
# @example Eliminating duplicate solutions
|
28
30
|
# solutions.distinct
|
@@ -58,6 +60,22 @@ module RDF; class Query
|
|
58
60
|
super
|
59
61
|
end
|
60
62
|
|
63
|
+
##
|
64
|
+
# Returns hash of bindings from each solution. Each bound variable will have
|
65
|
+
# an array of bound values representing those from each solution, where a given
|
66
|
+
# solution will have just a single value for each bound variable
|
67
|
+
# @return [Hash{Symbol => Array<RDF::Term>}]
|
68
|
+
def bindings
|
69
|
+
bindings = {}
|
70
|
+
each do |solution|
|
71
|
+
solution.each do |key, value|
|
72
|
+
bindings[key] ||= []
|
73
|
+
bindings[key] << value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
bindings
|
77
|
+
end
|
78
|
+
|
61
79
|
##
|
62
80
|
# Filters this solution sequence by the given `criteria`.
|
63
81
|
#
|
@@ -87,18 +105,35 @@ module RDF; class Query
|
|
87
105
|
##
|
88
106
|
# Reorders this solution sequence by the given `variables`.
|
89
107
|
#
|
90
|
-
#
|
108
|
+
# Variables may be symbols or {Query::Variable} instances.
|
109
|
+
# A variable may also be a Procedure/Lambda, compatible with {::Enumerable#sort}.
|
110
|
+
# This takes two arguments (solutions) and returns -1, 0, or 1 equivalently to <=>.
|
111
|
+
#
|
112
|
+
# If called with a block, variables are ignored, and the block is invoked with
|
113
|
+
# pairs of solutions. The block is expected to return -1, 0, or 1 equivalently to <=>.
|
114
|
+
#
|
115
|
+
# @param [Array<Proc, Query::Variable, Symbol, #to_sym>] variables
|
116
|
+
# @yield [solution]
|
117
|
+
# @yieldparam [RDF::Query::Solution] q
|
118
|
+
# @yieldparam [RDF::Query::Solution] b
|
119
|
+
# @yieldreturn [Integer] -1, 0, or 1 depending on value of comparator
|
91
120
|
# @return [void] `self`
|
92
|
-
def order(*variables)
|
93
|
-
if variables.empty?
|
121
|
+
def order(*variables, &block)
|
122
|
+
if variables.empty? && !block_given?
|
94
123
|
raise ArgumentError, "wrong number of arguments (0 for 1)"
|
95
124
|
else
|
96
|
-
# TODO: support for descending sort, e.g. `order(:s => :asc, :p => :desc)`
|
97
|
-
variables.map!(&:to_sym)
|
98
125
|
self.sort! do |a, b|
|
99
|
-
|
100
|
-
|
101
|
-
|
126
|
+
if block_given?
|
127
|
+
block.call((a.is_a?(Solution) ? a : Solution.new(a)), (b.is_a?(Solution) ? b : Solution.new(b)))
|
128
|
+
else
|
129
|
+
# Try each variable until a difference is found.
|
130
|
+
variables.inject(nil) do |memo, v|
|
131
|
+
memo || begin
|
132
|
+
comp = v.is_a?(Proc) ? v.call(a, b) : (v = v.to_sym; a[v] <=> b[v])
|
133
|
+
comp == 0 ? false : comp
|
134
|
+
end
|
135
|
+
end || 0
|
136
|
+
end
|
102
137
|
end
|
103
138
|
end
|
104
139
|
self
|
data/lib/rdf/query/variable.rb
CHANGED
@@ -102,6 +102,23 @@ class RDF::Query
|
|
102
102
|
value.nil?
|
103
103
|
end
|
104
104
|
|
105
|
+
##
|
106
|
+
# Returns `true` if this variable is distinguished.
|
107
|
+
#
|
108
|
+
# @return [Boolean]
|
109
|
+
def distinguished?
|
110
|
+
@distinguished.nil? || @distinguished
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Sets if variable is distinguished or non-distinguished.
|
115
|
+
# By default, variables are distinguished
|
116
|
+
#
|
117
|
+
# @return [Boolean]
|
118
|
+
def distinguished=(value)
|
119
|
+
@distinguished = value
|
120
|
+
end
|
121
|
+
|
105
122
|
##
|
106
123
|
# Rebinds this variable to the given `value`.
|
107
124
|
#
|
@@ -153,14 +170,21 @@ class RDF::Query
|
|
153
170
|
|
154
171
|
##
|
155
172
|
# Returns `true` if this variable is equivalent to a given `other`
|
156
|
-
# variable.
|
173
|
+
# variable. Or, to another Term if bound, or to any other Term
|
157
174
|
#
|
158
175
|
# @param [Object] other
|
159
176
|
# @return [Boolean] `true` or `false`
|
160
177
|
# @since 0.3.0
|
161
178
|
def eql?(other)
|
162
|
-
|
179
|
+
if unbound?
|
180
|
+
other.is_a?(RDF::Term) # match any Term when unbound
|
181
|
+
elsif other.is_a?(RDF::Query::Variable)
|
182
|
+
@name.eql?(other.name)
|
183
|
+
else
|
184
|
+
value.eql?(other)
|
185
|
+
end
|
163
186
|
end
|
187
|
+
alias_method :==, :eql?
|
164
188
|
|
165
189
|
##
|
166
190
|
# Compares this variable with the given value.
|
@@ -169,7 +193,7 @@ class RDF::Query
|
|
169
193
|
# @return [Boolean]
|
170
194
|
def ===(other)
|
171
195
|
if unbound?
|
172
|
-
|
196
|
+
other.is_a?(RDF::Term) # match any Term when unbound
|
173
197
|
else
|
174
198
|
value === other
|
175
199
|
end
|
@@ -178,9 +202,20 @@ class RDF::Query
|
|
178
202
|
##
|
179
203
|
# Returns a string representation of this variable.
|
180
204
|
#
|
205
|
+
# Distinguished variables are indicated with a single `?`.
|
206
|
+
#
|
207
|
+
# Non-distinguished variables are indicated with a double `??`
|
208
|
+
#
|
209
|
+
# @example
|
210
|
+
# v = Variable.new("a")
|
211
|
+
# v.to_s => '?a'
|
212
|
+
# v.distinguished = false
|
213
|
+
# v.to_s => '??a'
|
214
|
+
#
|
181
215
|
# @return [String]
|
182
216
|
def to_s
|
183
|
-
|
217
|
+
prefix = distinguished? ? '?' : "??"
|
218
|
+
unbound? ? "#{prefix}#{name}" : "#{prefix}#{name}=#{value}"
|
184
219
|
end
|
185
220
|
end # Variable
|
186
221
|
end # RDF::Query
|
data/lib/rdf/reader.rb
CHANGED
@@ -55,6 +55,8 @@ module RDF
|
|
55
55
|
##
|
56
56
|
# Finds an RDF reader class based on the given criteria.
|
57
57
|
#
|
58
|
+
# If the reader class has a defined format, use that.
|
59
|
+
#
|
58
60
|
# @overload for(format)
|
59
61
|
# Finds an RDF reader class based on a symbolic name.
|
60
62
|
#
|
@@ -75,16 +77,23 @@ module RDF
|
|
75
77
|
# @option options [Symbol, #to_sym] :file_extension (nil)
|
76
78
|
# @option options [String, #to_s] :content_type (nil)
|
77
79
|
# @return [Class]
|
80
|
+
# @option options [String] :sample (nil)
|
81
|
+
# A sample of input used for performing format detection.
|
82
|
+
# If we find no formats, or we find more than one, and we have a sample, we can
|
83
|
+
# perform format detection to find a specific format to use, in which case
|
84
|
+
# we pick the first one we find
|
85
|
+
# @return [Class]
|
86
|
+
# @yieldreturn [String] another way to provide a sample, allows lazy for retrieving the sample.
|
78
87
|
#
|
79
88
|
# @return [Class]
|
80
|
-
def self.for(options = {})
|
81
|
-
if format = Format.for(options)
|
89
|
+
def self.for(options = {}, &block)
|
90
|
+
if format = self.format || Format.for(options, &block)
|
82
91
|
format.reader
|
83
92
|
end
|
84
93
|
end
|
85
94
|
|
86
95
|
##
|
87
|
-
# Retrieves the RDF serialization format class for this
|
96
|
+
# Retrieves the RDF serialization format class for this reader class.
|
88
97
|
#
|
89
98
|
# @return [Class]
|
90
99
|
def self.format(klass = nil)
|
@@ -118,7 +127,12 @@ module RDF
|
|
118
127
|
format_options = options.dup
|
119
128
|
format_options[:content_type] ||= file.content_type if file.respond_to?(:content_type)
|
120
129
|
format_options[:file_name] ||= filename
|
121
|
-
reader = self.for(format_options[:format] || format_options)
|
130
|
+
reader = self.for(format_options[:format] || format_options) do
|
131
|
+
# Return a sample from the input file
|
132
|
+
sample = file.read(1000)
|
133
|
+
file.rewind
|
134
|
+
sample
|
135
|
+
end
|
122
136
|
if reader
|
123
137
|
reader.new(file, options, &block)
|
124
138
|
else
|
@@ -127,6 +141,23 @@ module RDF
|
|
127
141
|
end
|
128
142
|
end
|
129
143
|
|
144
|
+
##
|
145
|
+
# Returns a symbol appropriate to use with RDF::Reader.for()
|
146
|
+
# @return [Symbol]
|
147
|
+
def self.to_sym
|
148
|
+
elements = self.to_s.split("::")
|
149
|
+
sym = elements.pop
|
150
|
+
sym = elements.pop if sym == 'Reader'
|
151
|
+
sym.downcase.to_s.to_sym
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# Returns a symbol appropriate to use with RDF::Reader.for()
|
156
|
+
# @return [Symbol]
|
157
|
+
def to_sym
|
158
|
+
self.class.to_sym
|
159
|
+
end
|
160
|
+
|
130
161
|
##
|
131
162
|
# Initializes the reader.
|
132
163
|
#
|
@@ -177,6 +208,18 @@ module RDF
|
|
177
208
|
# @since 0.3.0
|
178
209
|
attr_reader :options
|
179
210
|
|
211
|
+
##
|
212
|
+
# Returns the base URI determined by this reader.
|
213
|
+
#
|
214
|
+
# @example
|
215
|
+
# reader.prefixes[:dc] #=> RDF::URI('http://purl.org/dc/terms/')
|
216
|
+
#
|
217
|
+
# @return [Hash{Symbol => RDF::URI}]
|
218
|
+
# @since 0.3.0
|
219
|
+
def base_uri
|
220
|
+
@options[:base_uri]
|
221
|
+
end
|
222
|
+
|
180
223
|
##
|
181
224
|
# Returns the URI prefixes currently defined for this reader.
|
182
225
|
#
|
data/lib/rdf/repository.rb
CHANGED
@@ -70,6 +70,8 @@ module RDF
|
|
70
70
|
# Loads one or more RDF files into a new transient in-memory repository.
|
71
71
|
#
|
72
72
|
# @param [String, Array<String>] filenames
|
73
|
+
# @param [Hash{Symbol => Object}] options
|
74
|
+
# Options from {RDF::Reader#initialize}, {RDF::Format.for} and {RDF::Repository#initialize}
|
73
75
|
# @yield [repository]
|
74
76
|
# @yieldparam [Repository]
|
75
77
|
# @return [void]
|
@@ -324,6 +326,8 @@ module RDF
|
|
324
326
|
protected
|
325
327
|
|
326
328
|
##
|
329
|
+
# Match elements with eql?, not ==
|
330
|
+
# Context of `false` matches default context. Unbound variable matches non-false context
|
327
331
|
# @private
|
328
332
|
# @see RDF::Queryable#query
|
329
333
|
def query_pattern(pattern, &block)
|
@@ -334,16 +338,16 @@ module RDF
|
|
334
338
|
|
335
339
|
cs = @data.has_key?(context) ? {context => @data[context]} : @data.dup
|
336
340
|
cs.each do |c, ss|
|
337
|
-
next unless context.nil? || context == c
|
341
|
+
next unless context.nil? || context == false && !c || context.eql?(c)
|
338
342
|
ss = ss.has_key?(subject) ? {subject => ss[subject]} : ss.dup
|
339
343
|
ss.each do |s, ps|
|
340
|
-
next unless subject.nil? || subject
|
344
|
+
next unless subject.nil? || subject.eql?(s)
|
341
345
|
ps = ps.has_key?(predicate) ? {predicate => ps[predicate]} : ps.dup
|
342
346
|
ps.each do |p, os|
|
343
|
-
next unless predicate.nil? || predicate
|
347
|
+
next unless predicate.nil? || predicate.eql?(p)
|
344
348
|
os = os.dup # TODO: is this really needed?
|
345
349
|
os.each do |o|
|
346
|
-
next unless object.nil? || object
|
350
|
+
next unless object.nil? || object.eql?(o)
|
347
351
|
block.call(RDF::Statement.new(s, p, o, :context => c.equal?(DEFAULT_CONTEXT) ? nil : c))
|
348
352
|
end
|
349
353
|
end
|