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
@@ -41,14 +41,12 @@ describe ActiveTriples::RDFSource do
41
41
  it { is_expected.to be_queryable }
42
42
  it { is_expected.to be_countable }
43
43
  it { is_expected.to be_a_value }
44
- # it { is_expected.to be_a_term }
45
- # it { is_expected.to be_a_resource }
46
44
 
47
45
  let(:enumerable) { source_class.new }
48
46
  it_behaves_like 'an RDF::Enumerable'
49
47
 
50
48
  let(:queryable) { enumerable }
51
- # it_behaves_like 'an RDF::Queryable'
49
+ it_behaves_like 'an RDF::Queryable'
52
50
 
53
51
  let(:countable) { enumerable }
54
52
  it_behaves_like 'an RDF::Countable'
@@ -152,7 +150,7 @@ describe ActiveTriples::RDFSource do
152
150
  subject.creator = 'moomin'
153
151
  expect(subject.attributes['creator']).to contain_exactly 'moomin'
154
152
  end
155
-
153
+
156
154
  it 'has unregistered properties' do
157
155
  predicate = RDF::OWL.sameAs
158
156
  subject << [subject, predicate, 'moomin']
@@ -174,16 +172,25 @@ describe ActiveTriples::RDFSource do
174
172
 
175
173
  context 'with statements for other subjects' do
176
174
  before do
177
- subject << RDF::Statement(RDF::URI('http://example.org/OTHER_SUBJECT'), RDF::URI('http://example.org/ontology/OTHER_PRED'), 'OTHER_OBJECT')
175
+ subject <<
176
+ RDF::Statement(RDF::URI('http://example.org/OTHER_SUBJECT'),
177
+ RDF::URI('http://example.org/ontology/OTHER_PRED'),
178
+ 'OTHER_OBJECT')
178
179
  end
179
180
 
180
- it "should not include predicate-values for statements with rdf_subject != this object's rdf_subject" do
181
+ it 'excludes values for statements not matching rdf_subject' do
181
182
  expect(subject.attributes.keys).not_to include 'http://example.org/ontology/OTHER_PRED'
182
183
  expect(subject.attributes.values).not_to include ['OTHER_OBJECT']
183
184
  end
184
185
  end
185
186
  end
186
187
 
188
+ describe '#attributes=' do
189
+ it 'raises an error when not passed a hash' do
190
+ expect { subject.attributes = true }.to raise_error ArgumentError
191
+ end
192
+ end
193
+
187
194
  describe '#fetch' do
188
195
  it 'raises an error when it is a node' do
189
196
  expect { subject.fetch }
@@ -195,7 +202,7 @@ describe ActiveTriples::RDFSource do
195
202
  subject { source_class.new(uri) }
196
203
 
197
204
  context 'with a bad link' do
198
- before { stub_request(:get, uri).to_return(:status => 404) }
205
+ before { stub_request(:get, uri).to_return(status: 404) }
199
206
 
200
207
  it 'raises an error if no block is given' do
201
208
  expect { subject.fetch }.to raise_error IOError
@@ -208,8 +215,7 @@ describe ActiveTriples::RDFSource do
208
215
 
209
216
  context 'with a working link' do
210
217
  before do
211
- stub_request(:get, uri).to_return(:status => 200,
212
- :body => graph.dump(:ttl))
218
+ stub_request(:get, uri).to_return(status: 200, body: graph.dump(:ttl))
213
219
  end
214
220
 
215
221
  let(:graph) { RDF::Graph.new << statement }
@@ -221,8 +227,8 @@ describe ActiveTriples::RDFSource do
221
227
  it 'loads retrieved graph into its own' do
222
228
  expect { subject.fetch }
223
229
  .to change { subject.statements.to_a }
224
- .from(a_collection_containing_exactly())
225
- .to(a_collection_containing_exactly(statement))
230
+ .from(a_collection_containing_exactly)
231
+ .to(a_collection_containing_exactly(statement))
226
232
  end
227
233
 
228
234
  it 'merges retrieved graph into its own' do
@@ -231,15 +237,18 @@ describe ActiveTriples::RDFSource do
231
237
 
232
238
  expect { subject.fetch }
233
239
  .to change { subject.statements.to_a }
234
- .from(a_collection_containing_exactly(existing))
235
- .to(a_collection_containing_exactly(statement, existing))
240
+ .from(a_collection_containing_exactly(existing))
241
+ .to(a_collection_containing_exactly(statement, existing))
236
242
  end
237
243
 
