ladder 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,354 @@
1
+ shared_examples 'a Resource' do
2
+ let(:subject) { Thing.new }
3
+ let(:person) { Person.new }
4
+
5
+ shared_context 'with data' do
6
+ let(:concept) { Concept.new }
7
+ let(:part) { Part.new }
8
+
9
+ before do
10
+ class Concept
11
+ include Ladder::Resource
12
+ end
13
+
14
+ class Part
15
+ include Ladder::Resource
16
+ end
17
+
18
+ # non-localized literal
19
+ subject.class.field :alt
20
+ subject.class.property :alt, :predicate => RDF::DC.alternative
21
+ subject.alt = 'Mumintrollet pa kometjakt'
22
+
23
+ # localized literal
24
+ subject.class.property :title, :predicate => RDF::DC.title
25
+ subject.title = 'Comet in Moominland'
26
+
27
+ # many-to-many
28
+ person.class.property :things, :predicate => RDF::DC.relation, :class_name => 'Thing'
29
+ subject.class.property :people, :predicate => RDF::DC.creator, :class_name => 'Person'
30
+ subject.people << person
31
+
32
+ # one-sided has-many
33
+ subject.class.has_and_belongs_to_many :concepts, inverse_of: nil
34
+ subject.class.property :concepts, :predicate => RDF::DC.subject, :class_name => 'Concept'
35
+ subject.concepts << concept
36
+
37
+ # embedded one
38
+ part.class.embedded_in :thing
39
+ part.class.property :thing, :predicate => RDF::DC.relation, :class_name => 'Thing'
40
+ subject.class.embeds_one :part, cascade_callbacks: true
41
+ subject.class.property :part, :predicate => RDF::DC.hasPart, :class_name => 'Part'
42
+ subject.part = part
43
+ subject.save
44
+ end
45
+
46
+ after do
47
+ Object.send(:remove_const, 'Concept')
48
+ Object.send(:remove_const, 'Part')
49
+ end
50
+
51
+ it 'should have relations' do
52
+ expect(subject.title).to eq 'Comet in Moominland'
53
+ expect(subject.people.to_a).to include person
54
+ expect(subject.concepts.to_a).to include concept
55
+ expect(subject.part).to eq part
56
+ end
57
+
58
+ it 'should have reverse relations' do
59
+ expect(person.things.to_a).to include subject
60
+ expect(concept.relations).to be_empty
61
+ expect(part.thing).to eq subject
62
+ end
63
+ end
64
+
65
+ describe 'LADDER_BASE_URI' do
66
+ it 'should automatically have a base URI' do
67
+ expect(subject.rdf_subject.parent).to eq RDF::URI('http://example.org/things/')
68
+ end
69
+ end
70
+
71
+ describe '#property' do
72
+ context 'with non-localized literal' do
73
+ before do
74
+ subject.class.field :alt
75
+ subject.class.property :alt, :predicate => RDF::DC.alternative
76
+ subject.alt = 'Mumintrollet pa kometjakt'
77
+ end
78
+
79
+ it 'should return non-localized value' do
80
+ expect(subject.alt).to eq 'Mumintrollet pa kometjakt'
81
+ end
82
+
83
+ it 'should not be a localized hash' do
84
+ expect(subject.attributes['alt']).to eq 'Mumintrollet pa kometjakt'
85
+ end
86
+
87
+ it 'should have a valid predicate' do
88
+ expect(subject.class.properties['alt'].predicate).to eq RDF::DC.alternative
89
+ end
90
+
91
+ it 'allows resetting of properties' do
92
+ subject.class.property :alt, :predicate => RDF::DC.title
93
+ expect(subject.class.properties['alt'].predicate).to eq RDF::DC.title
94
+ end
95
+ end
96
+
97
+ context 'with localized literal' do
98
+ before do
99
+ subject.class.property :title, :predicate => RDF::DC.title
100
+ subject.title = 'Comet in Moominland'
101
+ end
102
+
103
+ it 'should return localized value' do
104
+ expect(subject.title).to eq 'Comet in Moominland'
105
+ end
106
+
107
+ it 'should return all locales' do
108
+ expect(subject.attributes['title']).to eq({'en' => 'Comet in Moominland'})
109
+ end
110
+
111
+ it 'should have a valid predicate' do
112
+ expect(subject.class.properties['title'].predicate).to eq RDF::DC.title
113
+ end
114
+
115
+ it 'allows resetting of properties' do
116
+ subject.class.property :title, :predicate => RDF::DC.alternative
117
+ expect(subject.class.properties['title'].predicate).to eq RDF::DC.alternative
118
+ end
119
+ end
120
+
121
+ context 'with many-to-many' do
122
+ before do
123
+ subject.class.property :people, :predicate => RDF::DC.creator, :class_name => 'Person'
124
+ person.class.property :things, :predicate => RDF::DC.relation, :class_name => 'Thing'
125
+ subject.people << person
126
+ subject.save
127
+ end
128
+
129
+ it 'should have a relation' do
130
+ expect(subject.relations['people'].relation).to eq (Mongoid::Relations::Referenced::ManyToMany)
131
+ expect(subject.people.to_a).to include person
132
+ end
133
+
134
+ it 'should have an inverse relation' do
135
+ expect(person.relations['things'].relation).to eq (Mongoid::Relations::Referenced::ManyToMany)
136
+ expect(person.things.to_a).to include subject
137
+ end
138
+
139
+ it 'should have a valid predicate' do
140
+ expect(subject.class.properties['people'].predicate).to eq RDF::DC.creator
141
+ end
142
+
143
+ it 'should have a valid inverse predicate' do
144
+ expect(person.class.properties['things'].predicate).to eq RDF::DC.relation
145
+ end
146
+ end
147
+
148
+ context 'with one-sided has-many' do
149
+ before do
150
+ subject.class.has_and_belongs_to_many :people, inverse_of: nil
151
+ subject.class.property :people, :predicate => RDF::DC.creator, :class_name => 'Person'
152
+ subject.people << person
153
+ end
154
+
155
+ it 'should have a relation' do
156
+ expect(subject.relations['people'].relation).to eq (Mongoid::Relations::Referenced::ManyToMany)
157
+ expect(subject.people.to_a).to include person
158
+ end
159
+
160
+ it 'should not have an inverse relation' do
161
+ expect(subject.relations['people'].inverse_of).to be nil
162
+ expect(person.relations).to be_empty
163
+ end
164
+
165
+ it 'should have a valid predicate' do
166
+ expect(subject.class.properties['people'].predicate).to eq RDF::DC.creator
167
+ end
168
+
169
+ it 'should not have an inverse predicate' do
170
+ expect(person.class.properties).to be_empty
171
+ end
172
+ end
173
+
174
+ context 'with embeds-many' do
175
+ before do
176
+ subject.class.embeds_many :people
177
+ subject.class.property :people, :predicate => RDF::DC.creator, :class_name => 'Person'
178
+
179
+ person.class.embedded_in :thing
180
+ person.class.property :thing, :predicate => RDF::DC.relation, :class_name => 'Thing'
181
+
182
+ subject.people << person
183
+ end
184
+
185
+ it 'should have a relation' do
186
+ expect(subject.relations['people'].relation).to eq (Mongoid::Relations::Embedded::Many)
187
+ expect(subject.people.to_a).to include person
188
+ end
189
+
190
+ it 'should have an inverse relation' do
191
+ expect(person.relations['thing'].relation).to eq (Mongoid::Relations::Embedded::In)
192
+ expect(person.thing).to eq subject
193
+ end
194
+
195
+ it 'should have a valid predicate' do
196
+ expect(subject.class.properties['people'].predicate).to eq RDF::DC.creator
197
+ end
198
+
199
+ it 'should have a valid inverse predicate' do
200
+ expect(person.class.properties['thing'].predicate).to eq RDF::DC.relation
201
+ end
202
+ end
203
+ end
204
+
205
+ describe '#update_resource' do
206
+
207
+ context 'without related: true' do
208
+ include_context 'with data'
209
+
210
+ before do
211
+ subject.update_resource
212
+ end
213
+
214
+ it 'should have a literal object' do
215
+ subject.resource.query(:subject => subject.rdf_subject, :predicate => RDF::DC.title).each_statement do |s|
216
+ expect(s.object.to_s).to eq 'Comet in Moominland'
217
+ end
218
+ end
219
+
220
+ it 'should have an embedded object' do
221
+ query = subject.resource.query(:subject => subject.rdf_subject, :predicate => RDF::DC.hasPart)
222
+ expect(query.count).to eq 1
223
+
224
+ query.each_statement do |s|
225
+ expect(s.object).to eq part.rdf_subject
226
+ end
227
+ end
228
+
229
+ it 'should have an embedded object relation' do
230
+ query = subject.resource.query(:subject => part.rdf_subject, :predicate => RDF::DC.relation)
231
+ expect(query.count).to eq 1
232
+ expect(query.to_hash).to eq part.resource.statements.to_hash
233
+
234
+ query.each_statement do |s|
235
+ expect(s.object).to eq subject.rdf_subject
236
+ end
237
+ end
238
+
239
+ it 'should not have related objects' do
240
+ expect(subject.resource.query(:subject => person.rdf_subject)).to be_empty
241
+ expect(subject.resource.query(:subject => concept.rdf_subject)).to be_empty
242
+ end
243
+
244
+ it 'should not have related object relations' do
245
+ expect(person.resource.statements).to be_empty
246
+ expect(concept.resource.statements).to be_empty
247
+ end
248
+ end
249
+
250
+ context 'with related: true' do
251
+ include_context 'with data'
252
+
253
+ before do
254
+ subject.update_resource(:related => true)
255
+ end
256
+
257
+ it 'should have a literal object' do
258
+ subject.resource.query(:subject => subject.rdf_subject, :predicate => RDF::DC.title).each_statement do |s|
259
+ expect(s.object.to_s).to eq 'Comet in Moominland'
260
+ end
261
+ end
262
+
263
+ it 'should have an embedded object' do
264
+ query = subject.resource.query(:subject => subject.rdf_subject, :predicate => RDF::DC.hasPart)
265
+ expect(query.count).to eq 1
266
+
267
+ query.each_statement do |s|
268
+ expect(s.object).to eq part.rdf_subject
269
+ end
270
+ end
271
+
272
+ it 'should have an embedded object relation' do
273
+ query = subject.resource.query(:subject => part.rdf_subject, :predicate => RDF::DC.relation)
274
+ expect(query.count).to eq 1
275
+ expect(query.to_hash).to eq part.resource.statements.to_hash
276
+
277
+ query.each_statement do |s|
278
+ expect(s.object).to eq subject.rdf_subject
279
+ end
280
+ end
281
+
282
+ it 'should have related objects' do
283
+ # many-to-many
284
+ query_creator = subject.resource.query(:subject => subject.rdf_subject, :predicate => RDF::DC.creator)
285
+ expect(query_creator.count).to eq 1
286
+
287
+ query_creator.each_statement do |s|
288
+ expect(s.object).to eq person.rdf_subject
289
+ end
290
+
291
+ # one-sided has-many
292
+ query_subject = subject.resource.query(:subject => subject.rdf_subject, :predicate => RDF::DC.subject)
293
+ expect(query_subject.count).to eq 1
294
+
295
+ query_subject.each_statement do |s|
296
+ expect(s.object).to eq concept.rdf_subject
297
+ end
298
+ end
299
+
300
+ it 'should have related object relations' do
301
+ # many-to-many
302
+ query = person.resource.query(:subject => person.rdf_subject, :predicate => RDF::DC.relation)
303
+ expect(query.count).to eq 1
304
+ expect(query.to_hash).to eq person.resource.statements.to_hash
305
+
306
+ query.each_statement do |s|
307
+ expect(s.object).to eq subject.rdf_subject
308
+ end
309
+
310
+ # one-sided has-many
311
+ expect(subject.resource.query(:subject => concept.rdf_subject)).to be_empty
312
+ expect(concept.resource.statements).to be_empty
313
+ end
314
+ end
315
+
316
+ context 'with related and then without related' do
317
+ include_context 'with data'
318
+
319
+ before do
320
+ subject.update_resource(:related => true)
321
+ subject.update_resource
322
+ end
323
+
324
+ it 'should not have related objects' do
325
+ expect(subject.resource.query(:subject => person.rdf_subject)).to be_empty
326
+ expect(subject.resource.query(:subject => concept.rdf_subject)).to be_empty
327
+ end
328
+
329
+ it 'should have related object relations' do
330
+ # many-to-many
331
+ query = person.resource.query(:subject => person.rdf_subject, :predicate => RDF::DC.relation)
332
+ expect(query.count).to eq 1
333
+ expect(query.to_hash).to eq person.resource.statements.to_hash
334
+
335
+ query.each_statement do |s|
336
+ expect(s.object).to eq subject.rdf_subject
337
+ end
338
+
339
+ # one-sided has-many
340
+ expect(subject.resource.query(:subject => concept.rdf_subject)).to be_empty
341
+ expect(concept.resource.statements).to be_empty
342
+ end
343
+ end
344
+ end
345
+
346
+ describe '#as_jsonld' do
347
+ include_context 'with data'
348
+
349
+ it 'should output a valid jsonld representation of itself' do
350
+ g = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld)
351
+ expect(subject.resource.to_hash == g.to_hash).to be true
352
+ end
353
+ end
354
+ end
@@ -0,0 +1,191 @@
1
+ shared_examples 'a Searchable' do
2
+ let(:subject) { Thing.new }
3
+ let(:person) { Person.new }
4
+
5
+ shared_context 'with data' do
6
+ before do
7
+ subject.class.configure type: RDF::DC.BibliographicResource
8
+ subject.class.property :title, :predicate => RDF::DC.title
9
+ subject.title = 'Comet in Moominland'
10
+ end
11
+ end
12
+
13
+ describe '#index_for_search' do
14
+ include_context 'with data'
15
+
16
+ context 'with default' do
17
+ before do
18
+ subject.class.index_for_search
19
+ subject.save
20
+ Elasticsearch::Model.client.indices.flush
21
+ end
22
+
23
+ it 'should exist in the index' do
24
+ results = subject.class.search('title:moomin*')
25
+ expect(results.count).to eq 1
26
+ expect(results.first._source.to_hash).to eq JSON.parse(subject.as_indexed_json.to_json)
27
+ end
28
+ end
29
+
30
+ context 'with as qname' do
31
+ before do
32
+ subject.class.index_for_search as: :qname
33
+ subject.save
34
+ Elasticsearch::Model.client.indices.flush
35
+ end
36
+
37
+ it 'should exist in the index' do
38
+ results = subject.class.search('dc.title.en:moomin*')
39
+ expect(results.count).to eq 1
40
+ expect(results.first._source.to_hash).to eq JSON.parse(subject.as_qname.to_json)
41
+ end
42
+ end
43
+
44
+ context 'with as jsonld' do
45
+ before do
46
+ subject.class.index_for_search as: :jsonld
47
+ subject.save
48
+ Elasticsearch::Model.client.indices.flush
49
+ end
50
+
51
+ it 'should exist in the index' do
52
+ results = subject.class.search('dc\:title.@value:moomin*')
53
+ expect(results.count).to eq 1
54
+ expect(results.first._source.to_hash).to eq subject.as_jsonld
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#index_for_search related' do
60
+ include_context 'with data'
61
+
62
+ before do
63
+ # related object
64
+ person.class.configure type: RDF::FOAF.Person
65
+ person.class.property :foaf_name, :predicate => RDF::FOAF.name
66
+ person.foaf_name = 'Tove Jansson'
67
+
68
+ # many-to-many relation
69
+ person.class.property :things, :predicate => RDF::DC.relation, :class_name => 'Thing'
70
+ subject.class.property :people, :predicate => RDF::DC.creator, :class_name => 'Person'
71
+ subject.people << person
72
+ end
73
+
74
+ context 'with default' do
75
+ before do
76
+ person.class.index_for_search
77
+ subject.class.index_for_search
78
+ subject.save
79
+ Elasticsearch::Model.client.indices.flush
80
+ end
81
+
82
+ it 'should contain an ID for the related object' do
83
+ results = subject.class.search('person_ids.$oid:' + person.id)
84
+ expect(results.count).to eq 1
85
+ end
86
+
87
+ it 'should include the related object in the index' do
88
+ results = person.class.search('foaf_name:tove')
89
+ expect(results.count).to eq 1
90
+ expect(results.first._source.to_hash).to eq JSON.parse(person.as_indexed_json.to_json)
91
+ end
92
+
93
+ it 'should contain an ID for the subject' do
94
+ results = person.class.search('thing_ids.$oid:' + subject.id)
95
+ expect(results.count).to eq 1
96
+ end
97
+ end
98
+
99
+ context 'with as qname' do
100
+ before do
101
+ person.class.index_for_search as: :qname
102
+ subject.class.index_for_search as: :qname
103
+ subject.save
104
+ Elasticsearch::Model.client.indices.flush
105
+ end
106
+
107
+ it 'should contain an ID for the related object' do
108
+ results = subject.class.search('dc.creator:' + person.id)
109
+ expect(results.count).to eq 1
110
+ end
111
+
112
+ it 'should include the related object in the index' do
113
+ results = person.class.search('foaf.name.en:tove')
114
+ expect(results.count).to eq 1
115
+ expect(results.first._source.to_hash).to eq JSON.parse(person.as_qname.to_json)
116
+ end
117
+
118
+ it 'should contain an ID for the subject' do
119
+ results = person.class.search('dc.relation:' + subject.id)
120
+ expect(results.count).to eq 1
121
+ end
122
+ end
123
+
124
+ context 'with as_qname related' do
125
+ before do
126
+ person.class.index_for_search as: :qname, related: true
127
+ subject.class.index_for_search as: :qname, related: true
128
+ subject.save
129
+ Elasticsearch::Model.client.indices.flush
130
+ end
131
+
132
+ it 'should contain a embedded related object' do
133
+ results = subject.class.search('dc.creator.foaf.name.en:tove')
134
+ expect(results.count).to eq 1
135
+ expect(results.first._source['dc']['creator'].first).to eq Hashie::Mash.new person.as_qname
136
+ end
137
+
138
+ it 'should contain an embedded subject in the related object' do
139
+ results = person.class.search('dc.relation.dc.title.en:moomin*')
140
+ expect(results.count).to eq 1
141
+ expect(results.first._source['dc']['relation'].first).to eq Hashie::Mash.new subject.as_qname
142
+ end
143
+ end
144
+
145
+ context 'with as_jsonld' do
146
+ before do
147
+ person.class.index_for_search as: :jsonld
148
+ subject.class.index_for_search as: :jsonld
149
+ subject.save
150
+ Elasticsearch::Model.client.indices.flush
151
+ end
152
+
153
+ it 'should contain an ID for the related object' do
154
+ results = subject.class.search('dc\:creator.@id:' + person.id)
155
+ expect(results.count).to eq 1
156
+ end
157
+
158
+ it 'should include the related object in the index' do
159
+ results = person.class.search('foaf\:name.@value:tove')
160
+ expect(results.count).to eq 1
161
+ expect(results.first._source.to_hash).to eq person.as_jsonld
162
+ end
163
+
164
+ it 'should contain an ID for the subject' do
165
+ results = person.class.search('dc\:relation.@id:' + subject.id)
166
+ expect(results.count).to eq 1
167
+ end
168
+ end
169
+
170
+ context 'with as_jsonld related' do
171
+ before do
172
+ person.class.index_for_search as: :jsonld, related: true
173
+ subject.class.index_for_search as: :jsonld, related: true
174
+ subject.save
175
+ Elasticsearch::Model.client.indices.flush
176
+ end
177
+
178
+ it 'should contain a embedded related object' do
179
+ results = subject.class.search('dc\:creator.foaf\:name.@value:tove')
180
+ expect(results.count).to eq 1
181
+ expect(results.first._source.to_hash['dc:creator']).to eq person.as_jsonld.except '@context'
182
+ end
183
+
184
+ it 'should contain an embedded subject in the related object' do
185
+ results = person.class.search('dc\:relation.dc\:title.@value:moomin*')
186
+ expect(results.count).to eq 1
187
+ expect(results.first._source.to_hash['dc:relation']).to eq subject.as_jsonld.except '@context'
188
+ end
189
+ end
190
+ end
191
+ end