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
@@ -3,43 +3,267 @@ require 'spec_helper'
|
|
3
3
|
require 'rdf/isomorphic'
|
4
4
|
|
5
5
|
describe ActiveTriples::Relation do
|
6
|
-
let(:parent_resource) { double(
|
7
|
-
let(:value_args)
|
6
|
+
let(:parent_resource) { double('parent resource', reflections: {}) }
|
7
|
+
let(:value_args) { double('value args', last: {}) }
|
8
8
|
|
9
9
|
let(:uri) { RDF::URI('http://example.org/moomin') }
|
10
10
|
|
11
|
-
subject { described_class.new(parent_resource,
|
11
|
+
subject { described_class.new(parent_resource, value_args) }
|
12
12
|
|
13
13
|
shared_context 'with URI property' do
|
14
|
-
subject { described_class.new(parent_resource, [property]
|
14
|
+
subject { described_class.new(parent_resource, [property]) }
|
15
|
+
|
15
16
|
let(:property) { uri }
|
16
17
|
end
|
17
18
|
|
18
19
|
shared_context 'with symbol property' do
|
19
|
-
subject { described_class.new(parent_resource, [property]
|
20
|
+
subject { described_class.new(parent_resource, [property]) }
|
21
|
+
|
20
22
|
let(:property) { :moomin }
|
21
|
-
|
23
|
+
|
24
|
+
let(:reflections) do
|
22
25
|
Class.new do
|
23
26
|
include ActiveTriples::RDFSource
|
27
|
+
|
24
28
|
property :moomin, predicate: RDF::URI('http://example.org/moomin')
|
25
29
|
end
|
26
30
|
end
|
27
|
-
|
31
|
+
|
28
32
|
before do
|
29
33
|
allow(parent_resource).to receive(:reflections).and_return(reflections)
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
37
|
shared_context 'with unregistered property' do
|
34
|
-
subject { described_class.new(parent_resource, [property]
|
35
|
-
|
38
|
+
subject { described_class.new(parent_resource, [property]) }
|
39
|
+
|
40
|
+
let(:property) { :moomin }
|
36
41
|
let(:reflections) { Class.new { include ActiveTriples::RDFSource } }
|
37
|
-
|
42
|
+
|
38
43
|
before do
|
39
44
|
allow(parent_resource).to receive(:reflections).and_return(reflections)
|
40
45
|
end
|
41
46
|
end
|
42
47
|
|
48
|
+
shared_context 'with other relation' do
|
49
|
+
let(:other_class) do
|
50
|
+
Class.new do
|
51
|
+
include ActiveTriples::RDFSource
|
52
|
+
property :snork, predicate: RDF::URI('http://example.org/snork')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:other) { described_class.new(other_parent, other_args) }
|
57
|
+
let(:other_args) { [other_property] }
|
58
|
+
let(:other_parent) { other_class.new }
|
59
|
+
let(:other_property) { :snork }
|
60
|
+
end
|
61
|
+
|
62
|
+
[:&, :|, :+].each do |array_method|
|
63
|
+
describe "#{array_method}" do
|
64
|
+
shared_examples 'array method behavior' do
|
65
|
+
it "behaves like `Array##{array_method}`" do
|
66
|
+
expect(subject.send(array_method.to_sym, other_array))
|
67
|
+
.to contain_exactly(*subject.to_a.send(array_method.to_sym,
|
68
|
+
other_array.to_a))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with #to_ary as argument' do
|
73
|
+
include_context 'with symbol property'
|
74
|
+
|
75
|
+
let(:parent_resource) { ActiveTriples::Resource.new }
|
76
|
+
|
77
|
+
context 'when empty' do
|
78
|
+
context 'with empty other' do
|
79
|
+
it_behaves_like 'array method behavior' do
|
80
|
+
let(:other_array) { [] }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'with values in other' do
|
85
|
+
it_behaves_like 'array method behavior' do
|
86
|
+
let(:other_array) { [1, 'two', RDF::URI('uri'), RDF::Node.new] }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'with values' do
|
92
|
+
before { subject << [RDF::Node.new, 'two', 3] }
|
93
|
+
|
94
|
+
context 'with empty other' do
|
95
|
+
it_behaves_like 'array method behavior' do
|
96
|
+
let(:other_array) { [] }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'with values in other' do
|
101
|
+
it_behaves_like 'array method behavior' do
|
102
|
+
let(:other_array) { [1, 'two', RDF::URI('uri'), RDF::Node.new] }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#&' do
|
111
|
+
context 'with relation as argument' do
|
112
|
+
include_context 'with symbol property'
|
113
|
+
let(:parent_resource) { ActiveTriples::Resource.new }
|
114
|
+
|
115
|
+
include_context 'with other relation'
|
116
|
+
|
117
|
+
it { expect(subject & other).to be_empty }
|
118
|
+
|
119
|
+
it 'handles node equality' do
|
120
|
+
node = RDF::Node.new
|
121
|
+
|
122
|
+
subject << [1, node]
|
123
|
+
other << [2, node]
|
124
|
+
|
125
|
+
expect(subject & other).to contain_exactly(have_rdf_subject(node))
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'handles literal equality' do
|
129
|
+
literal = RDF::Literal('mummi')
|
130
|
+
lang_literal = RDF::Literal('mummi', language: :fi)
|
131
|
+
|
132
|
+
subject << [1, literal]
|
133
|
+
other << [2, lang_literal]
|
134
|
+
|
135
|
+
expect(subject & other).to be_empty
|
136
|
+
|
137
|
+
subject << [1, lang_literal]
|
138
|
+
expect(subject & other).to contain_exactly lang_literal
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe '#|' do
|
144
|
+
context 'with relation as argument' do
|
145
|
+
include_context 'with symbol property'
|
146
|
+
let(:parent_resource) { ActiveTriples::Resource.new }
|
147
|
+
|
148
|
+
include_context 'with other relation'
|
149
|
+
|
150
|
+
it 'handles node equality' do
|
151
|
+
node = RDF::Node.new
|
152
|
+
|
153
|
+
subject << [1, node]
|
154
|
+
other << [2, node]
|
155
|
+
|
156
|
+
expect(subject | other).to contain_exactly(1, 2, have_rdf_subject(node))
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'handles literal equality' do
|
160
|
+
literal = RDF::Literal('mummi')
|
161
|
+
lang_literal = RDF::Literal('mummi', language: :fi)
|
162
|
+
|
163
|
+
subject << [1, literal]
|
164
|
+
other << [2, lang_literal]
|
165
|
+
|
166
|
+
expect(subject | other).to contain_exactly('mummi', lang_literal, 1, 2)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#+' do
|
172
|
+
context 'with relation as argument' do
|
173
|
+
include_context 'with symbol property'
|
174
|
+
let(:parent_resource) { ActiveTriples::Resource.new }
|
175
|
+
|
176
|
+
include_context 'with other relation'
|
177
|
+
|
178
|
+
it 'still implements as ' do
|
179
|
+
subject << [RDF::Node.new, RDF::Node.new,
|
180
|
+
RDF::Literal('mummi'), RDF::Literal('mummi', language: :fi)]
|
181
|
+
other << [RDF::Node.new, RDF::Node.new,
|
182
|
+
RDF::Literal('mummi'), RDF::Literal('mummi', language: :fi)]
|
183
|
+
|
184
|
+
expect(subject + other).to contain_exactly(*(subject.to_a + other.to_a))
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe '#<=>' do
|
190
|
+
include_context 'with symbol property'
|
191
|
+
|
192
|
+
let(:parent_resource) { ActiveTriples::Resource.new }
|
193
|
+
|
194
|
+
shared_examples 'a comparable relation' do
|
195
|
+
it 'gives 0 when both are empty' do
|
196
|
+
expect(subject <=> other).to eq 0
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'gives nil when not comparable' do
|
200
|
+
subject << 1
|
201
|
+
expect(subject <=> other).to be_nil
|
202
|
+
end
|
203
|
+
|
204
|
+
types = { numeric: [0, 1, 2, 3_000_000_000],
|
205
|
+
string: ['moomin', 'snork', 'snufkin'],
|
206
|
+
date: [Date.today, Date.today - 1],
|
207
|
+
uri: [RDF::URI('one'), RDF::URI('two'), RDF::URI('three')],
|
208
|
+
node: [RDF::Node.new, RDF::Node.new],
|
209
|
+
}
|
210
|
+
|
211
|
+
types.each do |type, values|
|
212
|
+
it "gives 0 when containing the same #{type} elements" do
|
213
|
+
subject << 1
|
214
|
+
other << 1
|
215
|
+
expect(subject <=> other).to eq 0
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'with varied elements' do
|
220
|
+
before do
|
221
|
+
types.each do |_, values|
|
222
|
+
values.each do |value|
|
223
|
+
subject << value
|
224
|
+
other << value
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
it "gives 0 when containing the same varied elements" do
|
230
|
+
expect(subject <=> other).to eq 0
|
231
|
+
end
|
232
|
+
|
233
|
+
it "gives nil when other contains a subset of varied elements" do
|
234
|
+
subject << 'extra'
|
235
|
+
expect(subject <=> other).to be_nil
|
236
|
+
end
|
237
|
+
|
238
|
+
it "gives nil when other contains a superset of varied elements" do
|
239
|
+
other << 'extra'
|
240
|
+
expect(subject <=> other).to be_nil
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
context 'when other is a Relation' do
|
246
|
+
include_context 'with other relation'
|
247
|
+
it_behaves_like 'a comparable relation'
|
248
|
+
|
249
|
+
context 'and without cast' do
|
250
|
+
let(:other_args) { [other_property, cast: false] }
|
251
|
+
it_behaves_like 'a comparable relation'
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'and with return_literals' do
|
255
|
+
let(:other_args) { [other_property, return_literals: true] }
|
256
|
+
it_behaves_like 'a comparable relation'
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
context 'when other is an Array' do
|
261
|
+
let(:other) { [] }
|
262
|
+
|
263
|
+
it_behaves_like 'a comparable relation'
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
43
267
|
describe '#build' do
|
44
268
|
include_context 'with symbol property'
|
45
269
|
|
@@ -61,15 +285,16 @@ describe ActiveTriples::Relation do
|
|
61
285
|
uri = 'http://example.com/moomin'
|
62
286
|
expect(subject.build(id: uri)).to be_uri
|
63
287
|
end
|
64
|
-
|
288
|
+
|
65
289
|
context 'with configured properties' do
|
66
290
|
include_context 'with symbol property' do
|
67
|
-
before do
|
291
|
+
before do
|
68
292
|
reflections.property :moomin,
|
69
293
|
predicate: RDF::Vocab::DC.relation,
|
70
294
|
class_name: 'WithTitle'
|
71
295
|
class WithTitle
|
72
296
|
include ActiveTriples::RDFSource
|
297
|
+
|
73
298
|
property :title, predicate: RDF::Vocab::DC.title
|
74
299
|
end
|
75
300
|
end
|
@@ -79,7 +304,7 @@ describe ActiveTriples::Relation do
|
|
79
304
|
|
80
305
|
it 'sets attributes for built node' do
|
81
306
|
attributes = { title: 'moomin' }
|
82
|
-
|
307
|
+
|
83
308
|
expect(subject.build(attributes))
|
84
309
|
.to have_attributes(title: ['moomin'])
|
85
310
|
end
|
@@ -109,28 +334,28 @@ describe ActiveTriples::Relation do
|
|
109
334
|
it 'deletes a matched value' do
|
110
335
|
expect { subject.delete(values.first) }
|
111
336
|
.to change { subject.to_a }
|
112
|
-
|
337
|
+
.to contain_exactly(*values[1..-1])
|
113
338
|
end
|
114
339
|
|
115
340
|
it 'deletes a URI value' do
|
116
341
|
values.delete(uri)
|
117
342
|
expect { subject.delete(uri) }
|
118
343
|
.to change { subject.to_a }
|
119
|
-
|
344
|
+
.to contain_exactly(*values)
|
120
345
|
end
|
121
346
|
|
122
347
|
it 'deletes a node value' do
|
123
348
|
values.delete(node)
|
124
349
|
expect { subject.delete(node) }
|
125
350
|
.to change { subject.to_a }
|
126
|
-
|
351
|
+
.to contain_exactly(*values)
|
127
352
|
end
|
128
353
|
|
129
354
|
it 'deletes a token value' do
|
130
355
|
values.delete(:one)
|
131
356
|
expect { subject.delete(:one) }
|
132
357
|
.to change { subject.to_a }
|
133
|
-
|
358
|
+
.to contain_exactly(*values)
|
134
359
|
end
|
135
360
|
end
|
136
361
|
end
|
@@ -160,16 +385,16 @@ describe ActiveTriples::Relation do
|
|
160
385
|
include_context 'with symbol property'
|
161
386
|
|
162
387
|
let(:parent_resource) { ActiveTriples::Resource.new }
|
163
|
-
|
388
|
+
|
164
389
|
it 'subtracts values as arguments' do
|
165
|
-
subject.set([1,2,3])
|
166
|
-
expect { subject.subtract(2,3) }
|
390
|
+
subject.set([1, 2, 3])
|
391
|
+
expect { subject.subtract(2, 3) }
|
167
392
|
.to change { subject.to_a }.to contain_exactly(1)
|
168
393
|
end
|
169
394
|
|
170
395
|
it 'subtracts values as an enumerable' do
|
171
|
-
subject.set([1,2,3])
|
172
|
-
expect { subject.subtract([2,3]) }
|
396
|
+
subject.set([1, 2, 3])
|
397
|
+
expect { subject.subtract([2, 3]) }
|
173
398
|
.to change { subject.to_a }.to contain_exactly(1)
|
174
399
|
end
|
175
400
|
|
@@ -184,7 +409,7 @@ describe ActiveTriples::Relation do
|
|
184
409
|
include_context 'with symbol property'
|
185
410
|
|
186
411
|
let(:parent_resource) { ActiveTriples::Resource.new }
|
187
|
-
|
412
|
+
|
188
413
|
it 'returns nil when the value is not present' do
|
189
414
|
expect(subject.swap(1, 2)).to be_nil
|
190
415
|
end
|
@@ -203,18 +428,20 @@ describe ActiveTriples::Relation do
|
|
203
428
|
|
204
429
|
describe '#clear' do
|
205
430
|
include_context 'with symbol property'
|
431
|
+
|
206
432
|
let(:parent_resource) { ActiveTriples::Resource.new }
|
207
433
|
|
208
434
|
context 'with values' do
|
209
435
|
before do
|
210
|
-
subject.parent << [subject.parent.rdf_subject,
|
211
|
-
subject.predicate,
|
436
|
+
subject.parent << [subject.parent.rdf_subject,
|
437
|
+
subject.predicate,
|
212
438
|
'moomin']
|
213
|
-
end
|
439
|
+
end
|
214
440
|
|
215
441
|
it 'clears the relation' do
|
216
|
-
expect { subject.clear }
|
217
|
-
|
442
|
+
expect { subject.clear }
|
443
|
+
.to change { subject.to_a }
|
444
|
+
.from(['moomin']).to(be_empty)
|
218
445
|
end
|
219
446
|
|
220
447
|
it 'deletes statements from parent' do
|
@@ -224,7 +451,7 @@ describe ActiveTriples::Relation do
|
|
224
451
|
.to change { subject.parent.query(query_pattern) }.to([])
|
225
452
|
end
|
226
453
|
end
|
227
|
-
|
454
|
+
|
228
455
|
it 'is a no-op when relation is empty' do
|
229
456
|
subject.parent << [subject.parent.rdf_subject, RDF.type, 'moomin']
|
230
457
|
expect { subject.clear }.not_to change { subject.parent.statements.to_a }
|
@@ -233,8 +460,9 @@ describe ActiveTriples::Relation do
|
|
233
460
|
|
234
461
|
describe '#<<' do
|
235
462
|
include_context 'with symbol property'
|
463
|
+
|
236
464
|
let(:parent_resource) { ActiveTriples::Resource.new }
|
237
|
-
|
465
|
+
|
238
466
|
it 'adds a value' do
|
239
467
|
expect { subject << :moomin }
|
240
468
|
.to change { subject.to_a }.to contain_exactly(:moomin)
|
@@ -245,9 +473,74 @@ describe ActiveTriples::Relation do
|
|
245
473
|
expect { subject << values }
|
246
474
|
.to change { subject.to_a }.to contain_exactly(*values)
|
247
475
|
end
|
476
|
+
|
477
|
+
it 'keeps datatypes' do
|
478
|
+
values = [RDF::Literal(Date.today), RDF::Literal(:moomin)]
|
479
|
+
|
480
|
+
expect { values.each { |v| subject << v } }
|
481
|
+
.to change { subject.send(:objects).to_a }
|
482
|
+
.to contain_exactly(*values)
|
483
|
+
end
|
484
|
+
|
485
|
+
it 'keeps languages' do
|
486
|
+
values = [RDF::Literal("Moomin", language: :en),
|
487
|
+
RDF::Literal("Mummi", language: :fi)]
|
488
|
+
|
489
|
+
expect { values.each { |v| subject << v } }
|
490
|
+
.to change { subject.send(:objects).to_a }
|
491
|
+
.to contain_exactly(*values)
|
492
|
+
end
|
493
|
+
|
494
|
+
context 'when given a Relation' do
|
495
|
+
it 'keeps datatypes and languages of values' do
|
496
|
+
values = [RDF::Literal(Date.today),
|
497
|
+
RDF::Literal(:moomin),
|
498
|
+
RDF::Literal("Moomin", language: :en),
|
499
|
+
RDF::Literal("Mummi", language: :fi)]
|
500
|
+
|
501
|
+
subject.set(values)
|
502
|
+
expect(subject.send(:objects)).to contain_exactly(*values)
|
503
|
+
|
504
|
+
expect { subject << subject }
|
505
|
+
.not_to change { subject.send(:objects).to_a }
|
506
|
+
end
|
507
|
+
|
508
|
+
it 'retains unknown datatypes' do
|
509
|
+
literal =
|
510
|
+
RDF::Literal('snowflake',
|
511
|
+
datatype: RDF::URI('http://emaple.com/snowflake'))
|
512
|
+
|
513
|
+
subject << literal
|
514
|
+
|
515
|
+
expect { subject << 'snowflake' }
|
516
|
+
.to change { subject.to_a }
|
517
|
+
.to contain_exactly(literal, 'snowflake')
|
518
|
+
|
519
|
+
end
|
520
|
+
|
521
|
+
context 'with a datatyped literal' do
|
522
|
+
before do
|
523
|
+
class DummySnowflake < RDF::Literal
|
524
|
+
DATATYPE = RDF::URI('http://example.com/snowflake').freeze
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
after { Object.send(:remove_const, :DummySnowflake) }
|
529
|
+
|
530
|
+
it 'retains datatypes' do
|
531
|
+
literal = DummySnowflake.new('special')
|
532
|
+
|
533
|
+
subject << literal
|
534
|
+
|
535
|
+
expect { subject << 'special' }
|
536
|
+
.to change { subject.send(:objects).to_a }
|
537
|
+
.to contain_exactly(literal, RDF::Literal('special'))
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
248
541
|
end
|
249
542
|
|
250
|
-
describe
|
543
|
+
describe '#predicate' do
|
251
544
|
context 'when the property is an RDF::Term' do
|
252
545
|
include_context 'with URI property'
|
253
546
|
|
@@ -266,13 +559,14 @@ describe ActiveTriples::Relation do
|
|
266
559
|
|
267
560
|
context 'when the symbol property is unregistered' do
|
268
561
|
include_context 'with unregistered property'
|
562
|
+
|
269
563
|
it 'returns nil' do
|
270
564
|
expect(subject.predicate).to be_nil
|
271
565
|
end
|
272
566
|
end
|
273
567
|
end
|
274
|
-
|
275
|
-
describe
|
568
|
+
|
569
|
+
describe '#property' do
|
276
570
|
context 'when the property is an RDF::Term' do
|
277
571
|
include_context 'with URI property'
|
278
572
|
|
@@ -298,49 +592,22 @@ describe ActiveTriples::Relation do
|
|
298
592
|
end
|
299
593
|
end
|
300
594
|
|
301
|
-
describe '#
|
302
|
-
let(:parent_resource) { ActiveTriples::Resource.new }
|
303
|
-
|
304
|
-
context 'with symbol' do
|
305
|
-
include_context 'with symbol property'
|
306
|
-
|
307
|
-
it 'creates a new node' do
|
308
|
-
expect { subject.first_or_create }.to change { subject.count }.by(1)
|
309
|
-
end
|
310
|
-
|
311
|
-
it 'returns existing node if present' do
|
312
|
-
node = subject.build
|
313
|
-
expect(subject.first_or_create).to eq node
|
314
|
-
end
|
315
|
-
|
316
|
-
it 'does not create a new node when one exists' do
|
317
|
-
subject.build
|
318
|
-
expect { subject.first_or_create }.not_to change { subject.count }
|
319
|
-
end
|
320
|
-
|
321
|
-
it 'returns literal value if appropriate' do
|
322
|
-
subject << literal = 'moomin'
|
323
|
-
expect(subject.first_or_create).to eq literal
|
324
|
-
end
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
describe '#result' do
|
595
|
+
describe '#each' do
|
329
596
|
context 'with nil predicate' do
|
330
597
|
include_context 'with unregistered property'
|
331
|
-
|
598
|
+
|
332
599
|
it 'is empty' do
|
333
|
-
expect(subject.
|
600
|
+
expect(subject.each.to_a).to be_empty
|
334
601
|
end
|
335
602
|
end
|
336
|
-
|
603
|
+
|
337
604
|
context 'with predicate' do
|
338
605
|
include_context 'with symbol property' do
|
339
606
|
let(:parent_resource) { ActiveTriples::Resource.new }
|
340
607
|
end
|
341
608
|
|
342
609
|
it 'is empty' do
|
343
|
-
expect(subject.
|
610
|
+
expect(subject.each.to_a).to be_empty
|
344
611
|
end
|
345
612
|
|
346
613
|
context 'with values' do
|
@@ -351,10 +618,10 @@ describe ActiveTriples::Relation do
|
|
351
618
|
end
|
352
619
|
|
353
620
|
let(:values) { ['Comet in Moominland', 'Finn Family Moomintroll'] }
|
354
|
-
let(:node)
|
621
|
+
let(:node) { RDF::Node.new }
|
355
622
|
|
356
623
|
it 'contain values' do
|
357
|
-
expect(subject.
|
624
|
+
expect(subject.each).to contain_exactly(*values)
|
358
625
|
end
|
359
626
|
|
360
627
|
context 'with castable values' do
|
@@ -363,53 +630,42 @@ describe ActiveTriples::Relation do
|
|
363
630
|
end
|
364
631
|
|
365
632
|
it 'casts Resource values' do
|
366
|
-
expect(subject.
|
633
|
+
expect(subject.each)
|
367
634
|
.to contain_exactly(a_kind_of(ActiveTriples::Resource),
|
368
635
|
a_kind_of(ActiveTriples::Resource),
|
369
636
|
a_kind_of(ActiveTriples::Resource))
|
370
637
|
end
|
371
638
|
|
372
639
|
it 'cast values have correct URI' do
|
373
|
-
expect(subject.
|
640
|
+
expect(subject.each.map(&:rdf_subject))
|
374
641
|
.to contain_exactly(*values)
|
375
642
|
end
|
376
643
|
|
377
644
|
context 'and persistence_strategy is configured' do
|
378
645
|
before do
|
379
646
|
reflections
|
380
|
-
.property :moomin,
|
381
|
-
predicate: RDF::URI('http://example.org/moomin'),
|
647
|
+
.property :moomin,
|
648
|
+
predicate: RDF::URI('http://example.org/moomin'),
|
382
649
|
persist_to: ActiveTriples::RepositoryStrategy
|
383
650
|
end
|
384
|
-
|
651
|
+
|
385
652
|
it 'assigns persistence strategy' do
|
386
|
-
subject.
|
653
|
+
subject.each.each do |node|
|
387
654
|
expect(node.persistence_strategy)
|
388
655
|
.to be_a ActiveTriples::RepositoryStrategy
|
389
656
|
end
|
390
657
|
end
|
391
658
|
end
|
392
|
-
|
659
|
+
|
393
660
|
context 'and #cast? is false' do
|
394
661
|
let(:values) do
|
395
662
|
[uri, RDF::URI('http://ex.org/too-ticky'), RDF::Node.new,
|
396
|
-
|
663
|
+
'moomin', Date.today]
|
397
664
|
end
|
398
665
|
|
399
666
|
it 'does not cast results' do
|
400
667
|
allow(subject).to receive(:cast?).and_return(false)
|
401
|
-
expect(subject.
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
context 'when #return_literals? is true' do
|
406
|
-
let(:values) do
|
407
|
-
[RDF::Literal('moomin'), RDF::Literal(Date.today)]
|
408
|
-
end
|
409
|
-
|
410
|
-
it 'does not cast results' do
|
411
|
-
allow(subject).to receive(:return_literals?).and_return(true)
|
412
|
-
expect(subject.result).to contain_exactly(*values)
|
668
|
+
expect(subject.each).to contain_exactly(*values)
|
413
669
|
end
|
414
670
|
end
|
415
671
|
end
|
@@ -417,46 +673,46 @@ describe ActiveTriples::Relation do
|
|
417
673
|
end
|
418
674
|
end
|
419
675
|
|
420
|
-
describe
|
421
|
-
let(:parent_resource) { double(
|
676
|
+
describe '#rdf_subject' do
|
677
|
+
let(:parent_resource) { double('parent resource', reflections: {}) }
|
422
678
|
|
423
|
-
subject { described_class.new(parent_resource, double(
|
679
|
+
subject { described_class.new(parent_resource, double('value args')) }
|
424
680
|
|
425
|
-
context
|
681
|
+
context 'when relation has 0 value arguments' do
|
426
682
|
before { subject.value_arguments = double(length: 0) }
|
427
683
|
|
428
|
-
it
|
684
|
+
it 'should raise an error' do
|
429
685
|
expect { subject.send(:rdf_subject) }.to raise_error ArgumentError
|
430
686
|
end
|
431
687
|
end
|
432
688
|
|
433
|
-
context
|
689
|
+
context 'when term has 1 value argument' do
|
434
690
|
before do
|
435
|
-
allow(subject.parent).to receive(:rdf_subject) {
|
691
|
+
allow(subject.parent).to receive(:rdf_subject) { 'parent subject' }
|
436
692
|
subject.value_arguments = double(length: 1)
|
437
693
|
end
|
438
694
|
|
439
695
|
it "should call `rdf_subject' on the parent" do
|
440
|
-
expect(subject.send(:rdf_subject)
|
696
|
+
expect(subject.send(:rdf_subject)).to eq 'parent subject'
|
441
697
|
end
|
442
698
|
|
443
|
-
it
|
699
|
+
it 'is a private method' do
|
444
700
|
expect { subject.rdf_subject }.to raise_error NoMethodError
|
445
701
|
end
|
446
702
|
end
|
447
703
|
|
448
|
-
context
|
449
|
-
before { subject.value_arguments = double(length: 2, first:
|
704
|
+
context 'when relation has 2 value arguments' do
|
705
|
+
before { subject.value_arguments = double(length: 2, first: 'first') }
|
450
706
|
|
451
|
-
it
|
452
|
-
expect(subject.send(:rdf_subject)
|
707
|
+
it 'should return the first value argument' do
|
708
|
+
expect(subject.send(:rdf_subject)).to eq 'first'
|
453
709
|
end
|
454
710
|
end
|
455
711
|
|
456
|
-
context
|
712
|
+
context 'when relation has 3 value arguments' do
|
457
713
|
before { subject.value_arguments = double(length: 3) }
|
458
714
|
|
459
|
-
it
|
715
|
+
it 'should raise an error' do
|
460
716
|
expect { subject.send(:rdf_subject) }.to raise_error ArgumentError
|
461
717
|
end
|
462
718
|
end
|
@@ -476,7 +732,7 @@ describe ActiveTriples::Relation do
|
|
476
732
|
end
|
477
733
|
end
|
478
734
|
|
479
|
-
it
|
735
|
+
it 'returns the size of the result' do
|
480
736
|
expect(subject.size).to eq 2
|
481
737
|
end
|
482
738
|
|
@@ -489,7 +745,7 @@ describe ActiveTriples::Relation do
|
|
489
745
|
|
490
746
|
describe '#set' do
|
491
747
|
include_context 'with unregistered property'
|
492
|
-
|
748
|
+
|
493
749
|
it 'raises UndefinedPropertyError' do
|
494
750
|
expect { subject.set('x') }
|
495
751
|
.to raise_error ActiveTriples::UndefinedPropertyError
|
@@ -511,17 +767,47 @@ describe ActiveTriples::Relation do
|
|
511
767
|
.to change { subject.to_a }.to contain_exactly(*values)
|
512
768
|
end
|
513
769
|
|
770
|
+
context 'when given a Relation' do
|
771
|
+
before do
|
772
|
+
class DummySnowflake < RDF::Literal
|
773
|
+
DATATYPE = RDF::URI('http://example.com/snowflake').freeze
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
after { Object.send(:remove_const, :DummySnowflake) }
|
778
|
+
|
779
|
+
it 'keeps datatypes and languages of values' do
|
780
|
+
values = [Date.today,
|
781
|
+
'Moomin',
|
782
|
+
:moomin,
|
783
|
+
RDF::Literal("Moomin", language: :en),
|
784
|
+
RDF::Literal("Mummi", language: :fi),
|
785
|
+
RDF::Literal("Moomin", datatype: RDF::URI('custom')),
|
786
|
+
DummySnowflake.new('Moomin')]
|
787
|
+
|
788
|
+
subject.set(values)
|
789
|
+
|
790
|
+
values[6] = 'Moomin' # cast known datatypes
|
791
|
+
expect(subject.to_a).to contain_exactly(*values)
|
792
|
+
|
793
|
+
expect { subject.set(subject) }
|
794
|
+
.not_to change { subject.send(:objects).to_a }
|
795
|
+
end
|
796
|
+
end
|
797
|
+
|
514
798
|
context 'and persistence config' do
|
515
799
|
before do
|
516
800
|
reflections
|
517
|
-
.property :moomin,
|
518
|
-
predicate: RDF::URI('http://example.org/moomin'),
|
801
|
+
.property :moomin,
|
802
|
+
predicate: RDF::URI('http://example.org/moomin'),
|
519
803
|
persist_to: ActiveTriples::RepositoryStrategy
|
520
804
|
end
|
521
805
|
|
522
806
|
it 'returns values with persistence strategy set' do
|
523
807
|
expect(subject.set(RDF::Node.new).map(&:persistence_strategy))
|
524
|
-
.to contain_exactly(
|
808
|
+
.to contain_exactly(
|
809
|
+
an_instance_of(ActiveTriples::RepositoryStrategy)
|
810
|
+
)
|
525
811
|
end
|
526
812
|
end
|
527
813
|
end
|
@@ -542,70 +828,88 @@ describe ActiveTriples::Relation do
|
|
542
828
|
end
|
543
829
|
end
|
544
830
|
|
545
|
-
it
|
546
|
-
expect(subject.join(
|
547
|
-
|
548
|
-
|
831
|
+
it 'returns joined strings' do
|
832
|
+
expect(subject.join(', ')).to satisfy do |v|
|
833
|
+
v.split(', ').include?('Comet in Moominland') &&
|
834
|
+
v.split(', ').include?('Finn Family Moomintroll')
|
835
|
+
end
|
549
836
|
end
|
550
837
|
end
|
551
838
|
end
|
552
839
|
end
|
553
840
|
|
554
|
-
describe
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
841
|
+
describe '#valid_datatype?' do
|
842
|
+
before do
|
843
|
+
allow(subject.parent).to receive(:rdf_subject) { 'parent subject' }
|
844
|
+
end
|
845
|
+
|
846
|
+
subject { described_class.new(double('parent', reflections: []), 'value') }
|
847
|
+
|
848
|
+
context 'the value is not a Resource' do
|
849
|
+
it 'should be true if value is a String' do
|
850
|
+
expect(subject.send(:valid_datatype?, 'foo')).to be true
|
560
851
|
end
|
561
|
-
|
852
|
+
|
853
|
+
it 'should be true if value is a Symbol' do
|
562
854
|
expect(subject.send(:valid_datatype?, :foo)).to be true
|
563
855
|
end
|
564
|
-
|
565
|
-
|
856
|
+
|
857
|
+
it 'should be true if the value is a Numeric' do
|
858
|
+
expect(subject.send(:valid_datatype?, 1)).to be true
|
566
859
|
expect(subject.send(:valid_datatype?, 0.1)).to be true
|
567
860
|
end
|
568
|
-
|
861
|
+
|
862
|
+
it 'should be true if the value is a Date' do
|
569
863
|
expect(subject.send(:valid_datatype?, Date.today)).to be true
|
570
864
|
end
|
571
|
-
|
865
|
+
|
866
|
+
it 'should be true if the value is a Time' do
|
572
867
|
expect(subject.send(:valid_datatype?, Time.now)).to be true
|
573
868
|
end
|
574
|
-
|
869
|
+
|
870
|
+
it 'should be true if the value is a boolean' do
|
575
871
|
expect(subject.send(:valid_datatype?, false)).to be true
|
576
|
-
expect(subject.send(:valid_datatype?, true)).to
|
872
|
+
expect(subject.send(:valid_datatype?, true)).to be true
|
577
873
|
end
|
578
874
|
end
|
579
875
|
|
580
|
-
context
|
876
|
+
context 'the value is a Resource' do
|
581
877
|
after { Object.send(:remove_const, :DummyResource) }
|
878
|
+
|
582
879
|
let(:resource) { DummyResource.new }
|
583
|
-
|
880
|
+
|
881
|
+
context 'and the resource class does not include RDF::Isomorphic' do
|
584
882
|
before { class DummyResource; include ActiveTriples::RDFSource; end }
|
585
|
-
|
883
|
+
|
884
|
+
it 'should be false' do
|
586
885
|
expect(subject.send(:valid_datatype?, resource)).to be false
|
587
886
|
end
|
588
887
|
end
|
589
|
-
|
888
|
+
|
889
|
+
context 'and the resource class includes RDF:Isomorphic' do
|
590
890
|
before do
|
591
891
|
class DummyResource
|
592
892
|
include ActiveTriples::RDFSource
|
593
893
|
include RDF::Isomorphic
|
594
894
|
end
|
595
895
|
end
|
596
|
-
|
896
|
+
|
897
|
+
it 'should be false' do
|
597
898
|
expect(subject.send(:valid_datatype?, resource)).to be false
|
598
899
|
end
|
599
900
|
end
|
600
|
-
|
901
|
+
|
902
|
+
context 'and aliases #== to #isomorphic_with?' do
|
601
903
|
before do
|
602
904
|
class DummyResource
|
603
905
|
include ActiveTriples::RDFSource
|
604
906
|
include RDF::Isomorphic
|
605
|
-
|
907
|
+
|
908
|
+
alias == isomorphic_with?
|
606
909
|
end
|
607
910
|
end
|
608
|
-
|
911
|
+
|
912
|
+
it 'should be false' do
|
609
913
|
expect(subject.send(:valid_datatype?, resource)).to be false
|
610
914
|
end
|
611
915
|
end
|