238
- it "passes extra arguments to RDF::Reader" do
239
- expect(RDF::Reader).to receive(:open).with(subject.rdf_subject,
240
- { base_uri: subject.rdf_subject,
241
- headers: { Accept: 'x-humans/as-they-are' } })
242
- subject.fetch(headers: { Accept: 'x-humans/as-they-are' })
244
+ it 'passes extra arguments to RDF::Reader' do
245
+ mime = 'x-humans/as-they-are'
246
+
247
+ expect(RDF::Reader)
248
+ .to receive(:open).with(subject.rdf_subject,
249
+ base_uri: subject.rdf_subject,
250
+ headers: { Accept: mime })
251
+ subject.fetch(headers: { Accept: mime })
243
252
  end
244
253
  end
245
254
  end
@@ -309,14 +318,14 @@ describe ActiveTriples::RDFSource do
309
318
  context 'with no matching property' do
310
319
  it 'is empty' do
311
320
  expect(subject.get_values(:not_a_predicate))
312
- .to be_a_relation_containing()
321
+ .to be_a_relation_containing
313
322
  end
314
323
  end
315
324
 
316
325
  context 'with an empty predicate' do
317
326
  it 'is empty' do
318
327
  expect(subject.get_values(RDF::URI('http://example.org/empty')))
319
- .to be_a_relation_containing()
328
+ .to be_a_relation_containing
320
329
  end
321
330
  end
322
331
 
@@ -348,24 +357,24 @@ describe ActiveTriples::RDFSource do
348
357
 
349
358
  context 'when given an unregistered property name' do
350
359
  it 'raises an error' do
351
- expect { subject.set_value(:not_a_property, '') }.to raise_error do |err|
352
- expect(err).to be_a ActiveTriples::UndefinedPropertyError
353
- expect(err.klass).to eq subject.class
354
- expect(err.property).to eq :not_a_property
360
+ expect { subject.set_value(:not_a_property, '') }.to raise_error do |e|
361
+ expect(e).to be_a ActiveTriples::UndefinedPropertyError
362
+ expect(e.klass).to eq subject.class
363
+ expect(e.property).to eq :not_a_property
355
364
  end
356
365
  end
357
-
366
+
358
367
  it 'is a no-op' do
359
368
  subject << RDF::Statement(subject, RDF::Vocab::DC.title, 'Moomin')
360
369
  expect { subject.set_value(:not_a_property, '') rescue nil }
361
370
  .not_to change { subject.triples.to_a }
362
371
  end
363
372
  end
364
-
373
+
365
374
  shared_examples 'setting values' do
366
375
  include_context 'with properties'
367
376
  after do
368
- Object.send(:remove_const, 'SourceWithCreator') if
377
+ Object.send(:remove_const, 'SourceWithCreator') if
369
378
  defined? SourceWithCreator
370
379
  end
371
380
 
@@ -381,26 +390,26 @@ describe ActiveTriples::RDFSource do
381
390
  it 'sets a value' do
382
391
  expect { subject.set_value(predicate, value) }
383
392
  .to change { subject.statements }
384
- .to(a_collection_containing_exactly(*statements))
393
+ .to(a_collection_containing_exactly(*statements))
385
394
  end
386
395
 
387
396
  it 'sets a value with a property name' do
388
397
  expect { subject.set_value(property_name, value) }
389
398
  .to change { subject.statements }
390
- .to(a_collection_containing_exactly(*statements))
399
+ .to(a_collection_containing_exactly(*statements))
391
400
  end
392
401
 
393
402
  it 'overwrites existing values' do
