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
@@ -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
|