ladder 0.1.0 → 0.1.1

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