394
- old_vals = ['old value',
395
- RDF::Node.new,
396
- RDF::Vocab::DC.type,
403
+ old_vals = ['old value',
404
+ RDF::Node.new,
405
+ RDF::Vocab::DC.type,
397
406
  RDF::URI('----')]
398
407
 
399
408
  subject.set_value(predicate, old_vals)
400
409
 
401
410
  expect { subject.set_value(predicate, value) }
402
411
  .to change { subject.statements }
403
- .to(a_collection_containing_exactly(*statements))
412
+ .to(a_collection_containing_exactly(*statements))
404
413
  end
405
414
 
406
415
  it 'returns the set values in a Relation' do
@@ -408,7 +417,7 @@ describe ActiveTriples::RDFSource do
408
417
  .to be_a_relation_containing(*Array.wrap(value))
409
418
  end
410
419
  end
411
-
420
+
412
421
  context 'with string literal' do
413
422
  include_examples 'setting values' do
414
423
  let(:value) { 'moomin' }
@@ -417,7 +426,7 @@ describe ActiveTriples::RDFSource do
417
426
 
418
427
  context 'with multiple values' do
419
428
  include_examples 'setting values' do
420
- let(:value) { ['moominpapa', 'moominmama'] }
429
+ let(:value) { %w('moominpapa moominmama') }
421
430
  end
422
431
  end
423
432
 
@@ -454,11 +463,11 @@ describe ActiveTriples::RDFSource do
454
463
  context 'with mixed values' do
455
464
  include_examples 'setting values' do
456
465
  let(:value) do
457
- ['moomin',
458
- Date.today,
459
- RDF::Node.new,
460
- source_class.new,
461
- source_class.new(uri),
466
+ ['moomin',
467
+ Date.today,
468
+ RDF::Node.new,
469
+ source_class.new,
470
+ source_class.new(uri),
462
471
  subject]
463
472
  end
464
473
  end
@@ -473,23 +482,23 @@ describe ActiveTriples::RDFSource do
473
482
  person.set_value(RDF::Vocab::FOAF.publications, document)
474
483
 
475
484
  expect(person.get_values(RDF::Vocab::FOAF.publications))
476
- .to contain_exactly(document)
485
+ .to be_a_relation_containing(document)
477
486
  expect(document.get_values(RDF::Vocab::DC.creator))
478
- .to contain_exactly(person)
487
+ .to be_a_relation_containing(person)
479
488
  end
480
489
 
481
490
  it 'handles setting' do
482
- document.set_value(RDF::Vocab::DC.creator, person)
483
- person.set_value(RDF::Vocab::FOAF.knows, subject)
484
- subject.set_value(RDF::Vocab::FOAF.publications, document)
485
- subject.set_value(RDF::OWL.sameAs, subject)
491
+ document.set_value(RDF::Vocab::DC.creator, person)
492
+ person.set_value(RDF::Vocab::FOAF.knows, subject)
493
+ subject.set_value(RDF::Vocab::FOAF.publications, document)
494
+ subject.set_value(RDF::OWL.sameAs, subject)
486
495
 
487
496
  expect(subject.get_values(RDF::Vocab::FOAF.publications))
488
- .to contain_exactly(document)
497
+ .to be_a_relation_containing(document)
489
498
  expect(subject.get_values(RDF::OWL.sameAs))
490
- .to contain_exactly(subject)
499
+ .to be_a_relation_containing(subject)
491
500
  expect(document.get_values(RDF::Vocab::DC.creator))
492
- .to contain_exactly(person)
501
+ .to be_a_relation_containing(person)
493
502
  end
494
503
 
495
504
  it 'handles setting circularly' do
@@ -497,9 +506,9 @@ describe ActiveTriples::RDFSource do
497
506
  person.set_value(RDF::Vocab::FOAF.knows, subject)
498
507
 
499
508
  expect(document.get_values(RDF::Vocab::DC.creator))
500
- .to contain_exactly(person, subject)
509
+ .to be_a_relation_containing(person, subject)
501
510
  expect(person.get_values(RDF::Vocab::FOAF.knows))
502
- .to contain_exactly subject
511
+ .to be_a_relation_containing subject
503
512
  end
504
513
 
505
514
  it 'handles setting circularly within ancestor list' do
@@ -508,36 +517,53 @@ describe ActiveTriples::RDFSource do
508
517
  document.set_value(RDF::Vocab::DC.relation, person)
509
518
  person.set_value(RDF::Vocab::DC.relation, person2)
510
519
  person2.set_value(RDF::Vocab::DC.relation, document)
511
-
520
+
512
521
  expect(person.get_values(RDF::Vocab::DC.relation))
513
- .to contain_exactly person2
522
+ .to be_a_relation_containing person2
514
523
  expect(person2.get_values(RDF::Vocab::DC.relation))
515
- .to contain_exactly document
524
+ .to be_a_relation_containing document
516
525
  end
517
526
  end
518
527
 
519
528
  describe 'capturing child nodes' do
520
- let(:other) { source_class.new }
529
+ let(:other) { source_class.new }
530
+ let(:predicate) { RDF::OWL.sameAs }
521
531
 
522
532
  it 'adds child node data to own graph' do
523
533
  other << RDF::Statement(:s, RDF::URI('p'), 'o')
524
- expect { subject.set_value(RDF::OWL.sameAs, other) }
525
- .to change { subject.statements.to_a }.to include(*other.statements.to_a)
534
+
535
+ expect { subject.set_value(predicate, other) }
536
+ .to change { subject.statements.to_a }
537
+ .to include(*other.statements.to_a)
526
538
  end
527
539
 
528
540
  it 'does not change persistence strategy of added node' do
529
- expect { subject.set_value(RDF::OWL.sameAs, other) }
541
+ expect { subject.set_value(predicate, other) }
530
542
  .not_to change { other.persistence_strategy }
531
543
  end
532
-
544
+
533
545
  it 'does not capture a child node when it already persists to a parent' do
534
546
  third = source_class.new
535
- third.set_value(RDF::OWL.sameAs, other)
536
-
537
- child_other = third.get_values(RDF::OWL.sameAs).first
538
- expect { subject.set_value(RDF::OWL.sameAs, child_other) }
547
+ third.set_value(predicate, other)
548
+
549
+ child_other = third.get_values(predicate).first
550
+ expect { subject.set_value(predicate, child_other) }
539
551
  .not_to change { child_other.persistence_strategy.parent }
540
552
  end
553
+
554
+ context 'when setting to a relation' do
555
+ it 'adds child node data to graph' do
556
+ other << RDF::Statement(other, RDF::URI('p'), 'o')
557
+
558
+ relation_source = source_class.new
559
+ relation_source.set_value(predicate, other)
560
+ relation = relation_source.get_values(predicate)
561
+
562
+ expect { subject.set_value(predicate, relation) }
563
+ .to change { subject.statements.to_a }
564
+ .to include(*other.statements.to_a)
565
+ end
566
+ end
541
567
  end
542
568
  end
543
569
 
@@ -578,8 +604,8 @@ describe ActiveTriples::RDFSource do
578
604
  it 'adds error message' do
579
605
  expect { subject.valid? }
580
606
  .to change { subject.errors.messages }
581
- .from({})
582
- .to({ base: ["The underlying graph must be valid"] })
607
+ .from({})
608
+ .to(base: ['The underlying graph must be valid'])
583
609
  end
584
610
  end
585
611
 
@@ -604,8 +630,8 @@ describe ActiveTriples::RDFSource do
604
630
  it 'has errors' do
605
631
  expect { subject.valid? }
606
632
  .to change { subject.errors.messages }
607
- .from({})
608
- .to({ title: ["can't be blank"] })
633
+ .from({})
634
+ .to(title: ["can't be blank"])
609
635
  end
610
636
  end
611
637
 
@@ -622,45 +648,53 @@ describe ActiveTriples::RDFSource do
622
648
  it 'has errors' do
623
649
  expect { subject.valid? }
624
650
  .to change { subject.errors.messages }
625
- .from({})
626
- .to(include({ base: ["The underlying graph must be valid"] }))
651
+ .from({})
652
+ .to(include(base: ['The underlying graph must be valid']))
627
653
  end
628
654
  end
629
655
  end
630
656
  end
631
657
  end
632
658
 
633
- describe ".apply_schema" do
659
+ describe '.apply_schema' do
634
660
  let(:dummy_source) { Class.new { include ActiveTriples::RDFSource } }
635
661
 
636
662
  before do
637
663
  class MyDataModel < ActiveTriples::Schema
638
- property :test_title, :predicate => RDF::Vocab::DC.title
664
+ property :test_title, predicate: RDF::Vocab::DC.title
639
665
  end
640
666
  end
641
667
 
642
- after do
643
- Object.send(:remove_const, "MyDataModel")
644
- end
645
- it "should apply the schema" do
668
+ after { Object.send(:remove_const, 'MyDataModel') }
669
+
670
+ it 'should apply the schema' do
646
671
  dummy_source.apply_schema MyDataModel
647
672
 
648
- expect{dummy_source.new.test_title}.not_to raise_error
673
+ expect { dummy_source.new.test_title }.not_to raise_error
649
674
  end
650
675
  end
651
676
 
652
677
  describe 'sources with properties with class_name defined' do
653
678
  before(:context) do
654
679
  class DummyChapter < ActiveTriples::Resource
655
- configure repository: :default, type: RDF::URI('http://www.example.com/type/Chapter')
656
- property :title, predicate: RDF::URI('http://www.example.com/ontology/title')
657
- property :subtitle, predicate: RDF::URI('http://www.example.com/ontology/subtitle')
680
+ ontology = RDF::URI('http://www.example.com/ontology')
681
+ type = RDF::URI('http://www.example.com/type')
682
+
683
+ configure repository: :default, type: type / 'Chapter'
684
+
685
+ property :title, predicate: ontology / 'title'
686
+ property :subtitle, predicate: ontology / 'subtitle'
658
687
  end
659
688
 
660
689
  class DummyBook < ActiveTriples::Resource
661
- configure repository: :default, type: RDF::URI('http://www.example.com/type/Book')
662
- property :title, predicate: RDF::URI('http://www.example.com/ontology/title')
663
- property :has_chapter, predicate: RDF::URI('http://www.example.com/ontology/hasChapter'), class_name: DummyChapter # Explicit Link
690
+ ontology = RDF::URI('http://www.example.com/ontology')
691
+ type = RDF::URI('http://www.example.com/type')
692
+
693
+ configure repository: :default, type: type / 'Book'
694
+
695
+ property :title, predicate: ontology / 'title'
696
+ property :has_chapter, predicate: ontology / 'hasChapter',
697
+ class_name: DummyChapter
664
698
  end
665
699
  end
666
700
 
@@ -673,11 +707,11 @@ describe ActiveTriples::RDFSource do
673
707
  @book_title = 'Example Book.'
674
708
  @chapter_title = 'Chapter 1'
675
709
  ttl = "<#{@book_url}> a <http://www.example.com/type/Book>;
676
- <http://www.example.com/ontology/hasChapter> [
677
- a <http://www.example.com/type/Chapter>;
678
- <http://www.example.com/ontology/title> \"#{@chapter_title}\"
679
- ];
680
- <http://www.example.com/ontology/title> \"#{@book_title}\" ."
710
+ <http://www.example.com/ontology/hasChapter> [
711
+ a <http://www.example.com/type/Chapter>;
712
+ <http://www.example.com/ontology/title> \"#{@chapter_title}\"
713
+ ];
714
+ <http://www.example.com/ontology/title> \"#{@book_title}\" ."
681
715
  book_graph = ::RDF::Graph.new.from_ttl ttl
