active-triples 0.11.0 → 1.0.0.rc1

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.
@@ -203,6 +203,8 @@ describe ActiveTriples::Relation do
203
203
 
204
204
  types = { numeric: [0, 1, 2, 3_000_000_000],
205
205
  string: ['moomin', 'snork', 'snufkin'],
206
+ lang: [RDF::Literal('Moomin', language: :en),
207
+ RDF::Literal('Mummi', language: :fi)],
206
208
  date: [Date.today, Date.today - 1],
207
209
  uri: [RDF::URI('one'), RDF::URI('two'), RDF::URI('three')],
208
210
  node: [RDF::Node.new, RDF::Node.new],
@@ -239,6 +241,16 @@ describe ActiveTriples::Relation do
239
241
  other << 'extra'
240
242
  expect(subject <=> other).to be_nil
241
243
  end
244
+
245
+ it "gives nil when other contains a subset by language" do
246
+ subject << RDF::Literal("Moomin", language: :aa)
247
+ expect(subject <=> other).to be_nil
248
+ end
249
+
250
+ it "gives nil when other contains a superset by language" do
251
+ other << RDF::Literal("Moomin", language: :aa)
252
+ expect(subject <=> other).to be_nil
253
+ end
242
254
  end
243
255
  end
244
256
 
@@ -274,7 +286,7 @@ describe ActiveTriples::Relation do
274
286
  end
275
287
 
276
288
  it 'adds new child node to relation' do
277
- expect { subject.build }.to change { subject.count }.by(1)
289
+ expect { subject.build }.to change { subject.count }.by 1
278
290
  end
279
291
 
280
292
  it 'builds child as new blank node by default' do
@@ -286,6 +298,24 @@ describe ActiveTriples::Relation do
286
298
  expect(subject.build(id: uri)).to be_uri
287
299
  end
288
300
 
301
+ context 'when cast is false' do
302
+ include_context 'with symbol property' do
303
+ before do
304
+ reflections.property :moomin,
305
+ cast: false,
306
+ predicate: RDF::Vocab::DC.relation
307
+ end
308
+ end
309
+
310
+ it 'still returns as a resource' do
311
+ expect(subject.build).to be_a ActiveTriples::RDFSource
312
+ end
313
+
314
+ it 'adds a new child node to the relation' do
315
+ expect { subject.build }.to change { subject.count }.by 1
316
+ end
317
+ end
318
+
289
319
  context 'with configured properties' do
290
320
  include_context 'with symbol property' do
291
321
  before do
@@ -320,6 +350,15 @@ describe ActiveTriples::Relation do
320
350
  expect { subject.delete(1) }.not_to change { subject.to_a }
321
351
  end
322
352
 
353
+ it 'does not notify observers when no changes are made' do
354
+ observer = double(:observer)
355
+ parent_resource.add_observer(observer)
356
+
357
+ expect(observer).not_to receive(:notify)
358
+
359
+ subject.delete(1)
360
+ end
361
+
323
362
  context 'with values' do
324
363
  before { subject << values }
325
364
 
@@ -337,6 +376,17 @@ describe ActiveTriples::Relation do
337
376
  .to contain_exactly(*values[1..-1])
338
377
  end
339
378
 
379
+ it 'notifies observers of changes' do
380
+ observer = double(:observer)
381
+ parent_resource.add_observer(observer)
382
+
383
+ expect(observer).to receive(:notify)
384
+ .with(subject.property,
385
+ a_collection_containing_exactly(*values[1..-1]))
386
+
387
+ subject.delete(values.first)
388
+ end
389
+
340
390
  it 'deletes a URI value' do
341
391
  values.delete(uri)
342
392
  expect { subject.delete(uri) }
@@ -364,11 +414,19 @@ describe ActiveTriples::Relation do
364
414
  include_context 'with symbol property'
365
415
 
366
416
  let(:parent_resource) { ActiveTriples::Resource.new }
417
+ let(:observer) { double(:observer) }
367
418
 
368
419
  it 'gives nil for non-existant value' do
369
420
  expect(subject.delete?(1)).to be_nil
370
421
  end
371
422
 
423
+ it 'does not notify observers when no changes are made' do
424
+ parent_resource.add_observer(observer)
425
+
426
+ expect(observer).not_to receive(:notify)
427
+ subject.delete?(1)
428
+ end
429
+
372
430
  it 'returns value when deleted' do
373
431
  subject.set(1)
374
432
  expect(subject.delete?(1)).to eq 1
@@ -379,12 +437,23 @@ describe ActiveTriples::Relation do
379
437
  expect { subject.delete?(1) }
380
438
  .to change { subject.to_a }.to be_empty
381
439
  end
440
+
441
+ it 'notifies observers of changes' do
442
+ subject.set(1)
443
+ parent_resource.add_observer(observer)
444
+
445
+ expect(observer).to receive(:notify)
446
+ .with(subject.property, be_empty)
447
+
448
+ subject.delete?(1)
449
+ end
382
450
  end
383
451
 
384
452
  describe '#subtract' do
385
453
  include_context 'with symbol property'
386
454
 
387
455
  let(:parent_resource) { ActiveTriples::Resource.new }
456
+ let(:observer) { double(:observer) }
388
457
 
389
458
  it 'subtracts values as arguments' do
390
459
  subject.set([1, 2, 3])
@@ -403,6 +472,16 @@ describe ActiveTriples::Relation do
403
472
  expect { subject.subtract([:two, :three]) }
404
473
  .to change { subject.to_a }.to contain_exactly(:one)
405
474
  end
475
+
476
+ it 'notifies observers of changes' do
477
+ subject.set([1, 2, 3])
478
+ parent_resource.add_observer(observer)
479
+
480
+ expect(observer).to receive(:notify)
481
+ .with(subject.property, a_collection_containing_exactly(1))
482
+
483
+ subject.subtract([2, 3])
484
+ end
406
485
  end
407
486
 
408
487
  describe '#swap' do
@@ -450,12 +529,32 @@ describe ActiveTriples::Relation do
450
529
  expect { subject.clear }
451
530
  .to change { subject.parent.query(query_pattern) }.to([])
452
531
  end
532
+
533
+ it 'notifies observers of changed state' do
534
+ observer = double(:observer)
535
+ parent_resource.add_observer(observer)
536
+
537
+ expect(observer)
538
+ .to receive(:notify)
539
+ .with(subject.property, be_empty)
540
+
541
+ subject.clear
542
+ end
453
543
  end
454
544
 
455
545
  it 'is a no-op when relation is empty' do
456
546
  subject.parent << [subject.parent.rdf_subject, RDF.type, 'moomin']
457
547
  expect { subject.clear }.not_to change { subject.parent.statements.to_a }
458
548
  end
549
+
550
+ it 'does not notify observers when relation is empty' do
551
+ observer = double(:observer)
552
+ parent_resource.add_observer(observer)
553
+
554
+ expect(observer).not_to receive(:notify)
555
+
556
+ subject.clear
557
+ end
459
558
  end
460
559
 
461
560
  describe '#<<' do
@@ -474,6 +573,19 @@ describe ActiveTriples::Relation do
474
573
  .to change { subject.to_a }.to contain_exactly(*values)
475
574
  end
476
575
 
576
+ it 'notifies observers on the parent' do
577
+ observer = double(:observer)
578
+ values = [:moomin, :snork]
579
+ parent_resource.add_observer(observer)
580
+
581
+ expect(observer)
582
+ .to receive(:notify)
583
+ .with(subject.property,
584
+ a_collection_containing_exactly(*values))
585
+
586
+ subject << values
587
+ end
588
+
477
589
  it 'keeps datatypes' do
478
590
  values = [RDF::Literal(Date.today), RDF::Literal(:moomin)]
479
591
 
@@ -506,8 +618,8 @@ describe ActiveTriples::Relation do
506
618
  end
507
619
 
508
620
  it 'retains unknown datatypes' do
509
- literal =
510
- RDF::Literal('snowflake',
621
+ literal =
622
+ RDF::Literal('snowflake',
511
623
  datatype: RDF::URI('http://emaple.com/snowflake'))
512
624
 
513
625
  subject << literal
@@ -531,7 +643,7 @@ describe ActiveTriples::Relation do
531
643
  literal = DummySnowflake.new('special')
532
644
 
533
645
  subject << literal
534
-
646
+
535
647
  expect { subject << 'special' }
536
648
  .to change { subject.send(:objects).to_a }
537
649
  .to contain_exactly(literal, RDF::Literal('special'))
@@ -641,18 +753,81 @@ describe ActiveTriples::Relation do
641
753
  .to contain_exactly(*values)
642
754
  end
643
755
 
644
- context 'and persistence_strategy is configured' do
756
+ context 'and a class is configured' do
757
+ let(:this_type) { RDF::URI('http://example.org/Moomin') }
758
+ let(:this_class) do
759
+ Class.new do
760
+ include ActiveTriples::RDFSource
761
+ configure type: RDF::URI('http://example.org/Moomin')
762
+ end
763
+ end
764
+
765
+ let(:other_type) { RDF::URI('http://example.org/Snork') }
766
+ let(:other_class) do
767
+ Class.new do
768
+ include ActiveTriples::RDFSource
769
+ configure type: RDF::URI('http://example.org/Snork')
770
+ end
771
+ end
772
+
645
773
  before do
646
774
  reflections
647
- .property :moomin,
648
- predicate: RDF::URI('http://example.org/moomin'),
649
- persist_to: ActiveTriples::RepositoryStrategy
775
+ .property property,
776
+ class_name: this_class,
777
+ predicate: RDF::URI('http://example.org/moomin')
650
778
  end
651
779
 
652
- it 'assigns persistence strategy' do
653
- subject.each.each do |node|
654
- expect(node.persistence_strategy)
655
- .to be_a ActiveTriples::RepositoryStrategy
780
+ it 'casts values with no type to the class' do
781
+ expect(subject).to contain_exactly(an_instance_of(this_class),
782
+ an_instance_of(this_class),
783
+ an_instance_of(this_class))
784
+ end
785
+
786
+ it 'casts values with other type to the other class' do
787
+ subject << other_class.new
788
+ expect(subject).to contain_exactly(an_instance_of(this_class),
789
+ an_instance_of(this_class),
790
+ an_instance_of(this_class),
791
+ an_instance_of(other_class))
792
+ end
793
+ end
794
+
795
+ context 'and persistence_strategy is configured' do
796
+ context 'as a repository strategy' do
797
+ before do
798
+ reflections
799
+ .property :moomin,
800
+ predicate: RDF::URI('http://example.org/moomin'),
801
+ persist_to: ActiveTriples::RepositoryStrategy
802
+ end
803
+
804
+ it 'assigns persistence strategy' do
805
+ subject.each do |node|
806
+ expect(node.persistence_strategy)
807
+ .to be_a ActiveTriples::RepositoryStrategy
808
+ end
809
+ end
810
+ end
811
+
812
+ context 'as a parent strategy' do
813
+ before do
814
+ reflections
815
+ .property :moomin,
816
+ predicate: RDF::URI('http://example.org/moomin'),
817
+ persist_to: ActiveTriples::ParentStrategy
818
+ end
819
+
820
+ it 'assigns persistence strategy' do
821
+ subject.each do |node|
822
+ expect(node.persistence_strategy)
823
+ .to be_a ActiveTriples::ParentStrategy
824
+ end
825
+ end
826
+
827
+ it 'assigns parent' do
828
+ subject.each do |node|
829
+ expect(node.persistence_strategy.parent).to eql subject.parent
830
+ end
656
831
  end
657
832
  end
658
833
  end
@@ -767,6 +942,18 @@ describe ActiveTriples::Relation do
767
942
  .to change { subject.to_a }.to contain_exactly(*values)
768
943
  end
769
944
 
945
+ it 'notifies observers on the parent' do
946
+ observer = double(:observer)
947
+ values = [:moomin, :snork]
948
+ parent_resource.add_observer(observer)
949
+
950
+ expect(observer)
951
+ .to receive(:notify)
952
+ .with(subject.property, a_collection_containing_exactly(*values))
953
+
954
+ subject.set(values)
955
+ end
956
+
770
957
  context 'when given a Relation' do
771
958
  before do
772
959
  class DummySnowflake < RDF::Literal
@@ -635,6 +635,121 @@ describe ActiveTriples::Resource do
635
635
  end
636
636
  end
637
637
 
638
+ describe 'sources with properties with class_name defined' do
639
+ before(:context) do
640
+ class DummyChapter < ActiveTriples::Resource
641
+ ontology = RDF::URI('http://www.example.com/ontology')
642
+ type = RDF::URI('http://www.example.com/type')
643
+
644
+ configure repository: :default, type: type / 'Chapter'
645
+
646
+ property :title, predicate: ontology / 'title'
647
+ property :subtitle, predicate: ontology / 'subtitle'
648
+ end
649
+
650
+ class DummyBook < ActiveTriples::Resource
651
+ ontology = RDF::URI('http://www.example.com/ontology')
652
+ type = RDF::URI('http://www.example.com/type')
653
+
654
+ configure repository: :default, type: type / 'Book'
655
+
656
+ property :title, predicate: ontology / 'title'
657
+ property :has_chapter, predicate: ontology / 'hasChapter',
658
+ class_name: DummyChapter
659
+ end
660
+ end
661
+
662
+ context 'when loading models from graph' do
663
+ before(:context) do
664
+ r = RDF::Repository.new
665
+ ActiveTriples::Repositories.repositories[:default] = r
666
+
667
+ @book_url = 'http://www.example.com/BOOK_URI'
668
+ @book_title = 'Example Book.'
669
+ @chapter_title = 'Chapter 1'
670
+ ttl = "<#{@book_url}> a <http://www.example.com/type/Book>;
671
+ <http://www.example.com/ontology/hasChapter> [
672
+ a <http://www.example.com/type/Chapter>;
673
+ <http://www.example.com/ontology/title> \"#{@chapter_title}\"
674
+ ];
675
+ <http://www.example.com/ontology/title> \"#{@book_title}\" ."
676
+ book_graph = ::RDF::Graph.new.from_ttl ttl
677
+ r = ActiveTriples::Repositories.repositories[:default]
678
+ r << book_graph
679
+
680
+ @book = DummyBook.new(RDF::URI.new(@book_url))
681
+ end
682
+
683
+ it 'populates DummyBook properly' do
684
+ expect(@book.rdf_subject.to_s).to eq @book_url
685
+ expect(@book).to be_a DummyBook
686
+ expect(@book.type)
687
+ .to include(RDF::URI.new('http://www.example.com/type/Book'))
688
+ expect(@book.title.first).to eq @book_title
689
+ end
690
+
691
+ it 'populates DummyChapter properly' do
692
+ chapter = @book.has_chapter.first
693
+ expect(chapter).to be_a DummyChapter
694
+ expect(chapter.title.first).to eq @chapter_title
695
+ expect(chapter.type)
696
+ .to include(RDF::URI.new('http://www.example.com/type/Chapter'))
697
+ end
698
+ end
699
+
700
+ context 'when loading models through properties' do
701
+ before(:context) do
702
+ r = RDF::Repository.new
703
+ ActiveTriples::Repositories.repositories[:default] = r
704
+
705
+ bk1 = DummyBook.new('http://www.example.com/book1')
706
+ bk1.title = 'Learning about Explicit Links in ActiveTriples'
707
+
708
+ ch1 = DummyChapter.new('http://www.example.com/book1/chapter1')
709
+ ch1.title = 'Defining a source with an Explicit Link'
710
+ bk1.has_chapter = ch1
711
+ ch1.persist!
712
+ bk1.persist!
713
+
714
+ @bk1 = DummyBook.new('http://www.example.com/book1')
715
+ @ch1 = DummyChapter.new('http://www.example.com/book1/chapter1')
716
+ end
717
+
718
+ it 'populates DummyBook (resumed resource) properly' do
719
+ expect(@bk1.type.first)
720
+ .to eq RDF::URI('http://www.example.com/type/Book')
721
+ expect(@bk1.title.first)
722
+ .to eq 'Learning about Explicit Links in ActiveTriples'
723
+ end
724
+
725
+ it 'populates DummyChapter (property resource) properly' do
726
+ ch1 = @bk1.has_chapter.first
727
+ expect(ch1.type.first)
728
+ .to eq RDF::URI('http://www.example.com/type/Chapter')
729
+ expect(ch1.title.first)
730
+ .to eq 'Defining a source with an Explicit Link'
731
+ end
732
+
733
+ it 'populates DummyChapter (directly from repository) properly' do
734
+ expect(@ch1.type.first)
735
+ .to eq RDF::URI('http://www.example.com/type/Chapter')
736
+ expect(@ch1.title.first)
737
+ .to eq 'Defining a source with an Explicit Link'
738
+ end
739
+
740
+ it 'does not reload from repository twice' do
741
+ ch1 = @bk1.has_chapter.first
742
+ expect(ch1.title.first).to eq 'Defining a source with an Explicit Link'
743
+
744
+ @ch1.subtitle = 'Changed after original load'
745
+ @ch1.persist!
746
+ expect(@ch1.subtitle).to eq ['Changed after original load']
747
+
748
+ expect(ch1.subtitle).to eq []
749
+ end
750
+ end
751
+ end
752
+
638
753
  describe 'big complex graphs' do
639
754
  before do
640
755
  class DummyPerson