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