682
716
  r = ActiveTriples::Repositories.repositories[:default]
683
717
  r << book_graph
@@ -688,7 +722,8 @@ describe ActiveTriples::RDFSource do
688
722
  it 'populates DummyBook properly' do
689
723
  expect(@book.rdf_subject.to_s).to eq @book_url
690
724
  expect(@book).to be_a DummyBook
691
- expect(@book.type).to include(RDF::URI.new('http://www.example.com/type/Book'))
725
+ expect(@book.type)
726
+ .to include(RDF::URI.new('http://www.example.com/type/Book'))
692
727
  expect(@book.title.first).to eq @book_title
693
728
  end
694
729
 
@@ -696,11 +731,11 @@ describe ActiveTriples::RDFSource do
696
731
  chapter = @book.has_chapter.first
697
732
  expect(chapter).to be_a DummyChapter
698
733
  expect(chapter.title.first).to eq @chapter_title
699
- expect(chapter.type).to include(RDF::URI.new('http://www.example.com/type/Chapter'))
734
+ expect(chapter.type)
735
+ .to include(RDF::URI.new('http://www.example.com/type/Chapter'))
700
736
  end
701
737
  end
702
738
 
703
-
704
739
  context 'when loading models through properties' do
705
740
  before(:context) do
706
741
  r = RDF::Repository.new
