active-triples 0.10.2 → 0.11.0
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.
- 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
|