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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/active-triples.gemspec +2 -1
- data/lib/active_triples/configuration.rb +22 -11
- data/lib/active_triples/extension_strategy.rb +1 -1
- data/lib/active_triples/list.rb +0 -2
- data/lib/active_triples/node_config.rb +64 -8
- data/lib/active_triples/persistable.rb +2 -12
- data/lib/active_triples/persistence_strategies/parent_strategy.rb +16 -15
- data/lib/active_triples/property.rb +36 -11
- data/lib/active_triples/rdf_source.rb +86 -9
- data/lib/active_triples/relation.rb +61 -42
- data/lib/active_triples/schema.rb +20 -1
- data/lib/active_triples/version.rb +1 -1
- data/spec/active_triples/extension_strategy_spec.rb +12 -1
- data/spec/active_triples/node_config_spec.rb +54 -0
- data/spec/active_triples/persistence_strategies/parent_strategy_spec.rb +24 -17
- data/spec/active_triples/property_spec.rb +10 -0
- data/spec/active_triples/rdf_source_spec.rb +103 -122
- data/spec/active_triples/relation_spec.rb +199 -12
- data/spec/active_triples/resource_spec.rb +115 -0
- data/spec/active_triples/schema_spec.rb +6 -0
- metadata +21 -7
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe ActiveTriples::NodeConfig do
|
5
|
+
subject(:config) { described_class.new(term, predicate) }
|
6
|
+
let(:predicate) { RDF::URI(nil) }
|
7
|
+
let(:term) { :moomin }
|
8
|
+
|
9
|
+
it { is_expected.to have_attributes(predicate: predicate, term: term) }
|
10
|
+
|
11
|
+
describe '#[]' do
|
12
|
+
subject(:config) { described_class.new(term, predicate, some_opt: :moomin) }
|
13
|
+
|
14
|
+
it 'accesses arbitrary opts' do
|
15
|
+
expect(config[:some_opt]).to eq :moomin
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#cast' do
|
20
|
+
it 'defaults to true' do
|
21
|
+
expect { config.cast = false }
|
22
|
+
.to change { config.cast }
|
23
|
+
.from(true)
|
24
|
+
.to(false)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'is set with the initializer' do
|
28
|
+
expect(described_class.new(term, predicate, cast: false).cast).to be false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#with_index' do
|
33
|
+
it 'yields an index configuration object' do
|
34
|
+
expect { |b| config.with_index(&b) }
|
35
|
+
.to yield_with_args(an_instance_of(described_class::IndexObject))
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'accepts behavior settings' do
|
39
|
+
config.with_index do |index|
|
40
|
+
index.as :moomin, :snork
|
41
|
+
end
|
42
|
+
|
43
|
+
expect(config.behaviors).to contain_exactly :moomin, :snork
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'accepts type settings' do
|
47
|
+
config.with_index do |index|
|
48
|
+
index.type :moomin
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(config.type).to eq :moomin
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -4,7 +4,7 @@ require 'spec_helper'
|
|
4
4
|
describe ActiveTriples::ParentStrategy do
|
5
5
|
subject { described_class.new(rdf_source) }
|
6
6
|
let(:rdf_source) { BasicPersistable.new }
|
7
|
-
|
7
|
+
|
8
8
|
shared_context 'with a parent' do
|
9
9
|
subject { rdf_source.persistence_strategy }
|
10
10
|
let(:parent) { BasicPersistable.new }
|
@@ -50,7 +50,7 @@ describe ActiveTriples::ParentStrategy do
|
|
50
50
|
rdf_source.insert(*statements)
|
51
51
|
subject.persist!
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
let(:statements) do
|
55
55
|
[RDF::Statement(subject.source.rdf_subject, RDF::Vocab::DC.title, 'moomin'),
|
56
56
|
RDF::Statement(subject.parent, RDF::Vocab::DC.relation, subject.source.rdf_subject)]
|
@@ -58,7 +58,7 @@ describe ActiveTriples::ParentStrategy do
|
|
58
58
|
|
59
59
|
it 'removes graph from the parent' do
|
60
60
|
subject.destroy
|
61
|
-
|
61
|
+
|
62
62
|
statements.each do |statement|
|
63
63
|
expect(subject.parent.statements).not_to have_statement statement
|
64
64
|
end
|
@@ -83,18 +83,18 @@ describe ActiveTriples::ParentStrategy do
|
|
83
83
|
|
84
84
|
context 'with parent' do
|
85
85
|
include_context 'with a parent'
|
86
|
-
|
86
|
+
|
87
87
|
it 'gives the parent' do
|
88
88
|
expect(subject.ancestors).to contain_exactly(parent)
|
89
89
|
end
|
90
90
|
|
91
91
|
context 'and nested parents' do
|
92
92
|
let(:parents) do
|
93
|
-
[double('second', persistence_strategy: double('strategy2')),
|
93
|
+
[double('second', persistence_strategy: double('strategy2')),
|
94
94
|
double('third', persistence_strategy: double('strategy3'))]
|
95
95
|
end
|
96
96
|
let(:last) { double('last', persistence_strategy: double('last_strategy')) }
|
97
|
-
|
97
|
+
|
98
98
|
it 'gives all ancestors' do
|
99
99
|
allow(parent.persistence_strategy)
|
100
100
|
.to receive(:parent).and_return(parents.first)
|
@@ -102,7 +102,7 @@ describe ActiveTriples::ParentStrategy do
|
|
102
102
|
.to receive(:parent).and_return(parents[1])
|
103
103
|
allow(parents[1].persistence_strategy)
|
104
104
|
.to receive(:parent).and_return(last)
|
105
|
-
|
105
|
+
|
106
106
|
expect(subject.ancestors)
|
107
107
|
.to contain_exactly(*(parents << parent << last))
|
108
108
|
end
|
@@ -148,7 +148,19 @@ describe ActiveTriples::ParentStrategy do
|
|
148
148
|
describe '#parent' do
|
149
149
|
it { is_expected.to have_attributes(:parent => nil) }
|
150
150
|
|
151
|
-
|
151
|
+
context 'with a parent' do
|
152
|
+
include_context 'with a parent'
|
153
|
+
it { is_expected.to have_attributes(:parent => parent) }
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#parent=' do
|
158
|
+
it 'requires a non-nil value' do
|
159
|
+
expect { subject.parent = nil }
|
160
|
+
.to raise_error described_class::NilParentError
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'requires the value to be RDF::Mutable' do
|
152
164
|
expect { subject.parent = Object.new }
|
153
165
|
.to raise_error described_class::UnmutableParentError
|
154
166
|
end
|
@@ -159,11 +171,6 @@ describe ActiveTriples::ParentStrategy do
|
|
159
171
|
expect { subject.parent = immutable }
|
160
172
|
.to raise_error described_class::UnmutableParentError
|
161
173
|
end
|
162
|
-
|
163
|
-
context 'with a parent' do
|
164
|
-
include_context 'with a parent'
|
165
|
-
it { is_expected.to have_attributes(:parent => parent) }
|
166
|
-
end
|
167
174
|
end
|
168
175
|
|
169
176
|
describe '#persist!' do
|
@@ -205,7 +212,7 @@ describe ActiveTriples::ParentStrategy do
|
|
205
212
|
parent.persistence_strategy.parent = last
|
206
213
|
rdf_source.reload
|
207
214
|
end
|
208
|
-
|
215
|
+
|
209
216
|
it 'writes to #parent graph when parent changes while child is live' do
|
210
217
|
parent.insert(parent_st)
|
211
218
|
parent.persist!
|
@@ -248,16 +255,16 @@ describe ActiveTriples::ParentStrategy::Ancestors do
|
|
248
255
|
context 'with parents' do
|
249
256
|
let(:parent) { BasicPersistable.new }
|
250
257
|
let(:last) { BasicPersistable.new }
|
251
|
-
|
258
|
+
|
252
259
|
before do
|
253
260
|
parent.set_persistence_strategy(ActiveTriples::ParentStrategy)
|
254
261
|
parent.persistence_strategy.parent = last
|
255
262
|
rdf_source.set_persistence_strategy(ActiveTriples::ParentStrategy)
|
256
263
|
rdf_source.persistence_strategy.parent = parent
|
257
264
|
end
|
258
|
-
|
265
|
+
|
259
266
|
it { expect(subject.each).to be_a Enumerator }
|
260
|
-
|
267
|
+
|
261
268
|
it 'enumerates ancestors' do
|
262
269
|
expect(subject.each).to contain_exactly(parent, last)
|
263
270
|
end
|
@@ -17,6 +17,16 @@ describe ActiveTriples::Property do
|
|
17
17
|
expect(subject.class_name).to eq "Test"
|
18
18
|
end
|
19
19
|
|
20
|
+
it 'should hold a block' do
|
21
|
+
fake = Object.new
|
22
|
+
property = described_class.new(options) do
|
23
|
+
fake.configure
|
24
|
+
end
|
25
|
+
|
26
|
+
expect(fake).to receive(:configure)
|
27
|
+
property.config.call
|
28
|
+
end
|
29
|
+
|
20
30
|
describe "#to_h" do
|
21
31
|
it "should not return the property's name" do
|
22
32
|
expect(subject.to_h).to eq (
|
@@ -18,8 +18,9 @@ describe ActiveTriples::RDFSource do
|
|
18
18
|
end
|
19
19
|
|
20
20
|
before { @enumerable = subject }
|
21
|
+
|
21
22
|
let(:source_class) { Class.new { include ActiveTriples::RDFSource } }
|
22
|
-
let(:uri)
|
23
|
+
let(:uri) { RDF::URI('http://example.org/moomin') }
|
23
24
|
|
24
25
|
subject { source_class.new }
|
25
26
|
|
@@ -107,6 +108,45 @@ describe ActiveTriples::RDFSource do
|
|
107
108
|
end
|
108
109
|
end
|
109
110
|
|
111
|
+
describe 'observers' do
|
112
|
+
let(:observer) { double('observer') }
|
113
|
+
|
114
|
+
before { subject.add_observer(observer) }
|
115
|
+
|
116
|
+
include_context 'with properties'
|
117
|
+
|
118
|
+
it 'notifies an observer of changes' do
|
119
|
+
expect(observer)
|
120
|
+
.to receive(:notify)
|
121
|
+
.with(:creator, a_collection_containing_exactly('moomin'))
|
122
|
+
|
123
|
+
subject.creator = 'moomin'
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'notifies muliple observers of changes' do
|
127
|
+
other_observer = double('second observer')
|
128
|
+
values = ['moomin', 'snork']
|
129
|
+
|
130
|
+
expect(observer)
|
131
|
+
.to receive(:notify)
|
132
|
+
.with(:creator, a_collection_containing_exactly(*values))
|
133
|
+
expect(other_observer)
|
134
|
+
.to receive(:notify)
|
135
|
+
.with(:creator, a_collection_containing_exactly(*values))
|
136
|
+
|
137
|
+
subject.add_observer(other_observer)
|
138
|
+
subject.creator = values
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'does not notify removed observers' do
|
142
|
+
expect(observer).not_to receive(:notify)
|
143
|
+
|
144
|
+
subject.delete_observer(observer)
|
145
|
+
|
146
|
+
subject.creator = 'moomin'
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
110
150
|
describe '#==' do
|
111
151
|
shared_examples 'Term equality' do
|
112
152
|
it 'equals itself' do
|
@@ -172,9 +212,9 @@ describe ActiveTriples::RDFSource do
|
|
172
212
|
|
173
213
|
context 'with statements for other subjects' do
|
174
214
|
before do
|
175
|
-
subject <<
|
215
|
+
subject <<
|
176
216
|
RDF::Statement(RDF::URI('http://example.org/OTHER_SUBJECT'),
|
177
|
-
RDF::URI('http://example.org/ontology/OTHER_PRED'),
|
217
|
+
RDF::URI('http://example.org/ontology/OTHER_PRED'),
|
178
218
|
'OTHER_OBJECT')
|
179
219
|
end
|
180
220
|
|
@@ -191,6 +231,16 @@ describe ActiveTriples::RDFSource do
|
|
191
231
|
end
|
192
232
|
end
|
193
233
|
|
234
|
+
describe '#default_labels' do
|
235
|
+
it 'prefers skos:prefLabel' do
|
236
|
+
expect(subject.default_labels.first).to eq RDF::Vocab::SKOS.prefLabel
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'values are all valid predicates' do
|
240
|
+
subject.default_labels.each { |term| expect(term).to be_uri }
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
194
244
|
describe '#fetch' do
|
195
245
|
it 'raises an error when it is a node' do
|
196
246
|
expect { subject.fetch }
|
@@ -271,6 +321,18 @@ describe ActiveTriples::RDFSource do
|
|
271
321
|
end
|
272
322
|
end
|
273
323
|
|
324
|
+
describe '#inspect' do
|
325
|
+
it 'includes bnode id' do
|
326
|
+
expect(subject.inspect).to include subject.to_base
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'includes uri' do
|
330
|
+
subject.set_subject!('http://example.org/moomin')
|
331
|
+
|
332
|
+
expect(subject.inspect).to include subject.to_base
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
274
336
|
describe '#rdf_subject' do
|
275
337
|
its(:rdf_subject) { is_expected.to be_a_node }
|
276
338
|
|
@@ -373,6 +435,7 @@ describe ActiveTriples::RDFSource do
|
|
373
435
|
|
374
436
|
shared_examples 'setting values' do
|
375
437
|
include_context 'with properties'
|
438
|
+
|
376
439
|
after do
|
377
440
|
Object.send(:remove_const, 'SourceWithCreator') if
|
378
441
|
defined? SourceWithCreator
|
@@ -473,6 +536,39 @@ describe ActiveTriples::RDFSource do
|
|
473
536
|
end
|
474
537
|
end
|
475
538
|
|
539
|
+
describe 'on child nodes' do
|
540
|
+
let(:parent) { source_class.new }
|
541
|
+
let(:subject) { source_class.new(uri, parent) }
|
542
|
+
|
543
|
+
include_examples 'setting values' do
|
544
|
+
let(:value) do
|
545
|
+
['moomin',
|
546
|
+
Date.today,
|
547
|
+
RDF::Node.new,
|
548
|
+
source_class.new,
|
549
|
+
source_class.new(uri / 'new'),
|
550
|
+
subject]
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
it 'does not change parent' do
|
555
|
+
property = RDF::Vocab::DC.title
|
556
|
+
|
557
|
+
expect { subject.set_value(property, 'Comet in Moominland') }
|
558
|
+
.not_to change { parent.to_a }
|
559
|
+
end
|
560
|
+
|
561
|
+
it 'persists to parent' do
|
562
|
+
property = RDF::Vocab::DC.title
|
563
|
+
|
564
|
+
subject.set_value(property, 'Comet in Moominland')
|
565
|
+
|
566
|
+
expect { subject.persist! }
|
567
|
+
.to change { parent.to_a }
|
568
|
+
.to include RDF::Statement(subject, property, 'Comet in Moominland')
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
476
572
|
context 'with reciprocal relations' do
|
477
573
|
let(:document) { source_class.new }
|
478
574
|
let(:person) { source_class.new }
|
@@ -550,15 +646,15 @@ describe ActiveTriples::RDFSource do
|
|
550
646
|
expect { subject.set_value(predicate, child_other) }
|
551
647
|
.not_to change { child_other.persistence_strategy.parent }
|
552
648
|
end
|
553
|
-
|
649
|
+
|
554
650
|
context 'when setting to a relation' do
|
555
651
|
it 'adds child node data to graph' do
|
556
652
|
other << RDF::Statement(other, RDF::URI('p'), 'o')
|
557
|
-
|
558
|
-
relation_source = source_class.new
|
653
|
+
|
654
|
+
relation_source = source_class.new
|
559
655
|
relation_source.set_value(predicate, other)
|
560
656
|
relation = relation_source.get_values(predicate)
|
561
|
-
|
657
|
+
|
562
658
|
expect { subject.set_value(predicate, relation) }
|
563
659
|
.to change { subject.statements.to_a }
|
564
660
|
.to include(*other.statements.to_a)
|
@@ -673,119 +769,4 @@ describe ActiveTriples::RDFSource do
|
|
673
769
|
expect { dummy_source.new.test_title }.not_to raise_error
|
674
770
|
end
|
675
771
|
end
|
676
|
-
|
677
|
-
describe 'sources with properties with class_name defined' do
|
678
|
-
before(:context) do
|
679
|
-
class DummyChapter < ActiveTriples::Resource
|
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'
|
687
|
-
end
|
688
|
-
|
689
|
-
class DummyBook < ActiveTriples::Resource
|
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
|
698
|
-
end
|
699
|
-
end
|
700
|
-
|
701
|
-
context 'when loading models from graph' do
|
702
|
-
before(:context) do
|
703
|
-
r = RDF::Repository.new
|
704
|
-
ActiveTriples::Repositories.repositories[:default] = r
|
705
|
-
|
706
|
-
@book_url = 'http://www.example.com/BOOK_URI'
|
707
|
-
@book_title = 'Example Book.'
|
708
|
-
@chapter_title = 'Chapter 1'
|
709
|
-
ttl = "<#{@book_url}> a <http://www.example.com/type/Book>;
|
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}\" ."
|
715
|
-
book_graph = ::RDF::Graph.new.from_ttl ttl
|
716
|
-
r = ActiveTriples::Repositories.repositories[:default]
|
717
|
-
r << book_graph
|
718
|
-
|
719
|
-
@book = DummyBook.new(RDF::URI.new(@book_url))
|
720
|
-
end
|
721
|
-
|
722
|
-
it 'populates DummyBook properly' do
|
723
|
-
expect(@book.rdf_subject.to_s).to eq @book_url
|
724
|
-
expect(@book).to be_a DummyBook
|
725
|
-
expect(@book.type)
|
726
|
-
.to include(RDF::URI.new('http://www.example.com/type/Book'))
|
727
|
-
expect(@book.title.first).to eq @book_title
|
728
|
-
end
|
729
|
-
|
730
|
-
it 'populates DummyChapter properly' do
|
731
|
-
chapter = @book.has_chapter.first
|
732
|
-
expect(chapter).to be_a DummyChapter
|
733
|
-
expect(chapter.title.first).to eq @chapter_title
|
734
|
-
expect(chapter.type)
|
735
|
-
.to include(RDF::URI.new('http://www.example.com/type/Chapter'))
|
736
|
-
end
|
737
|
-
end
|
738
|
-
|
739
|
-
context 'when loading models through properties' do
|
740
|
-
before(:context) do
|
741
|
-
r = RDF::Repository.new
|
742
|
-
ActiveTriples::Repositories.repositories[:default] = r
|
743
|
-
|
744
|
-
bk1 = DummyBook.new('http://www.example.com/book1')
|
745
|
-
bk1.title = 'Learning about Explicit Links in ActiveTriples'
|
746
|
-
|
747
|
-
ch1 = DummyChapter.new('http://www.example.com/book1/chapter1')
|
748
|
-
ch1.title = 'Defining a source with an Explicit Link'
|
749
|
-
bk1.has_chapter = ch1
|
750
|
-
ch1.persist!
|
751
|
-
bk1.persist!
|
752
|
-
|
753
|
-
@bk1 = DummyBook.new('http://www.example.com/book1')
|
754
|
-
@ch1 = DummyChapter.new('http://www.example.com/book1/chapter1')
|
755
|
-
end
|
756
|
-
|
757
|
-
it 'populates DummyBook (resumed resource) properly' do
|
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'
|
762
|
-
end
|
763
|
-
|
764
|
-
it 'populates DummyChapter (property resource) properly' do
|
765
|
-
ch1 = @bk1.has_chapter.first
|
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'
|
770
|
-
end
|
771
|
-
|
772
|
-
it 'populates DummyChapter (directly from repository) properly' do
|
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'
|
777
|
-
end
|
778
|
-
|
779
|
-
it 'does not reload from repository twice' do
|
780
|
-
ch1 = @bk1.has_chapter.first
|
781
|
-
expect(ch1.title.first).to eq 'Defining a source with an Explicit Link'
|
782
|
-
|
783
|
-
@ch1.subtitle = 'Changed after original load'
|
784
|
-
@ch1.persist!
|
785
|
-
expect(@ch1.subtitle).to eq ['Changed after original load']
|
786
|
-
|
787
|
-
expect(ch1.subtitle).to eq []
|
788
|
-
end
|
789
|
-
end
|
790
|
-
end
|
791
772
|
end
|