@@ -720,20 +755,25 @@ describe ActiveTriples::RDFSource do
720
755
  end
721
756
 
722
757
  it 'populates DummyBook (resumed resource) properly' do
723
- expect(@bk1.type.first).to eq RDF::URI('http://www.example.com/type/Book')
724
- expect(@bk1.title.first).to eq 'Learning about Explicit Links in ActiveTriples'
758
+ expect(@bk1.type.first)
759
+ .to eq RDF::URI('http://www.example.com/type/Book')
760
+ expect(@bk1.title.first)
761
+ .to eq 'Learning about Explicit Links in ActiveTriples'
725
762
  end
726
763
 
727
-
728
764
  it 'populates DummyChapter (property resource) properly' do
729
765
  ch1 = @bk1.has_chapter.first
730
- expect(ch1.type.first).to eq RDF::URI('http://www.example.com/type/Chapter')
731
- expect(ch1.title.first).to eq 'Defining a source with an Explicit Link'
766
+ expect(ch1.type.first)
767
+ .to eq RDF::URI('http://www.example.com/type/Chapter')
768
+ expect(ch1.title.first)
769
+ .to eq 'Defining a source with an Explicit Link'
732
770
  end
733
771
 
734
772
  it 'populates DummyChapter (directly from repository) properly' do
735
- expect(@ch1.type.first).to eq RDF::URI('http://www.example.com/type/Chapter')
736
- expect(@ch1.title.first).to eq 'Defining a source with an Explicit Link'
773
+ expect(@ch1.type.first)
774
+ .to eq RDF::URI('http://www.example.com/type/Chapter')
775
+ expect(@ch1.title.first)
776
+ .to eq 'Defining a source with an Explicit Link'
737
777
  end
738
778
 
739
779
  it 'does not reload from repository twice' do