active-triples 0.10.2 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGES.md +17 -11
- data/README.md +72 -39
- data/lib/active_triples/configurable.rb +6 -1
- data/lib/active_triples/list.rb +1 -4
- data/lib/active_triples/nested_attributes.rb +10 -7
- data/lib/active_triples/persistable.rb +13 -0
- data/lib/active_triples/persistence_strategies/parent_strategy.rb +47 -34
- data/lib/active_triples/persistence_strategies/persistence_strategy.rb +14 -1
- data/lib/active_triples/properties.rb +19 -4
- data/lib/active_triples/property_builder.rb +4 -4
- data/lib/active_triples/rdf_source.rb +142 -189
- data/lib/active_triples/relation.rb +307 -156
- data/lib/active_triples/util/buffered_transaction.rb +126 -0
- data/lib/active_triples/util/extended_bounded_description.rb +75 -0
- data/lib/active_triples/version.rb +1 -1
- data/spec/active_triples/configurable_spec.rb +35 -7
- data/spec/active_triples/identifiable_spec.rb +19 -6
- data/spec/active_triples/list_spec.rb +15 -7
- data/spec/active_triples/nested_attributes_spec.rb +12 -10
- data/spec/active_triples/persistable_spec.rb +0 -4
- data/spec/active_triples/persistence_strategies/parent_strategy_spec.rb +57 -10
- data/spec/active_triples/rdf_source_spec.rb +137 -97
- data/spec/active_triples/relation_spec.rb +436 -132
- data/spec/active_triples/resource_spec.rb +8 -23
- data/spec/active_triples/util/buffered_transaction_spec.rb +187 -0
- data/spec/active_triples/util/extended_bounded_description_spec.rb +98 -0
- data/spec/integration/reciprocal_properties_spec.rb +10 -10
- data/spec/support/matchers.rb +13 -1
- metadata +7 -3
@@ -21,6 +21,9 @@ module ActiveTriples
|
|
21
21
|
# @see RDF::Term
|
22
22
|
class Relation
|
23
23
|
include Enumerable
|
24
|
+
include Comparable
|
25
|
+
|
26
|
+
TYPE_PROPERTY = { predicate: RDF.type, cast: false }.freeze
|
24
27
|
|
25
28
|
# @!attribute [rw] parent
|
26
29
|
# @return [RDFSource] the resource that is the domain of this relation
|
@@ -33,8 +36,7 @@ module ActiveTriples
|
|
33
36
|
attr_accessor :parent, :value_arguments, :rel_args
|
34
37
|
attr_reader :reflections
|
35
38
|
|
36
|
-
delegate
|
37
|
-
:to_a, :to_ary, :size, :join, :length, :+, :to => :result
|
39
|
+
delegate :[], :inspect, :last, :size, :join, to: :to_a
|
38
40
|
|
39
41
|
##
|
40
42
|
# @param [ActiveTriples::RDFSource] parent_source
|
@@ -44,95 +46,101 @@ module ActiveTriples
|
|
44
46
|
self.parent = parent_source
|
45
47
|
@reflections = parent_source.reflections
|
46
48
|
self.rel_args ||= {}
|
47
|
-
self.rel_args = value_arguments.pop if
|
48
|
-
|
49
|
+
self.rel_args = value_arguments.pop if
|
50
|
+
value_arguments.is_a?(Array) && value_arguments.last.is_a?(Hash)
|
51
|
+
|
49
52
|
self.value_arguments = value_arguments
|
50
53
|
end
|
51
54
|
|
52
55
|
##
|
53
|
-
#
|
56
|
+
# @param array [#to_ary, ActiveTriples::Relation]
|
57
|
+
# @return [Array]
|
54
58
|
#
|
55
|
-
# @
|
56
|
-
|
57
|
-
|
59
|
+
# @note simply passes to `Array#&` unless argument is a `Relation`
|
60
|
+
#
|
61
|
+
# @see Array#&
|
62
|
+
def &(array)
|
63
|
+
return to_a & array unless array.is_a? Relation
|
58
64
|
|
59
|
-
|
65
|
+
(objects.to_a & array.objects.to_a)
|
66
|
+
.map { |object| convert_object(object) }
|
60
67
|
end
|
61
|
-
|
68
|
+
|
62
69
|
##
|
63
|
-
#
|
70
|
+
# @param array [#to_ary, ActiveTriples::Relation]
|
71
|
+
# @return [Array]
|
64
72
|
#
|
65
|
-
#
|
66
|
-
# {Literal} results are given as their `#object` representations (e.g.
|
67
|
-
# {String}, {Date}.
|
73
|
+
# @note simply passes to `Array#|` unless argument is a `Relation`
|
68
74
|
#
|
69
|
-
# @
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
#
|
75
|
+
# @see Array#|
|
76
|
+
def |(array)
|
77
|
+
return to_a | array unless array.is_a? Relation
|
78
|
+
|
79
|
+
(objects.to_a | array.objects.to_a)
|
80
|
+
.map { |object| convert_object(object) }
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# @param array [#to_ary, ActiveTriples::Relation]
|
85
|
+
# @return [Array]
|
79
86
|
#
|
80
|
-
#
|
81
|
-
# form. Similarly, when `#return_literals?` is `true`, literals are
|
82
|
-
# returned in their {RDF::Literal} form, preserving language tags,
|
83
|
-
# datatype, and value.
|
87
|
+
# @note simply passes to `Array#+` unless argument is a `Relation`
|
84
88
|
#
|
85
|
-
# @
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
# @see Array#+
|
90
|
+
def +(array)
|
91
|
+
return to_a + array unless array.is_a? Relation
|
92
|
+
|
93
|
+
(objects.to_a + array.objects.to_a)
|
94
|
+
.map { |object| convert_object(object) }
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Mimics `Set#<=>`, returning `0` when set membership is equivalent, and
|
99
|
+
# `nil` (as non-comparable) otherwise. Unlike `Set#<=>`, uses `#==` for
|
100
|
+
# member comparisons.
|
91
101
|
#
|
92
|
-
# @
|
93
|
-
# relation.result
|
94
|
-
# # => [#<RDF::Literal:0x3f8...("my_value")>,
|
95
|
-
# # #<RDF::Literal::Date:0x3f8...("2015-09-25"^^<http://www.w3.org/2001/XMLSchema#date>)>,
|
96
|
-
# # #<ActiveTriples::Resource:0x3f8...>,
|
97
|
-
# # #<ActiveTriples::Resource:0x3f8...>]
|
102
|
+
# @param [Object] other
|
98
103
|
#
|
99
|
-
# @
|
100
|
-
def
|
101
|
-
return
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
# @see Set#<=>
|
105
|
+
def <=>(other)
|
106
|
+
return nil unless other.respond_to?(:each)
|
107
|
+
|
108
|
+
if empty?
|
109
|
+
return 0 if other.each.first.nil?
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
# We'll need to traverse `other` repeatedly, so we get a stable `Array`
|
114
|
+
# representation. This avoids any repeated query cost if `other` is a
|
115
|
+
# `Relation`.
|
116
|
+
length = 0
|
117
|
+
other = other.to_a
|
118
|
+
this = each
|
119
|
+
|
120
|
+
loop do
|
121
|
+
begin
|
122
|
+
cur = this.next
|
123
|
+
rescue StopIteration
|
124
|
+
return other.length == length ? 0 : nil
|
125
|
+
end
|
126
|
+
|
127
|
+
length += 1
|
128
|
+
|
129
|
+
return nil if other.length < length || !other.include?(cur)
|
107
130
|
end
|
108
131
|
end
|
109
132
|
|
110
133
|
##
|
111
|
-
# Adds values to the
|
134
|
+
# Adds values to the result set
|
112
135
|
#
|
113
|
-
# @param [Array<
|
114
|
-
# or a single value. If not an {RDF::Resource}, the values will be
|
115
|
-
# coerced to an {RDF::Literal} or {RDF::Node} by {RDF::Statement}
|
136
|
+
# @param values [Object, Array<Object>] values to add
|
116
137
|
#
|
117
138
|
# @return [Relation] a relation containing the set values; i.e. `self`
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
#
|
122
|
-
# @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Statement For
|
123
|
-
# documentation on {RDF::Statement} and the handling of
|
124
|
-
# non-{RDF::Resource} values.
|
125
|
-
def set(values)
|
126
|
-
raise UndefinedPropertyError.new(property, reflections) if predicate.nil?
|
127
|
-
values = values.to_a if values.is_a? Relation
|
128
|
-
values = [values].compact unless values.kind_of?(Array)
|
129
|
-
|
130
|
-
clear
|
131
|
-
values.each { |val| set_value(val) }
|
132
|
-
|
133
|
-
parent.persist! if parent.persistence_strategy.is_a? ParentStrategy
|
134
|
-
self
|
139
|
+
def <<(values)
|
140
|
+
values = prepare_relation(values) if values.is_a?(Relation)
|
141
|
+
self.set(objects.to_a | Array.wrap(values))
|
135
142
|
end
|
143
|
+
alias_method :push, :<<
|
136
144
|
|
137
145
|
##
|
138
146
|
# Builds a node with the given attributes, adding it to the relation.
|
@@ -195,17 +203,27 @@ module ActiveTriples
|
|
195
203
|
end
|
196
204
|
|
197
205
|
##
|
198
|
-
#
|
206
|
+
# Empties the `Relation`, deleting any associated triples from `parent`.
|
207
|
+
#
|
208
|
+
# @return [Relation] self; a now empty relation
|
209
|
+
def clear
|
210
|
+
parent.delete([rdf_subject, predicate, nil])
|
211
|
+
|
212
|
+
self
|
213
|
+
end
|
214
|
+
|
215
|
+
##
|
216
|
+
# @note this method behaves somewhat differently from `Array#delete`.
|
199
217
|
# It succeeds on deletion of non-existing values, always returning
|
200
|
-
# `self` unless an error is raised. There is no option to pass a block
|
201
|
-
# to evaluate if the value is not present. This is because access for
|
218
|
+
# `self` unless an error is raised. There is no option to pass a block
|
219
|
+
# to evaluate if the value is not present. This is because access for
|
202
220
|
# `value` depends on query time. i.e. the `Relation` set does not have an
|
203
221
|
# underlying efficient data structure allowing a reliably cheap existence
|
204
222
|
# check.
|
205
223
|
#
|
206
|
-
# @note symbols are treated as RDF::Nodes by default in
|
224
|
+
# @note symbols are treated as RDF::Nodes by default in
|
207
225
|
# `RDF::Mutable#delete`, but may also represent tokens in statements.
|
208
|
-
# This casts symbols to a literals, which gets us symmetric behavior
|
226
|
+
# This casts symbols to a literals, which gets us symmetric behavior
|
209
227
|
# between `#set(:sym)` and `#delete(:sym)`.
|
210
228
|
#
|
211
229
|
# @example deleting a value
|
@@ -219,7 +237,7 @@ module ActiveTriples
|
|
219
237
|
# resource.title = 'moomin'
|
220
238
|
# resource.title.delete('valley') # => ["moomin"]
|
221
239
|
# resource.title # => ['moomin']
|
222
|
-
#
|
240
|
+
#
|
223
241
|
# @param value [Object] the value to delete from the relation
|
224
242
|
# @return [ActiveTriples::Relation] self
|
225
243
|
def delete(value)
|
@@ -230,12 +248,12 @@ module ActiveTriples
|
|
230
248
|
end
|
231
249
|
|
232
250
|
##
|
233
|
-
# A variation on `#delete`. This queries the relation for matching
|
251
|
+
# A variation on `#delete`. This queries the relation for matching
|
234
252
|
# values before running the deletion, returning `nil` if it does not exist.
|
235
253
|
#
|
236
254
|
# @param value [Object] the value to delete from the relation
|
237
255
|
#
|
238
|
-
# @return [Object, nil] `nil` if the value doesn't exist; the value
|
256
|
+
# @return [Object, nil] `nil` if the value doesn't exist; the value
|
239
257
|
# otherwise
|
240
258
|
# @see #delete
|
241
259
|
def delete?(value)
|
@@ -248,71 +266,94 @@ module ActiveTriples
|
|
248
266
|
end
|
249
267
|
|
250
268
|
##
|
251
|
-
#
|
252
|
-
# Deletes objects in the enumerable from the relation
|
253
|
-
# @param values [Enumerable] an enumerable of objects to delete
|
254
|
-
# @overload subtract(*values)
|
255
|
-
# Deletes each argument value from the relation
|
256
|
-
# @param *values [Array<Object>] the objects to delete
|
269
|
+
# Gives a result set for the `Relation`.
|
257
270
|
#
|
258
|
-
#
|
271
|
+
# By default, `RDF::URI` and `RDF::Node` results are cast to `RDFSource`.
|
272
|
+
# When `cast?` is `false`, `RDF::Resource` values are left in their raw
|
273
|
+
# form.
|
259
274
|
#
|
260
|
-
#
|
261
|
-
#
|
262
|
-
#
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
275
|
+
# `Literal` results are cast as follows:
|
276
|
+
#
|
277
|
+
# - Simple string literals are returned as `String`
|
278
|
+
# - `rdf:langString` literals are always returned as raw `Literal` objects,
|
279
|
+
# retaining their language tags.
|
280
|
+
# - Typed literals are cast to their Ruby `#object` when their datatype
|
281
|
+
# is associated with a `Literal` subclass.
|
282
|
+
#
|
283
|
+
# @example results with default casting
|
284
|
+
# datatype = RDF::URI("http://example.com/custom_type")
|
285
|
+
#
|
286
|
+
# parent << [parent.rdf_subject, predicate, 'my value']
|
287
|
+
# parent << [parent.rdf_subject, predicate, RDF::Literal('my_value',
|
288
|
+
# datatype: datatype)]
|
289
|
+
# parent << [parent.rdf_subject, predicate, Date.today]
|
290
|
+
# parent << [parent.rdf_subject, predicate, RDF::URI('http://ex.org/#me')]
|
291
|
+
# parent << [parent.rdf_subject, predicate, RDF::Node.new]
|
292
|
+
#
|
293
|
+
# relation.to_a
|
294
|
+
# # => ["my_value",
|
295
|
+
# # "my_value" R:L:(Literal),
|
296
|
+
# # Fri, 25 Sep 2015,
|
297
|
+
# # #<ActiveTriples::Resource:0x3f8...>,
|
298
|
+
# # #<ActiveTriples::Resource:0x3f8...>]
|
299
|
+
#
|
300
|
+
# @example results with `cast?` set to `false`
|
301
|
+
# relation.to_a
|
302
|
+
# # => ["my_value",
|
303
|
+
# # "my_value" R:L:(Literal),
|
304
|
+
# # Fri, 25 Sep 2015,
|
305
|
+
# # #<RDF::URI:0x3f8... URI:http://ex.org/#me>,
|
306
|
+
# # #<RDF::Node:0x3f8...(_:g69843536054680)>]
|
307
|
+
#
|
308
|
+
# @return [Enumerator<Object>] the result set
|
309
|
+
def each
|
310
|
+
return [].to_enum if predicate.nil?
|
311
|
+
|
312
|
+
if block_given?
|
313
|
+
objects do |object|
|
314
|
+
converted_object = convert_object(object)
|
315
|
+
yield converted_object unless converted_object.nil?
|
316
|
+
end
|
268
317
|
end
|
269
|
-
|
270
|
-
|
271
|
-
self
|
318
|
+
|
319
|
+
to_enum
|
272
320
|
end
|
273
321
|
|
274
322
|
##
|
275
|
-
#
|
276
|
-
|
277
|
-
|
278
|
-
# @example
|
279
|
-
#
|
280
|
-
#
|
281
|
-
# @param swap_out [Object] the value to delete
|
282
|
-
# @param swap_in [Object] the replacement value
|
283
|
-
#
|
284
|
-
# @return [Relation] self
|
285
|
-
def swap(swap_out, swap_in)
|
286
|
-
self.<<(swap_in) if delete?(swap_out)
|
323
|
+
# @return [Boolean] true if the results are empty.
|
324
|
+
def empty?
|
325
|
+
objects.empty?
|
287
326
|
end
|
288
327
|
|
289
328
|
##
|
329
|
+
# @deprecated for removal in 1.0.0. Use `first || build({})`,
|
330
|
+
# `build({}) if empty?` or similar logic.
|
331
|
+
#
|
290
332
|
# @return [Object] the first result, if present; else a newly built node
|
291
333
|
#
|
292
334
|
# @see #build
|
293
335
|
def first_or_create(attributes={})
|
294
|
-
|
336
|
+
warn 'DEPRECATION: #first_or_create is deprecated for removal in 1.0.0.'
|
337
|
+
first || build(attributes)
|
295
338
|
end
|
296
339
|
|
297
340
|
##
|
298
|
-
#
|
299
|
-
|
300
|
-
|
301
|
-
#
|
302
|
-
# @return [Relation] a relation containing the set values; i.e. `self`
|
303
|
-
def <<(values)
|
304
|
-
values = Array.wrap(result) | Array.wrap(values)
|
305
|
-
self.set(values)
|
341
|
+
# @return [Integer]
|
342
|
+
def length
|
343
|
+
objects.to_a.length
|
306
344
|
end
|
307
|
-
alias_method :push, :<<
|
308
345
|
|
309
346
|
##
|
310
|
-
#
|
311
|
-
#
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
347
|
+
# Gives the predicate used by the Relation. Values of this object are
|
348
|
+
# those that match the pattern `<rdf_subject> <predicate> [value] .`
|
349
|
+
#
|
350
|
+
# @return [RDF::Term, nil] the predicate for this relation; nil if
|
351
|
+
# no predicate can be found
|
352
|
+
#
|
353
|
+
# @see #property
|
354
|
+
def predicate
|
355
|
+
return property if property.is_a?(RDF::Term)
|
356
|
+
property_config[:predicate] if is_property?
|
316
357
|
end
|
317
358
|
|
318
359
|
##
|
@@ -326,33 +367,136 @@ module ActiveTriples
|
|
326
367
|
end
|
327
368
|
|
328
369
|
##
|
329
|
-
#
|
330
|
-
# those that match the pattern `<rdf_subject> <predicate> [value] .`
|
370
|
+
# Adds values to the relation
|
331
371
|
#
|
332
|
-
# @
|
333
|
-
#
|
372
|
+
# @param [Array<RDF::Resource>, RDF::Resource] values an array of values
|
373
|
+
# or a single value. If not an {RDF::Resource}, the values will be
|
374
|
+
# coerced to an {RDF::Literal} or {RDF::Node} by {RDF::Statement}
|
334
375
|
#
|
335
|
-
# @
|
336
|
-
|
337
|
-
|
338
|
-
|
376
|
+
# @return [Relation] a relation containing the set values; i.e. `self`
|
377
|
+
#
|
378
|
+
# @raise [ActiveTriples::UndefinedPropertyError] if the property is not
|
379
|
+
# already an {RDF::Term} and is not defined in `#property_config`
|
380
|
+
#
|
381
|
+
# @see http://www.rubydoc.info/github/ruby-rdf/rdf/RDF/Statement For
|
382
|
+
# documentation on {RDF::Statement} and the handling of
|
383
|
+
# non-{RDF::Resource} values.
|
384
|
+
def set(values)
|
385
|
+
raise UndefinedPropertyError.new(property, reflections) if predicate.nil?
|
386
|
+
|
387
|
+
values = prepare_relation(values) if values.is_a?(Relation)
|
388
|
+
values = [values].compact unless values.kind_of?(Array)
|
389
|
+
|
390
|
+
clear
|
391
|
+
values.each { |val| set_value(val) }
|
392
|
+
|
393
|
+
parent.persist! if parent.persistence_strategy.is_a? ParentStrategy
|
394
|
+
self
|
395
|
+
end
|
396
|
+
|
397
|
+
##
|
398
|
+
# @overload subtract(enum)
|
399
|
+
# Deletes objects in the enumerable from the relation
|
400
|
+
# @param values [Enumerable] an enumerable of objects to delete
|
401
|
+
# @overload subtract(*values)
|
402
|
+
# Deletes each argument value from the relation
|
403
|
+
# @param *values [Array<Object>] the objects to delete
|
404
|
+
#
|
405
|
+
# @return [Relation] self
|
406
|
+
#
|
407
|
+
# @note This casts symbols to a literals, which gets us symmetric behavior
|
408
|
+
# with `#set(:sym)`.
|
409
|
+
# @see #delete
|
410
|
+
def subtract(*values)
|
411
|
+
values = values.first if values.first.is_a? Enumerable
|
412
|
+
statements = values.map do |value|
|
413
|
+
value = RDF::Literal(value) if value.is_a? Symbol
|
414
|
+
[rdf_subject, predicate, value]
|
415
|
+
end
|
416
|
+
|
417
|
+
parent.delete(*statements)
|
418
|
+
self
|
419
|
+
end
|
420
|
+
|
421
|
+
##
|
422
|
+
# Replaces the first argument with the second as a value within the
|
423
|
+
# relation.
|
424
|
+
#
|
425
|
+
# @param swap_out [Object] the value to delete
|
426
|
+
# @param swap_in [Object] the replacement value
|
427
|
+
#
|
428
|
+
# @return [Relation] self
|
429
|
+
def swap(swap_out, swap_in)
|
430
|
+
self.<<(swap_in) if delete?(swap_out)
|
339
431
|
end
|
340
432
|
|
341
433
|
protected
|
342
434
|
|
435
|
+
##
|
436
|
+
# Converts an object to the appropriate class.
|
437
|
+
#
|
438
|
+
# Literals are cast only when the datatype is known.
|
439
|
+
#
|
440
|
+
# @private
|
441
|
+
def convert_object(value)
|
442
|
+
case value
|
443
|
+
when RDFSource
|
444
|
+
value
|
445
|
+
when RDF::Literal
|
446
|
+
if value.simple?
|
447
|
+
value.object
|
448
|
+
elsif value.has_datatype?
|
449
|
+
RDF::Literal.datatyped_class(value.datatype.to_s) ? value.object : value
|
450
|
+
else
|
451
|
+
value
|
452
|
+
end
|
453
|
+
when RDF::Resource
|
454
|
+
make_node(value)
|
455
|
+
else
|
456
|
+
value
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
##
|
461
|
+
# @private
|
343
462
|
def node_cache
|
344
463
|
@node_cache ||= {}
|
345
464
|
end
|
346
465
|
|
466
|
+
##
|
467
|
+
# @private
|
468
|
+
def objects(&block)
|
469
|
+
solutions = parent.query(subject: rdf_subject, predicate: predicate)
|
470
|
+
solutions.extend(RDF::Enumerable) unless solutions.respond_to?(:each_object)
|
471
|
+
|
472
|
+
solutions.each_object(&block)
|
473
|
+
end
|
474
|
+
|
475
|
+
private
|
476
|
+
##
|
477
|
+
# @private
|
347
478
|
def is_property?
|
348
479
|
reflections.has_property?(property) || is_type?
|
349
480
|
end
|
350
481
|
|
482
|
+
##
|
483
|
+
# @private
|
351
484
|
def is_type?
|
352
485
|
(property == RDF.type || property.to_s == "type") &&
|
353
486
|
(!reflections.kind_of?(RDFSource) || !is_property?)
|
354
487
|
end
|
355
488
|
|
489
|
+
##
|
490
|
+
# @private
|
491
|
+
# @return [Hash<Symbol, ]
|
492
|
+
def property_config
|
493
|
+
return TYPE_PROPERTY if is_type?
|
494
|
+
|
495
|
+
reflections.reflect_on_property(property)
|
496
|
+
end
|
497
|
+
|
498
|
+
##
|
499
|
+
# @private
|
356
500
|
def set_value(val)
|
357
501
|
resource = value_to_node(val.respond_to?(:resource) ? val.resource : val)
|
358
502
|
if resource.kind_of? RDFSource
|
@@ -365,14 +509,24 @@ module ActiveTriples
|
|
365
509
|
parent.insert [rdf_subject, predicate, resource]
|
366
510
|
end
|
367
511
|
|
368
|
-
|
369
|
-
|
370
|
-
end
|
371
|
-
|
512
|
+
##
|
513
|
+
# @private
|
372
514
|
def value_to_node(val)
|
373
515
|
valid_datatype?(val) ? RDF::Literal(val) : val
|
374
516
|
end
|
375
517
|
|
518
|
+
def prepare_relation(values)
|
519
|
+
values.objects.map do |value|
|
520
|
+
if value.respond_to?(:resource?) && value.resource?
|
521
|
+
values.convert_object(value)
|
522
|
+
else
|
523
|
+
value
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
##
|
529
|
+
# @private
|
376
530
|
def add_child_node(object, resource)
|
377
531
|
parent.insert [rdf_subject, predicate, resource.rdf_subject]
|
378
532
|
resource = resource.respond_to?(:resource) ? resource.resource : resource
|
@@ -384,12 +538,14 @@ module ActiveTriples
|
|
384
538
|
parent.persistence_strategy.ancestors.find { |a| a == new_resource })
|
385
539
|
new_resource.set_persistence_strategy(ParentStrategy)
|
386
540
|
new_resource.parent = parent
|
541
|
+
new_resource.persist!
|
387
542
|
end
|
388
543
|
|
389
544
|
self.node_cache[resource.rdf_subject] = (resource == object ? new_resource : object)
|
390
|
-
new_resource.persist! if new_resource.persistence_strategy.is_a? ParentStrategy
|
391
545
|
end
|
392
546
|
|
547
|
+
##
|
548
|
+
# @private
|
393
549
|
def valid_datatype?(val)
|
394
550
|
case val
|
395
551
|
when String, Date, Time, Numeric, Symbol, TrueClass, FalseClass then true
|
@@ -397,48 +553,36 @@ module ActiveTriples
|
|
397
553
|
end
|
398
554
|
end
|
399
555
|
|
400
|
-
# Converts an object to the appropriate class.
|
401
|
-
def convert_object(value)
|
402
|
-
case value
|
403
|
-
when RDFSource
|
404
|
-
value
|
405
|
-
when RDF::Literal
|
406
|
-
return_literals? ? value : value.object
|
407
|
-
when RDF::Resource
|
408
|
-
make_node(value)
|
409
|
-
else
|
410
|
-
value
|
411
|
-
end
|
412
|
-
end
|
413
|
-
|
414
556
|
##
|
415
557
|
# Build a child resource or return it from this object's cache
|
416
558
|
#
|
417
559
|
# Builds the resource from the class_name specified for the
|
418
560
|
# property.
|
561
|
+
#
|
562
|
+
# @private
|
419
563
|
def make_node(value)
|
420
564
|
return value unless cast?
|
421
565
|
klass = class_for_value(value)
|
422
566
|
value = RDF::Node.new if value.nil?
|
423
567
|
node = node_cache[value] if node_cache[value]
|
424
568
|
node ||= klass.from_uri(value,parent)
|
425
|
-
node.set_persistence_strategy(property_config[:persist_to]) if
|
569
|
+
node.set_persistence_strategy(property_config[:persist_to]) if
|
426
570
|
is_property? && property_config[:persist_to]
|
427
571
|
return nil if (is_property? && property_config[:class_name]) && (class_for_value(value) != class_for_property)
|
428
572
|
self.node_cache[value] ||= node
|
429
573
|
node
|
430
574
|
end
|
431
575
|
|
576
|
+
##
|
577
|
+
# @private
|
432
578
|
def cast?
|
433
579
|
return true unless is_property? || (rel_args && rel_args[:cast])
|
434
580
|
return rel_args[:cast] if rel_args.has_key?(:cast)
|
435
581
|
!!property_config[:cast]
|
436
582
|
end
|
437
583
|
|
438
|
-
|
439
|
-
|
440
|
-
end
|
441
|
-
|
584
|
+
##
|
585
|
+
# @private
|
442
586
|
def final_parent
|
443
587
|
@final_parent ||= begin
|
444
588
|
parent = self.parent
|
@@ -450,16 +594,22 @@ module ActiveTriples
|
|
450
594
|
end
|
451
595
|
end
|
452
596
|
|
597
|
+
##
|
598
|
+
# @private
|
453
599
|
def class_for_value(v)
|
454
600
|
uri_class(v) || class_for_property
|
455
601
|
end
|
456
602
|
|
603
|
+
##
|
604
|
+
# @private
|
457
605
|
def uri_class(v)
|
458
|
-
v = RDF::URI.
|
606
|
+
v = RDF::URI.intern(v) if v.kind_of? String
|
459
607
|
type_uri = parent.query([v, RDF.type, nil]).to_a.first.try(:object)
|
460
608
|
Resource.type_registry[type_uri]
|
461
609
|
end
|
462
610
|
|
611
|
+
##
|
612
|
+
# @private
|
463
613
|
def class_for_property
|
464
614
|
klass = property_config[:class_name] if is_property?
|
465
615
|
klass ||= Resource
|
@@ -469,6 +619,7 @@ module ActiveTriples
|
|
469
619
|
end
|
470
620
|
|
471
621
|
##
|
622
|
+
# @private
|
472
623
|
# @return [RDF::Term] the subject of the relation
|
473
624
|
def rdf_subject
|
474
625
|
if value_arguments.length < 1 || value_arguments.length > 2
|