active-triples 0.11.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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