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