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