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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/CHANGES.md +17 -11
  4. data/README.md +72 -39
  5. data/lib/active_triples/configurable.rb +6 -1
  6. data/lib/active_triples/list.rb +1 -4
  7. data/lib/active_triples/nested_attributes.rb +10 -7
  8. data/lib/active_triples/persistable.rb +13 -0
  9. data/lib/active_triples/persistence_strategies/parent_strategy.rb +47 -34
  10. data/lib/active_triples/persistence_strategies/persistence_strategy.rb +14 -1
  11. data/lib/active_triples/properties.rb +19 -4
  12. data/lib/active_triples/property_builder.rb +4 -4
  13. data/lib/active_triples/rdf_source.rb +142 -189
  14. data/lib/active_triples/relation.rb +307 -156
  15. data/lib/active_triples/util/buffered_transaction.rb +126 -0
  16. data/lib/active_triples/util/extended_bounded_description.rb +75 -0
  17. data/lib/active_triples/version.rb +1 -1
  18. data/spec/active_triples/configurable_spec.rb +35 -7
  19. data/spec/active_triples/identifiable_spec.rb +19 -6
  20. data/spec/active_triples/list_spec.rb +15 -7
  21. data/spec/active_triples/nested_attributes_spec.rb +12 -10
  22. data/spec/active_triples/persistable_spec.rb +0 -4
  23. data/spec/active_triples/persistence_strategies/parent_strategy_spec.rb +57 -10
  24. data/spec/active_triples/rdf_source_spec.rb +137 -97
  25. data/spec/active_triples/relation_spec.rb +436 -132
  26. data/spec/active_triples/resource_spec.rb +8 -23
  27. data/spec/active_triples/util/buffered_transaction_spec.rb +187 -0
  28. data/spec/active_triples/util/extended_bounded_description_spec.rb +98 -0
  29. data/spec/integration/reciprocal_properties_spec.rb +10 -10
  30. data/spec/support/matchers.rb +13 -1
  31. 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("parent resource", reflections: {}) }
7
- let(:value_args) { double("value args", last: {}) }
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, value_args) }
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
- let(:reflections) do
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
- let(:property) { :moomin }
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
- .to contain_exactly(*values[1..-1])
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
- .to contain_exactly(*values)
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
- .to contain_exactly(*values)
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
- .to contain_exactly(*values)
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 }.to change { subject.result }
217
- .from(['moomin']).to([])
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 "#predicate" do
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 "#property" do
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 '#first_or_create' do
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.result).to contain_exactly()
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.result).to contain_exactly()
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) { RDF::Node.new }
621
+ let(:node) { RDF::Node.new }
355
622
 
356
623
  it 'contain values' do
357
- expect(subject.result).to contain_exactly(*values)
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.result)
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.result.map(&:rdf_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.result.each do |node|
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
- 'moomin', Date.today]
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.result).to contain_exactly(*values)
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 "#rdf_subject" do
421
- let(:parent_resource) { double("parent resource", reflections: {}) }
676
+ describe '#rdf_subject' do
677
+ let(:parent_resource) { double('parent resource', reflections: {}) }
422
678
 
423
- subject { described_class.new(parent_resource, double("value args") ) }
679
+ subject { described_class.new(parent_resource, double('value args')) }
424
680
 
425
- context "when relation has 0 value arguments" do
681
+ context 'when relation has 0 value arguments' do
426
682
  before { subject.value_arguments = double(length: 0) }
427
683
 
428
- it "should raise an error" do
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 "when term has 1 value argument" do
689
+ context 'when term has 1 value argument' do
434
690
  before do
435
- allow(subject.parent).to receive(:rdf_subject) { "parent 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) ).to eq "parent subject"
696
+ expect(subject.send(:rdf_subject)).to eq 'parent subject'
441
697
  end
442
698
 
443
- it " is a private method" do
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 "when relation has 2 value arguments" do
449
- before { subject.value_arguments = double(length: 2, first: "first") }
704
+ context 'when relation has 2 value arguments' do
705
+ before { subject.value_arguments = double(length: 2, first: 'first') }
450
706
 
451
- it "should return the first value argument" do
452
- expect(subject.send(:rdf_subject) ).to eq "first"
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 "when relation has 3 value arguments" do
712
+ context 'when relation has 3 value arguments' do
457
713
  before { subject.value_arguments = double(length: 3) }
458
714
 
459
- it "should raise an error" do
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 "returns the size of the result" do
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(an_instance_of(ActiveTriples::RepositoryStrategy))
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 "returns joined strings" do
546
- expect(subject.join(", "))
547
- .to satisfy { |v| sp = v.split(', ').include?("Comet in Moominland") &&
548
- v.split(', ').include?("Finn Family Moomintroll") }
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 "#valid_datatype?" do
555
- subject { described_class.new(double("parent", reflections: []), "value" ) }
556
- before { allow(subject.parent).to receive(:rdf_subject) { "parent subject" } }
557
- context "the value is not a Resource" do
558
- it "should be true if value is a String" do
559
- expect(subject.send(:valid_datatype?, "foo")).to be true
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
- it "should be true if value is a Symbol" do
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
- it "should be true if the value is a Numeric" do
565
- expect(subject.send(:valid_datatype?, 1)).to be true
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
- it "should be true if the value is a Date" do
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
- it "should be true if the value is a Time" do
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
- it "should be true if the value is a boolean" do
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 be true
872
+ expect(subject.send(:valid_datatype?, true)).to be true
577
873
  end
578
874
  end
579
875
 
580
- context "the value is a Resource" do
876
+ context 'the value is a Resource' do
581
877
  after { Object.send(:remove_const, :DummyResource) }
878
+
582
879
  let(:resource) { DummyResource.new }
583
- context "and the resource class does not include RDF::Isomorphic" do
880
+
881
+ context 'and the resource class does not include RDF::Isomorphic' do
584
882
  before { class DummyResource; include ActiveTriples::RDFSource; end }
585
- it "should be false" do
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
- context "and the resource class includes RDF:Isomorphic" do
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
- it "should be false" do
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
- context "and the resource class includes RDF::Isomorphic and aliases :== to :isomorphic_with?" do
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
- alias_method :==, :isomorphic_with?
907
+
908
+ alias == isomorphic_with?
606
909
  end
607
910
  end
608
- it "should be false" do
911
+
912
+ it 'should be false' do
609
913
  expect(subject.send(:valid_datatype?, resource)).to be false
610
914
  end
611
915
  end