active-triples 0.8.1 → 0.8.2
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 +8 -9
- data/CHANGES.md +69 -0
- data/Gemfile +0 -2
- data/Guardfile +1 -2
- data/active-triples.gemspec +3 -3
- data/lib/active/triples.rb +1 -0
- data/lib/active_triples.rb +4 -0
- data/lib/active_triples/configurable.rb +3 -1
- data/lib/active_triples/configuration.rb +1 -0
- data/lib/active_triples/configuration/item.rb +1 -0
- data/lib/active_triples/configuration/item_factory.rb +1 -0
- data/lib/active_triples/configuration/merge_item.rb +5 -2
- data/lib/active_triples/extension_strategy.rb +1 -0
- data/lib/active_triples/identifiable.rb +1 -0
- data/lib/active_triples/list.rb +2 -0
- data/lib/active_triples/nested_attributes.rb +1 -1
- data/lib/active_triples/node_config.rb +5 -3
- data/lib/active_triples/persistable.rb +1 -0
- data/lib/active_triples/persistence_strategies/parent_strategy.rb +104 -29
- data/lib/active_triples/persistence_strategies/persistence_strategy.rb +15 -7
- data/lib/active_triples/persistence_strategies/repository_strategy.rb +26 -22
- data/lib/active_triples/properties.rb +84 -6
- data/lib/active_triples/property.rb +35 -4
- data/lib/active_triples/property_builder.rb +38 -4
- data/lib/active_triples/rdf_source.rb +225 -75
- data/lib/active_triples/reflection.rb +42 -3
- data/lib/active_triples/relation.rb +330 -73
- data/lib/active_triples/repositories.rb +4 -2
- data/lib/active_triples/resource.rb +1 -0
- data/lib/active_triples/schema.rb +1 -0
- data/lib/active_triples/undefined_property_error.rb +27 -0
- data/lib/active_triples/version.rb +2 -1
- data/spec/active_triples/configurable_spec.rb +3 -2
- data/spec/active_triples/configuration_spec.rb +2 -1
- data/spec/active_triples/extension_strategy_spec.rb +2 -1
- data/spec/active_triples/identifiable_spec.rb +7 -11
- data/spec/active_triples/list_spec.rb +1 -4
- data/spec/active_triples/nested_attributes_spec.rb +4 -3
- data/spec/active_triples/persistable_spec.rb +4 -1
- data/spec/active_triples/persistence_strategies/parent_strategy_spec.rb +141 -11
- data/spec/active_triples/persistence_strategies/persistence_strategy_spec.rb +1 -0
- data/spec/active_triples/persistence_strategies/repository_strategy_spec.rb +32 -17
- data/spec/active_triples/properties_spec.rb +68 -33
- data/spec/active_triples/property_builder_spec.rb +36 -0
- data/spec/active_triples/property_spec.rb +15 -1
- data/spec/active_triples/rdf_source_spec.rb +544 -6
- data/spec/active_triples/reflection_spec.rb +78 -0
- data/spec/active_triples/relation_spec.rb +505 -3
- data/spec/active_triples/repositories_spec.rb +3 -1
- data/spec/active_triples/resource_spec.rb +90 -147
- data/spec/active_triples/schema_spec.rb +3 -2
- data/spec/active_triples_spec.rb +1 -0
- data/spec/integration/dummies/dummy_resource_a.rb +6 -0
- data/spec/integration/dummies/dummy_resource_b.rb +6 -0
- data/spec/integration/parent_persistence_spec.rb +18 -0
- data/spec/integration/reciprocal_properties_spec.rb +69 -0
- data/spec/pragmatic_context_spec.rb +10 -8
- data/spec/spec_helper.rb +5 -0
- data/spec/support/active_model_lint.rb +4 -6
- data/spec/support/dummies/basic_persistable.rb +2 -11
- data/spec/support/matchers.rb +11 -0
- data/spec/support/shared_examples/persistence_strategy.rb +3 -16
- metadata +20 -13
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'active_support/core_ext/class'
|
2
3
|
|
3
4
|
module ActiveTriples
|
@@ -9,22 +10,60 @@ module ActiveTriples
|
|
9
10
|
self._active_triples_config = {}
|
10
11
|
end
|
11
12
|
|
13
|
+
##
|
14
|
+
# Gives access to a `Reflection` of the properties configured on this class
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# my_source.reflections.has_property?(:title)
|
18
|
+
# my_source.reflections.reflect_on_property(:title)
|
19
|
+
# @return [Class] gives `self#class`
|
20
|
+
#
|
21
|
+
def reflections
|
22
|
+
self.class
|
23
|
+
end
|
24
|
+
|
12
25
|
def self.add_reflection(model, name, reflection)
|
13
|
-
model._active_triples_config =
|
26
|
+
model._active_triples_config =
|
27
|
+
model._active_triples_config.merge(name.to_s => reflection)
|
14
28
|
end
|
15
29
|
|
16
30
|
module ClassMethods
|
17
|
-
|
18
|
-
|
31
|
+
##
|
32
|
+
# @param [#to_s] property
|
33
|
+
#
|
34
|
+
# @return [ActiveTriples::NodeConfig] the configuration for the property
|
35
|
+
#
|
36
|
+
# @raise [ActiveTriples::UndefinedPropertyError] when the property does
|
37
|
+
# not exist
|
38
|
+
def reflect_on_property(property)
|
39
|
+
_active_triples_config.fetch(property.to_s) do
|
40
|
+
raise ActiveTriples::UndefinedPropertyError.new(property.to_s, self)
|
41
|
+
end
|
19
42
|
end
|
20
43
|
|
44
|
+
##
|
45
|
+
# @return [Hash{String=>ActiveTriples::NodeConfig}] a hash of property
|
46
|
+
# names and their configurations
|
21
47
|
def properties
|
22
48
|
_active_triples_config
|
23
49
|
end
|
24
50
|
|
51
|
+
##
|
52
|
+
# @param [Hash{String=>ActiveTriples::NodeConfig}] a complete config hash
|
53
|
+
# to set the properties to.
|
54
|
+
# @return [Hash{String=>ActiveTriples::NodeConfig}] a hash of property
|
55
|
+
# names and their configurations
|
25
56
|
def properties=(val)
|
26
57
|
self._active_triples_config = val
|
27
58
|
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# @param [#to_s] property
|
62
|
+
#
|
63
|
+
# @return [Boolean] true if the property exsits; false otherwise
|
64
|
+
def has_property?(property)
|
65
|
+
_active_triples_config.keys.include? property.to_s
|
66
|
+
end
|
28
67
|
end
|
29
68
|
end
|
30
69
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'active_support/core_ext/module/delegation'
|
2
3
|
|
3
4
|
module ActiveTriples
|
@@ -8,63 +9,179 @@ module ActiveTriples
|
|
8
9
|
#
|
9
10
|
# <{#parent}> <{#predicate}> [term] .
|
10
11
|
#
|
11
|
-
# Relations
|
12
|
-
# a term.
|
12
|
+
# Relations express a set of binary relationships (on a predicate) between
|
13
|
+
# the parent node and a term.
|
14
|
+
#
|
15
|
+
# When the term is a URI or Blank Node, it is represented in the results
|
16
|
+
# {Array} as an {RDFSource} with a graph selected as a subgraph of the
|
17
|
+
# parent's. The triples in this subgraph are: (a) those whose subject is the
|
18
|
+
# term; (b) ...
|
19
|
+
#
|
13
20
|
#
|
14
21
|
# @see RDF::Term
|
15
22
|
class Relation
|
16
|
-
|
17
|
-
|
23
|
+
include Enumerable
|
24
|
+
|
25
|
+
# @!attribute [rw] parent
|
26
|
+
# @return [RDFSource] the resource that is the domain of this relation
|
27
|
+
# @!attribute [rw] value_arguments
|
28
|
+
# @return [Array<Object>]
|
29
|
+
# @!attribute [rw] rel_args
|
30
|
+
# @return [Hash]
|
31
|
+
# @!attribute [r] reflections
|
32
|
+
# @return [Class]
|
33
|
+
attr_accessor :parent, :value_arguments, :rel_args
|
18
34
|
attr_reader :reflections
|
19
35
|
|
20
|
-
delegate
|
36
|
+
delegate :<=>, :==, :===, :[], :each, :empty?, :equal, :inspect, :last,
|
37
|
+
:to_a, :to_ary, :size, :join, :to => :result
|
21
38
|
|
39
|
+
##
|
40
|
+
# @param [ActiveTriples::RDFSource] parent_source
|
41
|
+
# @param [Array<Symbol, Hash>] value_arguments if a Hash is passed as the
|
42
|
+
# final element, it is removed and set to `@rel_args`.
|
22
43
|
def initialize(parent_source, value_arguments)
|
23
44
|
self.parent = parent_source
|
24
45
|
@reflections = parent_source.reflections
|
25
46
|
self.rel_args ||= {}
|
47
|
+
self.rel_args = value_arguments.pop if value_arguments.is_a?(Array) &&
|
48
|
+
value_arguments.last.is_a?(Hash)
|
26
49
|
self.value_arguments = value_arguments
|
27
50
|
end
|
28
51
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@value_arguments = value_args
|
34
|
-
end
|
35
|
-
|
52
|
+
##
|
53
|
+
# Empties the `Relation`, deleting any associated triples from `parent`.
|
54
|
+
#
|
55
|
+
# @return [Relation] self; a now empty relation
|
36
56
|
def clear
|
37
|
-
|
57
|
+
parent.delete([rdf_subject, predicate, nil])
|
58
|
+
|
59
|
+
self
|
38
60
|
end
|
39
61
|
|
62
|
+
##
|
63
|
+
# Gives an {Array} containing the result set for the {Relation}.
|
64
|
+
#
|
65
|
+
# By default, {RDF::URI} and {RDF::Node} results are cast to `RDFSource`.
|
66
|
+
# {Literal} results are given as their `#object` representations (e.g.
|
67
|
+
# {String}, {Date}.
|
68
|
+
#
|
69
|
+
# @example results with default casting
|
70
|
+
# parent << [parent.rdf_subject, predicate, 'my value']
|
71
|
+
# parent << [parent.rdf_subject, predicate, Date.today]
|
72
|
+
# parent << [parent.rdf_subject, predicate, RDF::URI('http://ex.org/#me')]
|
73
|
+
# parent << [parent.rdf_subject, predicate, RDF::Node.new]
|
74
|
+
# relation.result
|
75
|
+
# # => ["my_value",
|
76
|
+
# # Fri, 25 Sep 2015,
|
77
|
+
# # #<ActiveTriples::Resource:0x3f8...>,
|
78
|
+
# # #<ActiveTriples::Resource:0x3f8...>]
|
79
|
+
#
|
80
|
+
# When `cast?` is `false`, {RDF::Resource} values are left in their raw
|
81
|
+
# form. Similarly, when `#return_literals?` is `true`, literals are
|
82
|
+
# returned in their {RDF::Literal} form, preserving language tags,
|
83
|
+
# datatype, and value.
|
84
|
+
#
|
85
|
+
# @example results with `cast?` set to `false`
|
86
|
+
# relation.result
|
87
|
+
# # => ["my_value",
|
88
|
+
# # Fri, 25 Sep 2015,
|
89
|
+
# # #<RDF::URI:0x3f8... URI:http://ex.org/#me>,
|
90
|
+
# # #<RDF::Node:0x3f8...(_:g69843536054680)>]
|
91
|
+
#
|
92
|
+
# @example results with `return_literals?` set to `true`
|
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...>]
|
98
|
+
#
|
99
|
+
# @return [Array<Object>] the result set
|
40
100
|
def result
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
101
|
+
return [] if predicate.nil?
|
102
|
+
statements = parent.query(:subject => rdf_subject,
|
103
|
+
:predicate => predicate)
|
104
|
+
statements.each_with_object([]) do |x, collector|
|
105
|
+
converted_object = convert_object(x.object)
|
106
|
+
collector << converted_object unless converted_object.nil?
|
45
107
|
end
|
46
108
|
end
|
47
109
|
|
110
|
+
##
|
111
|
+
# Adds values to the relation
|
112
|
+
#
|
113
|
+
# @param [Array<RDF::Resource>, RDF::Resource] values an array of values
|
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}
|
116
|
+
#
|
117
|
+
# @return [Relation] a relation containing the set values; i.e. `self`
|
118
|
+
#
|
119
|
+
# @raise [ActiveTriples::UndefinedPropertyError] if the property is not
|
120
|
+
# already an {RDF::Term} and is not defined in `#property_config`
|
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.
|
48
125
|
def set(values)
|
126
|
+
raise UndefinedPropertyError.new(property, reflections) if predicate.nil?
|
127
|
+
values = values.to_a if values.is_a? Relation
|
49
128
|
values = [values].compact unless values.kind_of?(Array)
|
50
|
-
values = values.to_a if values.class == Relation
|
51
|
-
empty_property
|
52
|
-
values.each do |val|
|
53
|
-
set_value(val)
|
54
|
-
end
|
55
|
-
parent.persist! if parent.persistence_strategy.is_a? ParentStrategy
|
56
|
-
end
|
57
129
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
130
|
+
clear
|
131
|
+
values.each { |val| set_value(val) }
|
132
|
+
|
133
|
+
parent.persist! if parent.persistence_strategy.is_a? ParentStrategy
|
134
|
+
self
|
64
135
|
end
|
65
136
|
|
137
|
+
##
|
138
|
+
# Builds a node with the given attributes, adding it to the relation.
|
139
|
+
#
|
140
|
+
# @param attributes [Hash] a hash of attribute names and values for the
|
141
|
+
# built node.
|
142
|
+
#
|
143
|
+
# @example building an empty generic node
|
144
|
+
# resource = ActiveTriples::Resource.new
|
145
|
+
# resource.resource.get_values(RDF::Vocab::DC.relation).build
|
146
|
+
# # => #<ActiveTriples::Resource:0x2b0(#<ActiveTriples::Resource:0x005>)>)
|
147
|
+
#
|
148
|
+
# resource.dump :ttl
|
149
|
+
# # => "\n [ <http://purl.org/dc/terms/relation> []] .\n"
|
150
|
+
#
|
151
|
+
# Nodes are built using the configured `class_name` for the relation.
|
152
|
+
# Attributes passed in the Hash argument are set on the new node through
|
153
|
+
# `RDFSource#attributes=`. If the attribute keys are not valid properties
|
154
|
+
# on the built node, we raise an error.
|
155
|
+
#
|
156
|
+
# @example building a node with attributes
|
157
|
+
# class WithRelation
|
158
|
+
# include ActiveTriples::RDFSource
|
159
|
+
# property :relation, predicate: RDF::Vocab::DC.relation,
|
160
|
+
# class_name: 'WithTitle'
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# class WithTitle
|
164
|
+
# include ActiveTriples::RDFSource
|
165
|
+
# property :title, predicate: RDF::Vocab::DC.title
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# resource = WithRelation.new
|
169
|
+
# attributes = { id: 'http://ex.org/moomin', title: 'moomin' }
|
170
|
+
#
|
171
|
+
# resource.get_values(:relation).build(attributes)
|
172
|
+
# # => #<ActiveTriples::Resource:0x2b0(#<ActiveTriples::Resource:0x005>)>)
|
173
|
+
#
|
174
|
+
# resource.dump :ttl
|
175
|
+
# # => "\n<http://ex.org/moomin> <http://purl.org/dc/terms/title> \"moomin\" .\n\n [ <http://purl.org/dc/terms/relation> <http://ex.org/moomin>] .\n"
|
176
|
+
#
|
177
|
+
# @todo: clarify class behavior; it is actually tied to type, in some cases.
|
178
|
+
#
|
179
|
+
# @see RDFSource#attributes=
|
180
|
+
# @see http://guides.rubyonrails.org/active_model_basics.html for some
|
181
|
+
# context on ActiveModel attributes.
|
66
182
|
def build(attributes={})
|
67
183
|
new_subject = attributes.fetch('id') { RDF::Node.new }
|
184
|
+
|
68
185
|
make_node(new_subject).tap do |node|
|
69
186
|
node.attributes = attributes.except('id')
|
70
187
|
if parent.kind_of? List::ListResource
|
@@ -77,84 +194,198 @@ module ActiveTriples
|
|
77
194
|
end
|
78
195
|
end
|
79
196
|
|
80
|
-
|
81
|
-
|
197
|
+
##
|
198
|
+
# @note this method behaves somewhat differently from `Array#delete`.
|
199
|
+
# 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
|
202
|
+
# `value` depends on query time. i.e. the `Relation` set does not have an
|
203
|
+
# underlying efficient data structure allowing a reliably cheap existence
|
204
|
+
# check.
|
205
|
+
#
|
206
|
+
# @note symbols are treated as RDF::Nodes by default in
|
207
|
+
# `RDF::Mutable#delete`, but may also represent tokens in statements.
|
208
|
+
# This casts symbols to a literals, which gets us symmetric behavior
|
209
|
+
# between `#set(:sym)` and `#delete(:sym)`.
|
210
|
+
#
|
211
|
+
# @example deleting a value
|
212
|
+
# resource = MySource.new
|
213
|
+
# resource.title = ['moomin', 'valley']
|
214
|
+
# resource.title.delete('moomin') # => ["valley"]
|
215
|
+
# resource.title # => ['valley']
|
216
|
+
#
|
217
|
+
# @example note the behavior of unmatched values
|
218
|
+
# resource = MySource.new
|
219
|
+
# resource.title = 'moomin'
|
220
|
+
# resource.title.delete('valley') # => ["moomin"]
|
221
|
+
# resource.title # => ['moomin']
|
222
|
+
#
|
223
|
+
# @param value [Object] the value to delete from the relation
|
224
|
+
# @return [ActiveTriples::Relation] self
|
225
|
+
def delete(value)
|
226
|
+
value = RDF::Literal(value) if value.is_a? Symbol
|
227
|
+
parent.delete([rdf_subject, predicate, value])
|
228
|
+
|
229
|
+
self
|
82
230
|
end
|
83
231
|
|
84
|
-
|
85
|
-
|
86
|
-
|
232
|
+
##
|
233
|
+
# A variation on `#delete`. This queries the relation for matching
|
234
|
+
# values before running the deletion, returning `nil` if it does not exist.
|
235
|
+
#
|
236
|
+
# @param value [Object] the value to delete from the relation
|
237
|
+
#
|
238
|
+
# @return [Object, nil] `nil` if the value doesn't exist; the value
|
239
|
+
# otherwise
|
240
|
+
# @see #delete
|
241
|
+
def delete?(value)
|
242
|
+
value = RDF::Literal(value) if value.is_a? Symbol
|
243
|
+
|
244
|
+
return nil if parent.query([rdf_subject, predicate, value]).empty?
|
245
|
+
|
246
|
+
delete(value)
|
247
|
+
value
|
248
|
+
end
|
249
|
+
|
250
|
+
##
|
251
|
+
# @overload subtract(enum)
|
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
|
257
|
+
#
|
258
|
+
# @return [Relation] self
|
259
|
+
#
|
260
|
+
# @note This casts symbols to a literals, which gets us symmetric behavior
|
261
|
+
# with `#set(:sym)`.
|
262
|
+
# @see #delete
|
263
|
+
def subtract(*values)
|
264
|
+
values = values.first if values.first.is_a? Enumerable
|
265
|
+
statements = values.map do |value|
|
266
|
+
value = RDF::Literal(value) if value.is_a? Symbol
|
267
|
+
[rdf_subject, predicate, value]
|
87
268
|
end
|
269
|
+
|
270
|
+
parent.delete(*statements)
|
271
|
+
self
|
88
272
|
end
|
89
273
|
|
90
|
-
|
91
|
-
|
92
|
-
|
274
|
+
##
|
275
|
+
# Replaces the first argument with the second as a value within the
|
276
|
+
# relation.
|
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)
|
93
287
|
end
|
94
288
|
|
95
|
-
|
289
|
+
##
|
290
|
+
# @return [Object] the first result, if present; else a newly built node
|
291
|
+
#
|
292
|
+
# @see #build
|
293
|
+
def first_or_create(attributes={})
|
294
|
+
result.first || build(attributes)
|
295
|
+
end
|
96
296
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
297
|
+
##
|
298
|
+
# Adds values to the result set
|
299
|
+
#
|
300
|
+
# @param values [Object, Array<Object>] values to add
|
301
|
+
#
|
302
|
+
# @return [Relation] a relation containing the set values; i.e. `self`
|
303
|
+
def <<(values)
|
304
|
+
values = Array.wrap(result) | Array.wrap(values)
|
101
305
|
self.set(values)
|
102
306
|
end
|
307
|
+
alias_method :push, :<<
|
103
308
|
|
309
|
+
##
|
310
|
+
# @return [Hash<Symbol, ]
|
311
|
+
# @todo find a way to simplify this?
|
104
312
|
def property_config
|
105
|
-
return type_property if
|
313
|
+
return type_property if is_type?
|
314
|
+
|
106
315
|
reflections.reflect_on_property(property)
|
107
316
|
end
|
108
317
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
318
|
+
##
|
319
|
+
# Returns the property for the Relation. This may be a registered
|
320
|
+
# property key or an {RDF::URI}.
|
321
|
+
#
|
322
|
+
# @return [Symbol, RDF::URI] the property for this Relation.
|
323
|
+
# @see #predicate
|
116
324
|
def property
|
117
325
|
value_arguments.last
|
118
326
|
end
|
119
327
|
|
328
|
+
##
|
329
|
+
# Gives the predicate used by the Relation. Values of this object are
|
330
|
+
# those that match the pattern `<rdf_subject> <predicate> [value] .`
|
331
|
+
#
|
332
|
+
# @return [RDF::Term, nil] the predicate for this relation; nil if
|
333
|
+
# no predicate can be found
|
334
|
+
#
|
335
|
+
# @see #property
|
336
|
+
def predicate
|
337
|
+
return property if property.is_a?(RDF::Term)
|
338
|
+
property_config[:predicate] if is_property?
|
339
|
+
end
|
340
|
+
|
120
341
|
protected
|
121
342
|
|
122
343
|
def node_cache
|
123
344
|
@node_cache ||= {}
|
124
345
|
end
|
125
346
|
|
347
|
+
def is_property?
|
348
|
+
reflections.has_property?(property) || is_type?
|
349
|
+
end
|
350
|
+
|
351
|
+
def is_type?
|
352
|
+
(property == RDF.type || property.to_s == "type") &&
|
353
|
+
(!reflections.kind_of?(RDFSource) || !is_property?)
|
354
|
+
end
|
355
|
+
|
126
356
|
def set_value(val)
|
127
|
-
|
128
|
-
val = val.resource if val.respond_to?(:resource)
|
129
|
-
val = value_to_node(val)
|
357
|
+
val = value_to_node(val.respond_to?(:resource) ? val.resource : val)
|
130
358
|
if val.kind_of? RDFSource
|
131
359
|
node_cache[val.rdf_subject] = nil
|
132
|
-
add_child_node(val
|
360
|
+
add_child_node(val)
|
133
361
|
return
|
134
362
|
end
|
135
363
|
val = val.to_uri if val.respond_to? :to_uri
|
136
|
-
raise
|
137
|
-
" See RDF::Literal.\n\tYou provided #{val.inspect}" unless
|
138
|
-
val.kind_of? RDF::Term
|
364
|
+
raise ValueError, val unless val.kind_of? RDF::Term
|
139
365
|
parent.insert [rdf_subject, predicate, val]
|
140
366
|
end
|
141
367
|
|
368
|
+
def type_property
|
369
|
+
{ :predicate => RDF.type, :cast => false }
|
370
|
+
end
|
371
|
+
|
142
372
|
def value_to_node(val)
|
143
373
|
valid_datatype?(val) ? RDF::Literal(val) : val
|
144
374
|
end
|
145
375
|
|
146
|
-
def add_child_node(resource
|
376
|
+
def add_child_node(resource)
|
147
377
|
parent.insert [rdf_subject, predicate, resource.rdf_subject]
|
148
|
-
|
378
|
+
|
379
|
+
resource = resource.dup
|
380
|
+
unless resource == parent ||
|
381
|
+
(parent.persistence_strategy.is_a?(ParentStrategy) &&
|
382
|
+
parent.persistence_strategy.ancestors.find { |a| a == resource })
|
149
383
|
resource.set_persistence_strategy(ParentStrategy)
|
150
384
|
resource.parent = parent
|
151
385
|
end
|
152
|
-
self.node_cache[resource.rdf_subject] = (object ? object : resource)
|
153
|
-
resource.persist! if resource.persistence_strategy.is_a? ParentStrategy
|
154
|
-
end
|
155
386
|
|
156
|
-
|
157
|
-
|
387
|
+
self.node_cache[resource.rdf_subject] = resource
|
388
|
+
resource.persist! if resource.persistence_strategy.is_a? ParentStrategy
|
158
389
|
end
|
159
390
|
|
160
391
|
def valid_datatype?(val)
|
@@ -189,13 +420,15 @@ module ActiveTriples
|
|
189
420
|
value = RDF::Node.new if value.nil?
|
190
421
|
node = node_cache[value] if node_cache[value]
|
191
422
|
node ||= klass.from_uri(value,parent)
|
192
|
-
|
423
|
+
node.set_persistence_strategy(property_config[:persist_to]) if
|
424
|
+
is_property? && property_config[:persist_to]
|
425
|
+
return nil if (is_property? && property_config[:class_name]) && (class_for_value(value) != class_for_property)
|
193
426
|
self.node_cache[value] ||= node
|
194
427
|
node
|
195
428
|
end
|
196
429
|
|
197
430
|
def cast?
|
198
|
-
return true unless
|
431
|
+
return true unless is_property? || (rel_args && rel_args[:cast])
|
199
432
|
return rel_args[:cast] if rel_args.has_key?(:cast)
|
200
433
|
!!property_config[:cast]
|
201
434
|
end
|
@@ -226,20 +459,44 @@ module ActiveTriples
|
|
226
459
|
end
|
227
460
|
|
228
461
|
def class_for_property
|
229
|
-
klass = property_config[:class_name] if
|
462
|
+
klass = property_config[:class_name] if is_property?
|
230
463
|
klass ||= Resource
|
231
464
|
klass = ActiveTriples.class_from_string(klass, final_parent.class) if
|
232
465
|
klass.kind_of? String
|
233
466
|
klass
|
234
467
|
end
|
235
468
|
|
469
|
+
##
|
470
|
+
# @return [RDF::Term] the subject of the relation
|
236
471
|
def rdf_subject
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
else
|
241
|
-
parent.rdf_subject
|
472
|
+
if value_arguments.length < 1 || value_arguments.length > 2
|
473
|
+
raise(ArgumentError,
|
474
|
+
"wrong number of arguments (#{value_arguments.length} for 1-2)")
|
242
475
|
end
|
476
|
+
|
477
|
+
value_arguments.length > 1 ? value_arguments.first : parent.rdf_subject
|
243
478
|
end
|
479
|
+
|
480
|
+
public
|
481
|
+
|
482
|
+
##
|
483
|
+
# An error class for unallowable values in relations.
|
484
|
+
class ValueError < ArgumentError
|
485
|
+
# @!attribute [r] value
|
486
|
+
attr_reader :value
|
487
|
+
|
488
|
+
##
|
489
|
+
# @param value [Object]
|
490
|
+
def initialize(value)
|
491
|
+
@value = value
|
492
|
+
end
|
493
|
+
|
494
|
+
##
|
495
|
+
# @return [String]
|
496
|
+
def message
|
497
|
+
'value must be an RDF URI, Node, Literal, or a valid datatype. '\
|
498
|
+
"See RDF::Literal.\n\tYou provided #{value.inspect}"
|
499
|
+
end
|
500
|
+
end
|
244
501
|
end
|
245
502
|
end
|