ld4l-open_annotation_rdf 0.0.4

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.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE.txt +14 -0
  7. data/README.md +231 -0
  8. data/Rakefile +2 -0
  9. data/ld4l-open_annotation_rdf.gemspec +46 -0
  10. data/lib/ld4l/open_annotation_rdf.rb +66 -0
  11. data/lib/ld4l/open_annotation_rdf/annotation.rb +82 -0
  12. data/lib/ld4l/open_annotation_rdf/comment_annotation.rb +47 -0
  13. data/lib/ld4l/open_annotation_rdf/comment_body.rb +24 -0
  14. data/lib/ld4l/open_annotation_rdf/configuration.rb +127 -0
  15. data/lib/ld4l/open_annotation_rdf/semantic_tag_annotation.rb +66 -0
  16. data/lib/ld4l/open_annotation_rdf/semantic_tag_body.rb +70 -0
  17. data/lib/ld4l/open_annotation_rdf/tag_annotation.rb +98 -0
  18. data/lib/ld4l/open_annotation_rdf/tag_body.rb +83 -0
  19. data/lib/ld4l/open_annotation_rdf/version.rb +5 -0
  20. data/lib/ld4l/open_annotation_rdf/vocab/cnt.rb +6 -0
  21. data/lib/ld4l/open_annotation_rdf/vocab/dctypes.rb +5 -0
  22. data/lib/ld4l/open_annotation_rdf/vocab/oa.rb +23 -0
  23. data/spec/ld4l/open_annotation_rdf/annotation_spec.rb +603 -0
  24. data/spec/ld4l/open_annotation_rdf/comment_annotation_spec.rb +559 -0
  25. data/spec/ld4l/open_annotation_rdf/comment_body_spec.rb +371 -0
  26. data/spec/ld4l/open_annotation_rdf/configuration_spec.rb +194 -0
  27. data/spec/ld4l/open_annotation_rdf/semantic_tag_annotation_spec.rb +619 -0
  28. data/spec/ld4l/open_annotation_rdf/semantic_tag_body_spec.rb +412 -0
  29. data/spec/ld4l/open_annotation_rdf/tag_annotation_spec.rb +672 -0
  30. data/spec/ld4l/open_annotation_rdf/tag_body_spec.rb +430 -0
  31. data/spec/ld4l/open_annotation_rdf_spec.rb +57 -0
  32. data/spec/spec_helper.rb +21 -0
  33. metadata +201 -0
@@ -0,0 +1,412 @@
1
+ require 'spec_helper'
2
+ require 'ld4l/open_annotation_rdf/vocab/dctypes'
3
+ require 'ld4l/open_annotation_rdf/vocab/oa'
4
+
5
+
6
+ describe 'LD4L::OpenAnnotationRDF::SemanticTagBody' do
7
+
8
+ subject { LD4L::OpenAnnotationRDF::SemanticTagBody.new }
9
+
10
+ describe 'rdf_subject' do
11
+ it "should be a blank node if we haven't set it" do
12
+ expect(subject.rdf_subject.node?).to be true
13
+ end
14
+
15
+ it "should be settable when it has not been set yet" do
16
+ subject.set_subject! RDF::URI('http://example.org/moomin')
17
+ expect(subject.rdf_subject).to eq RDF::URI('http://example.org/moomin')
18
+ end
19
+
20
+ it "should append to base URI when setting to non-URI subject" do
21
+ subject.set_subject! '123'
22
+ expect(subject.rdf_subject).to eq RDF::URI("#{LD4L::OpenAnnotationRDF::SemanticTagBody.base_uri}123")
23
+ end
24
+
25
+ describe 'when changing subject' do
26
+ before do
27
+ subject << RDF::Statement.new(subject.rdf_subject, RDF::DC.title, RDF::Literal('Comet in Moominland'))
28
+ subject << RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.isPartOf, subject.rdf_subject)
29
+ subject << RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.relation, 'http://example.org/moomin_land')
30
+ subject.set_subject! RDF::URI('http://example.org/moomin')
31
+ end
32
+
33
+ it 'should update graph subjects' do
34
+ expect(subject.has_statement?(RDF::Statement.new(subject.rdf_subject, RDF::DC.title, RDF::Literal('Comet in Moominland')))).to be true
35
+ end
36
+
37
+ it 'should update graph objects' do
38
+ expect(subject.has_statement?(RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.isPartOf, subject.rdf_subject))).to be true
39
+ end
40
+
41
+ it 'should leave other uris alone' do
42
+ expect(subject.has_statement?(RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.relation, 'http://example.org/moomin_land'))).to be true
43
+ end
44
+ end
45
+
46
+ describe 'created with URI subject' do
47
+ before do
48
+ subject.set_subject! RDF::URI('http://example.org/moomin')
49
+ end
50
+
51
+ it 'should not be settable' do
52
+ expect{ subject.set_subject! RDF::URI('http://example.org/moomin2') }.to raise_error
53
+ end
54
+ end
55
+ end
56
+
57
+
58
+ # -------------------------------------------------
59
+ # START -- Test attributes specific to this model
60
+ # -------------------------------------------------
61
+
62
+ describe 'type' do
63
+ it "should be set to text and astext from new" do
64
+ expect(subject.type.size).to eq 1
65
+ expect(subject.type).to include RDFVocabularies::OA.SemanticTag
66
+ end
67
+ end
68
+
69
+ describe '#localname_prefix' do
70
+ it "should return default prefix" do
71
+ prefix = LD4L::OpenAnnotationRDF::SemanticTagBody.localname_prefix
72
+ expect(prefix).to eq "stb"
73
+ end
74
+ end
75
+
76
+ describe "#annotations_using" do
77
+
78
+ context "when term value is nil" do
79
+ it "should throw invalid arguement exception" do
80
+ expect{ LD4L::OpenAnnotationRDF::SemanticTagBody.annotations_using(nil) }.to raise_error
81
+ end
82
+ end
83
+
84
+ context "when term value is a string of 0 length" do
85
+ it "should throw invalid arguement exception" do
86
+ expect{ LD4L::OpenAnnotationRDF::SemanticTagBody.annotations_using("") }.to raise_error
87
+ end
88
+ end
89
+
90
+ context "when term is not a string or uri" do
91
+ it "should throw invalid arguement exception" do
92
+ expect{ LD4L::OpenAnnotationRDF::SemanticTagBody.annotations_using(3) }.to raise_error
93
+ end
94
+ end
95
+
96
+ context "when terms exist in the repository" do
97
+ before(:all) do
98
+ # Create inmemory repository
99
+ sta = LD4L::OpenAnnotationRDF::SemanticTagAnnotation.new('http://example.org/sta1')
100
+ sta.setTerm(RDF::URI("http://example.org/EXISTING_term"))
101
+ sta.persist!
102
+ sta = LD4L::OpenAnnotationRDF::SemanticTagAnnotation.new('http://example.org/sta2')
103
+ sta.setTerm(RDF::URI("http://example.org/EXISTING_term"))
104
+ sta.persist!
105
+ stb = LD4L::OpenAnnotationRDF::SemanticTagBody.new('http://example.org/UNUSED_term')
106
+ stb.persist!
107
+ end
108
+ after(:all) do
109
+ LD4L::OpenAnnotationRDF::SemanticTagAnnotation.new('http://example.org/sta1').destroy!
110
+ LD4L::OpenAnnotationRDF::SemanticTagAnnotation.new('http://example.org/sta2').destroy!
111
+ LD4L::OpenAnnotationRDF::SemanticTagBody.new('http://example.org/UNUSED_term').destroy!
112
+ end
113
+
114
+ context "and term is passed as string URI" do
115
+ it "should find annotations using the term" do
116
+ annotations = LD4L::OpenAnnotationRDF::SemanticTagBody.annotations_using('http://example.org/EXISTING_term')
117
+ expect( annotations.include?(RDF::URI('http://example.org/sta1')) ).to be true
118
+ expect( annotations.include?(RDF::URI('http://example.org/sta2')) ).to be true
119
+ expect( annotations.size ).to be 2
120
+ end
121
+
122
+ it "should find 0 annotations for unused term" do
123
+ annotations = LD4L::OpenAnnotationRDF::SemanticTagBody.annotations_using('http://example.org/UNUSED_term')
124
+ expect( annotations ).to eq []
125
+ end
126
+
127
+ it "should find 0 annotations for non-existent term" do
128
+ annotations = LD4L::OpenAnnotationRDF::SemanticTagBody.annotations_using('http://example.org/NONEXISTING_term')
129
+ expect( annotations ).to eq []
130
+ end
131
+ end
132
+
133
+ context "and term is passed as RDF::URI" do
134
+ it "should find annotations using the term" do
135
+ annotations = LD4L::OpenAnnotationRDF::SemanticTagBody.annotations_using(RDF::URI('http://example.org/EXISTING_term'))
136
+ expect( annotations.include?(RDF::URI('http://example.org/sta1')) ).to be true
137
+ expect( annotations.include?(RDF::URI('http://example.org/sta2')) ).to be true
138
+ expect( annotations.size ).to be 2
139
+ end
140
+
141
+ it "should find 0 annotations for unused term" do
142
+ annotations = LD4L::OpenAnnotationRDF::SemanticTagBody.annotations_using(RDF::URI('http://example.org/UNUSED_term'))
143
+ expect( annotations ).to eq []
144
+ end
145
+
146
+ it "should find 0 annotations for non-existent term" do
147
+ annotations = LD4L::OpenAnnotationRDF::SemanticTagBody.annotations_using(RDF::URI'http://example.org/NONEXISTING_term')
148
+ expect( annotations ).to eq []
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ describe "#destroy_if_unused" do
155
+ context "when term is nil" do
156
+ it "should throw invalid arguement exception" do
157
+ expect{ LD4L::OpenAnnotationRDF::SemanticTagBody.destroy_if_unused(nil) }.to raise_error
158
+ end
159
+ end
160
+
161
+ context "when term is a string of 0 length" do
162
+ it "should throw invalid arguement exception" do
163
+ expect{ LD4L::OpenAnnotationRDF::SemanticTagBody.destroy_if_unused("") }.to raise_error
164
+ end
165
+ end
166
+
167
+ context "when term is not a string or uri" do
168
+ it "should throw invalid arguement exception" do
169
+ expect{ LD4L::OpenAnnotationRDF::SemanticTagBody.destroy_if_unused(3) }.to raise_error
170
+ end
171
+ end
172
+
173
+ context "when terms exist in the repository" do
174
+ before(:all) do
175
+ # Create inmemory repository
176
+ sta = LD4L::OpenAnnotationRDF::SemanticTagAnnotation.new('http://example.org/sta1')
177
+ sta.setTerm(RDF::URI("http://example.org/EXISTING_term"))
178
+ sta.persist!
179
+ sta = LD4L::OpenAnnotationRDF::SemanticTagAnnotation.new('http://example.org/sta2')
180
+ sta.setTerm(RDF::URI("http://example.org/EXISTING_term"))
181
+ sta.persist!
182
+ stb = LD4L::OpenAnnotationRDF::SemanticTagBody.new('http://example.org/UNUSED_term')
183
+ stb.persist!
184
+ end
185
+ after(:all) do
186
+ LD4L::OpenAnnotationRDF::SemanticTagAnnotation.new('http://example.org/sta1').destroy!
187
+ LD4L::OpenAnnotationRDF::SemanticTagAnnotation.new('http://example.org/sta2').destroy!
188
+ LD4L::OpenAnnotationRDF::SemanticTagBody.new('http://example.org/UNUSED_term').destroy!
189
+ end
190
+
191
+ context "and term is passed as string URI" do
192
+ it "should not destroy if used by any annotations" do
193
+ expect( LD4L::OpenAnnotationRDF::SemanticTagBody.destroy_if_unused('http://example.org/EXISTING_term') ).to be false
194
+ end
195
+
196
+ it "should destory if not used by any annotations" do
197
+ expect( LD4L::OpenAnnotationRDF::SemanticTagBody.destroy_if_unused('http://example.org/UNUSED_term') ).to be true
198
+ end
199
+
200
+ it "will destory if term doesn't exist" do
201
+ # NOTE: ActiveTriples.destroy! persists the object to be destroyed before destroying it
202
+ expect( LD4L::OpenAnnotationRDF::SemanticTagBody.destroy_if_unused('http://example.org/NONEXISTENT_term') ).to be true
203
+ end
204
+ end
205
+
206
+ context "and term is passed as RDF::URI" do
207
+ it "should not destroy if used by any annotations" do
208
+ expect( LD4L::OpenAnnotationRDF::SemanticTagBody.destroy_if_unused(RDF::URI('http://example.org/EXISTING_term')) ).to be false
209
+ end
210
+
211
+ it "should destory if not used by any annotations" do
212
+ expect( LD4L::OpenAnnotationRDF::SemanticTagBody.destroy_if_unused(RDF::URI('http://example.org/UNUSED_term')) ).to be true
213
+ end
214
+
215
+ it "will destory if term doesn't exist" do
216
+ # NOTE: ActiveTriples.destroy! persists the object to be destroyed before destroying it
217
+ expect( LD4L::OpenAnnotationRDF::SemanticTagBody.destroy_if_unused(RDF::URI('http://example.org/NONEXISTENT_term')) ).to be true
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ # -----------------------------------------------
224
+ # END -- Test attributes specific to this model
225
+ # -----------------------------------------------
226
+
227
+
228
+ describe "#persisted?" do
229
+ context 'with a repository' do
230
+ before do
231
+ # Create inmemory repository
232
+ repository = RDF::Repository.new
233
+ allow(subject).to receive(:repository).and_return(repository)
234
+ end
235
+
236
+ context "when the object is new" do
237
+ it "should return false" do
238
+ expect(subject).not_to be_persisted
239
+ end
240
+ end
241
+
242
+ context "when it is saved" do
243
+ before do
244
+ subject.persist!
245
+ end
246
+
247
+ it "should return true" do
248
+ expect(subject).to be_persisted
249
+ end
250
+
251
+ context "and then reloaded" do
252
+ before do
253
+ subject.reload
254
+ end
255
+
256
+ it "should be persisted" do
257
+ expect(subject).to be_persisted
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
263
+
264
+ describe "#persist!" do
265
+ context "when the repository is set" do
266
+ context "and the item is not a blank node" do
267
+
268
+ subject {LD4L::OpenAnnotationRDF::SemanticTagBody.new("123")}
269
+
270
+ before do
271
+ # Create inmemory repository
272
+ @repo = RDF::Repository.new
273
+ allow(subject.class).to receive(:repository).and_return(nil)
274
+ allow(subject).to receive(:repository).and_return(@repo)
275
+ subject.persist!
276
+ end
277
+
278
+ it "should persist to the repository" do
279
+ expect(@repo.statements.first).to eq subject.statements.first
280
+ end
281
+
282
+ it "should delete from the repository" do
283
+ subject.reload
284
+ subject.persist!
285
+ subject.reload
286
+ expect(@repo.statements.to_a.length).to eq 1 # Only the 1 type statements
287
+ end
288
+ end
289
+ end
290
+ end
291
+
292
+ describe '#destroy!' do
293
+ before do
294
+ subject << RDF::Statement(RDF::DC.LicenseDocument, RDF::DC.title, 'LICENSE')
295
+ end
296
+
297
+ subject { LD4L::OpenAnnotationRDF::SemanticTagBody.new('456')}
298
+
299
+ it 'should return true' do
300
+ expect(subject.destroy!).to be true
301
+ expect(subject.destroy).to be true
302
+ end
303
+
304
+ it 'should delete the graph' do
305
+ subject.destroy
306
+ expect(subject).to be_empty
307
+ end
308
+ end
309
+
310
+ describe '#rdf_label' do
311
+ subject {LD4L::OpenAnnotationRDF::SemanticTagBody.new("123")}
312
+
313
+ it 'should return an array of label values' do
314
+ expect(subject.rdf_label).to be_kind_of Array
315
+ end
316
+
317
+ it 'should return the default label as URI when no title property exists' do
318
+ expect(subject.rdf_label).to eq [RDF::URI("#{LD4L::OpenAnnotationRDF::SemanticTagBody.base_uri}123")]
319
+ end
320
+
321
+ it 'should prioritize configured label values' do
322
+ custom_label = RDF::URI('http://example.org/custom_label')
323
+ subject.class.configure :rdf_label => custom_label
324
+ subject << RDF::Statement(subject.rdf_subject, custom_label, RDF::Literal('New Label'))
325
+ expect(subject.rdf_label).to eq ['New Label']
326
+ end
327
+ end
328
+
329
+ describe '#solrize' do
330
+ it 'should return a label for bnodes' do
331
+ expect(subject.solrize).to eq subject.rdf_label
332
+ end
333
+
334
+ it 'should return a string of the resource uri' do
335
+ subject.set_subject! 'http://example.org/moomin'
336
+ expect(subject.solrize).to eq 'http://example.org/moomin'
337
+ end
338
+ end
339
+
340
+ describe 'big complex graphs' do
341
+ before do
342
+ class DummyPerson < ActiveTriples::Resource
343
+ configure :type => RDF::URI('http://example.org/Person')
344
+ property :foafname, :predicate => RDF::FOAF.name
345
+ property :publications, :predicate => RDF::FOAF.publications, :class_name => 'DummyDocument'
346
+ property :knows, :predicate => RDF::FOAF.knows, :class_name => DummyPerson
347
+ end
348
+
349
+ class DummyDocument < ActiveTriples::Resource
350
+ configure :type => RDF::URI('http://example.org/Document')
351
+ property :title, :predicate => RDF::DC.title
352
+ property :creator, :predicate => RDF::DC.creator, :class_name => 'DummyPerson'
353
+ end
354
+
355
+ LD4L::OpenAnnotationRDF::SemanticTagBody.property :item, :predicate => RDF::DC.relation, :class_name => DummyDocument
356
+ end
357
+
358
+ subject { LD4L::OpenAnnotationRDF::SemanticTagBody.new }
359
+
360
+ let (:document1) do
361
+ d = DummyDocument.new
362
+ d.title = 'Document One'
363
+ d
364
+ end
365
+
366
+ let (:document2) do
367
+ d = DummyDocument.new
368
+ d.title = 'Document Two'
369
+ d
370
+ end
371
+
372
+ let (:person1) do
373
+ p = DummyPerson.new
374
+ p.foafname = 'Alice'
375
+ p
376
+ end
377
+
378
+ let (:person2) do
379
+ p = DummyPerson.new
380
+ p.foafname = 'Bob'
381
+ p
382
+ end
383
+
384
+ let (:data) { <<END
385
+ _:1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/SomeClass> .
386
+ _:1 <http://purl.org/dc/terms/relation> _:2 .
387
+ _:2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Document> .
388
+ _:2 <http://purl.org/dc/terms/title> "Document One" .
389
+ _:2 <http://purl.org/dc/terms/creator> _:3 .
390
+ _:2 <http://purl.org/dc/terms/creator> _:4 .
391
+ _:4 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> .
392
+ _:4 <http://xmlns.com/foaf/0.1/name> "Bob" .
393
+ _:3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> .
394
+ _:3 <http://xmlns.com/foaf/0.1/name> "Alice" .
395
+ _:3 <http://xmlns.com/foaf/0.1/knows> _:4 ."
396
+ END
397
+ }
398
+
399
+ after do
400
+ Object.send(:remove_const, "DummyDocument")
401
+ Object.send(:remove_const, "DummyPerson")
402
+ end
403
+
404
+ it 'should allow access to deep nodes' do
405
+ document1.creator = [person1, person2]
406
+ document2.creator = person1
407
+ person1.knows = person2
408
+ subject.item = [document1]
409
+ expect(subject.item.first.creator.first.knows.first.foafname).to eq ['Bob']
410
+ end
411
+ end
412
+ end
@@ -0,0 +1,672 @@
1
+ require 'spec_helper'
2
+
3
+ # TODO: Uses CommentBody as the annotation body, which is wrong. Should be URI into controlled vocabulary.
4
+
5
+ describe 'LD4L::OpenAnnotationRDF::TagAnnotation' do
6
+
7
+ subject { LD4L::OpenAnnotationRDF::TagAnnotation.new }
8
+
9
+ describe 'rdf_subject' do
10
+ it "should be a blank node if we haven't set it" do
11
+ expect(subject.rdf_subject.node?).to be true
12
+ end
13
+
14
+ it "should be settable when it has not been set yet" do
15
+ subject.set_subject! RDF::URI('http://example.org/moomin')
16
+ expect(subject.rdf_subject).to eq RDF::URI('http://example.org/moomin')
17
+ end
18
+
19
+ it "should append to base URI when setting to non-URI subject" do
20
+ subject.set_subject! '123'
21
+ expect(subject.rdf_subject).to eq RDF::URI("#{LD4L::OpenAnnotationRDF::TagAnnotation.base_uri}123")
22
+ end
23
+
24
+ describe 'when changing subject' do
25
+ before do
26
+ subject << RDF::Statement.new(subject.rdf_subject, RDF::DC.title, RDF::Literal('Comet in Moominland'))
27
+ subject << RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.isPartOf, subject.rdf_subject)
28
+ subject << RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.relation, 'http://example.org/moomin_land')
29
+ subject.set_subject! RDF::URI('http://example.org/moomin')
30
+ end
31
+
32
+ it 'should update graph subjects' do
33
+ expect(subject.has_statement?(RDF::Statement.new(subject.rdf_subject, RDF::DC.title, RDF::Literal('Comet in Moominland')))).to be true
34
+ end
35
+
36
+ it 'should update graph objects' do
37
+ expect(subject.has_statement?(RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.isPartOf, subject.rdf_subject))).to be true
38
+ end
39
+
40
+ it 'should leave other uris alone' do
41
+ expect(subject.has_statement?(RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.relation, 'http://example.org/moomin_land'))).to be true
42
+ end
43
+ end
44
+
45
+ describe 'created with URI subject' do
46
+ before do
47
+ subject.set_subject! RDF::URI('http://example.org/moomin')
48
+ end
49
+
50
+ it 'should not be settable' do
51
+ expect{ subject.set_subject! RDF::URI('http://example.org/moomin2') }.to raise_error
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+ # -------------------------------------------------
58
+ # START -- Test attributes specific to this model
59
+ # -------------------------------------------------
60
+
61
+ describe 'type' do
62
+ it "should be an RDFVocabularies::OA.Annotation" do
63
+ expect(subject.type.first.value).to eq RDFVocabularies::OA.Annotation.value
64
+ end
65
+ end
66
+
67
+ describe 'hasTarget' do
68
+ it "should be empty array if we haven't set it" do
69
+ expect(subject.hasTarget).to match_array([])
70
+ end
71
+
72
+ it "should be settable" do
73
+ subject.hasTarget = RDF::URI("http://example.org/b123")
74
+ expect(subject.hasTarget.first.rdf_subject.to_s).to eq "http://example.org/b123"
75
+ end
76
+
77
+ it "should be changeable" do
78
+ subject.hasTarget = RDF::URI("http://example.org/b123")
79
+ subject.hasTarget = RDF::URI("http://example.org/b123_NEW")
80
+ expect(subject.hasTarget.first.rdf_subject.to_s).to eq "http://example.org/b123_NEW"
81
+ end
82
+ end
83
+
84
+ describe 'hasBody' do
85
+ # NOTE: Preferred method to set body is to use setTag method which will
86
+ # create the appropriate annotation body object and triples.
87
+ it "should be empty array if we haven't set it" do
88
+ expect(subject.hasBody).to match_array([])
89
+ end
90
+
91
+ it "should be settable" do
92
+ a_open_annotation_body = LD4L::OpenAnnotationRDF::TagBody.new('foo')
93
+ subject.hasBody = a_open_annotation_body
94
+ expect(subject.hasBody.first).to eq a_open_annotation_body
95
+ end
96
+
97
+ it "should be changeable" do
98
+ orig_open_annotation_body = LD4L::OpenAnnotationRDF::TagBody.new('foo')
99
+ new_open_annotation_body = LD4L::OpenAnnotationRDF::TagBody.new('bar')
100
+ subject.hasBody = orig_open_annotation_body
101
+ subject.hasBody = new_open_annotation_body
102
+ expect(subject.hasBody.first).to eq new_open_annotation_body
103
+ end
104
+ end
105
+
106
+ describe '#setTag' do
107
+ # TODO Need fuller set of tests for all potential behaviours.
108
+ before do
109
+ @repo = RDF::Repository.new
110
+ allow(subject.class).to receive(:repository).and_return(nil)
111
+ allow(subject).to receive(:repository).and_return(@repo)
112
+ end
113
+
114
+ context "when new value is nil" do
115
+ it "should throw invalid arguement exception" do
116
+ expect{ subject.setTag(nil) }.to raise_error
117
+ end
118
+ end
119
+
120
+ context "when new value is a string of 0 length" do
121
+ it "should throw invalid arguement exception" do
122
+ expect{ subject.setTag("") }.to raise_error
123
+ end
124
+ end
125
+
126
+ context "when new value is not a string" do
127
+ it "should throw invalid arguement exception" do
128
+ expect{ subject.setTag(3) }.to raise_error
129
+ end
130
+ end
131
+
132
+ context "when new value is same as old value" do
133
+ it "should return the existing TagBody unchanged" do
134
+ tb1 = subject.setTag('foo')
135
+ tb2 = subject.setTag('foo')
136
+ expect(tb2).to eq tb1
137
+ end
138
+ end
139
+
140
+ context "when config enforces unique tags" do
141
+ before do
142
+ LD4L::OpenAnnotationRDF.configuration.unique_tags = true
143
+ end
144
+ after do
145
+ LD4L::OpenAnnotationRDF.configuration.reset_unique_tags
146
+ end
147
+
148
+ context "and tag doesn't already exist as a TagBody" do
149
+ it "should create an instance of LD4L::OpenAnnotationRDF::TagBody and set hasBody property to it" do
150
+ # verify tag value doesn't already exist
151
+ tb = LD4L::OpenAnnotationRDF::TagBody.fetch_by_tag_value('foo')
152
+ expect(tb).to be_nil
153
+
154
+ subject.setTag('foo')
155
+ expect(subject.hasBody.first.tag.first).to eq 'foo'
156
+ expect(subject.getBody.tag.first).to eq 'foo'
157
+ end
158
+ end
159
+
160
+ context "and tag already exists as a TagBody" do
161
+ before do
162
+ tb = LD4L::OpenAnnotationRDF::TagBody.new('http://example.org/existing_tag')
163
+ tb.tag = 'foo'
164
+ tb.persist!
165
+ expect(tb).to be_persisted
166
+ end
167
+ it "should resume the existing LD4L::OpenAnnotationRDF::TagBody and set hasBody property to it" do
168
+ tb = LD4L::OpenAnnotationRDF::TagBody.new('http://example.org/existing_tag')
169
+ expect(tb.tag).to eq ['foo']
170
+ expect(tb).to be_persisted
171
+
172
+ subject.setTag('foo')
173
+ expect(subject.hasBody.first.rdf_subject.to_s).to eq 'http://example.org/existing_tag'
174
+ expect(subject.getBody.rdf_subject.to_s).to eq 'http://example.org/existing_tag'
175
+ expect(subject.getBody.rdf_subject).to eq tb.rdf_subject
176
+ # NOTE: body is considered not persisted because it's parent, the annotation, is not persisted
177
+ expect(subject.getBody).not_to be_persisted
178
+ end
179
+ end
180
+ end
181
+
182
+ context "when config does not enforce unique tags" do
183
+ before do
184
+ LD4L::OpenAnnotationRDF.configuration.unique_tags = false
185
+ end
186
+ after do
187
+ LD4L::OpenAnnotationRDF.configuration.reset_unique_tags
188
+ end
189
+
190
+ context "and tag doesn't already exist as a TagBody" do
191
+ it "should create an instance of LD4L::OpenAnnotationRDF::TagBody and set hasBody property to it" do
192
+ # verify tag value doesn't already exist
193
+ tb = LD4L::OpenAnnotationRDF::TagBody.fetch_by_tag_value('new_foo')
194
+ expect(tb).to be_nil
195
+
196
+ subject.setTag('new_foo')
197
+ expect(subject.hasBody.first.tag.first).to eq 'new_foo'
198
+ expect(subject.getBody.tag.first).to eq 'new_foo'
199
+ end
200
+ end
201
+
202
+ context "and tag already exists as a TagBody" do
203
+ before do
204
+ tb = LD4L::OpenAnnotationRDF::TagBody.new('http://example.org/existing_tag')
205
+ tb.tag = 'foo'
206
+ tb.persist!
207
+ expect(tb).to be_persisted
208
+ end
209
+ it "should create an instance of LD4L::OpenAnnotationRDF::TagBody and set hasBody property to it" do
210
+ tb = LD4L::OpenAnnotationRDF::TagBody.new('http://example.org/existing_tag')
211
+ expect(tb.tag).to eq ['foo']
212
+ expect(tb).to be_persisted
213
+
214
+ subject.setTag('foo')
215
+ expect(subject.hasBody.first.rdf_subject.to_s).not_to eq 'http://example.org/existing_tag'
216
+ expect(subject.getBody.rdf_subject.to_s).not_to eq 'http://example.org/existing_tag'
217
+ expect(subject.getBody.rdf_subject).not_to eq tb.rdf_subject
218
+ # NOTE: body is considered not persisted because it's parent, the annotation, is not persisted
219
+ expect(subject.getBody).not_to be_persisted
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ describe 'annotatedBy' do
226
+ it "should be empty array if we haven't set it" do
227
+ expect(subject.annotatedBy).to match_array([])
228
+ end
229
+
230
+ it "should be settable" do
231
+ a_person = LD4L::FoafRDF::Person.new('1')
232
+ subject.annotatedBy = a_person
233
+ expect(subject.annotatedBy.first).to eq a_person
234
+ end
235
+
236
+ it "should be changeable" do
237
+ orig_person = LD4L::FoafRDF::Person.new('1')
238
+ new_person = LD4L::FoafRDF::Person.new('2')
239
+ subject.annotatedBy = orig_person
240
+ subject.annotatedBy = new_person
241
+ expect(subject.annotatedBy.first).to eq new_person
242
+ end
243
+ end
244
+
245
+ describe 'annotatedAt' do
246
+ it "should be empty array if we haven't set it" do
247
+ expect(subject.annotatedAt).to match_array([])
248
+ end
249
+
250
+ it "should be settable" do
251
+ a_time = Time::now.strftime("%Y-%m-%dT%H:%M:%S.%L%z")
252
+ subject.annotatedAt = a_time
253
+ expect(subject.annotatedAt.first).to eq a_time
254
+ end
255
+
256
+ it "should be changeable" do
257
+ orig_time = Time.local(2014, 6, 1, 8, 30).strftime("%Y-%m-%dT%H:%M:%S.%L%z")
258
+ new_time = Time.now.strftime("%Y-%m-%dT%H:%M:%S.%L%z")
259
+ subject.annotatedAt = orig_time
260
+ subject.annotatedAt = new_time
261
+ expect(subject.annotatedAt.first).to eq new_time
262
+ end
263
+ end
264
+
265
+ describe 'motivatedBy' do
266
+ it "should be OA.tagging if we haven't set it" do
267
+ expect(subject.motivatedBy.first.rdf_subject).to eq RDFVocabularies::OA.tagging
268
+ end
269
+
270
+ it "should be settable" do
271
+ subject.motivatedBy = RDFVocabularies::OA.describing
272
+ expect(subject.motivatedBy.first.rdf_subject).to eq RDFVocabularies::OA.describing
273
+ end
274
+
275
+ it "should be changeable" do
276
+ subject.motivatedBy = RDFVocabularies::OA.describing
277
+ subject.motivatedBy = RDFVocabularies::OA.classifying
278
+ expect(subject.motivatedBy.first.rdf_subject).to eq RDFVocabularies::OA.classifying
279
+ end
280
+ end
281
+
282
+ describe '#localname_prefix' do
283
+ it "should return default prefix" do
284
+ prefix = LD4L::OpenAnnotationRDF::TagAnnotation.localname_prefix
285
+ expect(prefix).to eq "ta"
286
+ end
287
+ end
288
+
289
+ # -----------------------------------------------
290
+ # END -- Test attributes specific to this model
291
+ # -----------------------------------------------
292
+
293
+
294
+ describe "#persisted?" do
295
+ context 'with a repository' do
296
+ before do
297
+ # Create inmemory repository
298
+ repository = RDF::Repository.new
299
+ allow(subject).to receive(:repository).and_return(repository)
300
+ end
301
+
302
+ context "when the object is new" do
303
+ it "should return false" do
304
+ expect(subject).not_to be_persisted
305
+ end
306
+ end
307
+
308
+ context "when it is saved" do
309
+ before do
310
+ subject.motivatedBy = RDFVocabularies::OA.commenting
311
+ subject.persist!
312
+ end
313
+
314
+ it "should return true" do
315
+ expect(subject).to be_persisted
316
+ end
317
+
318
+ context "and then modified" do
319
+ before do
320
+ subject.motivatedBy = RDFVocabularies::OA.tagging
321
+ end
322
+
323
+ it "should return true" do
324
+ expect(subject).to be_persisted
325
+ end
326
+ end
327
+ context "and then reloaded" do
328
+ before do
329
+ subject.reload
330
+ end
331
+
332
+ it "should reset the motivatedBy" do
333
+ expect(subject.motivatedBy.first.rdf_subject.to_s).to eq RDFVocabularies::OA.commenting.to_s
334
+ end
335
+
336
+ it "should be persisted" do
337
+ expect(subject).to be_persisted
338
+ end
339
+ end
340
+ end
341
+ end
342
+ end
343
+
344
+ describe "#persist!" do
345
+ context "when the repository is set" do
346
+ context "and the annotation is not a blank node" do
347
+
348
+ subject {LD4L::OpenAnnotationRDF::TagAnnotation.new("123")}
349
+
350
+ before do
351
+ # Create inmemory repository
352
+ @repo = RDF::Repository.new
353
+ allow(subject.class).to receive(:repository).and_return(nil)
354
+ allow(subject).to receive(:repository).and_return(@repo)
355
+ subject.motivatedBy = RDFVocabularies::OA.commenting
356
+ subject.persist!
357
+ end
358
+
359
+ it "should persist to the repository" do
360
+ expect(@repo.statements.first).to eq subject.statements.first
361
+ end
362
+
363
+ it "should delete from the repository" do
364
+ subject.reload
365
+ expect(subject.motivatedBy.first.rdf_subject.to_s).to eq RDFVocabularies::OA.commenting.to_s
366
+ subject.motivatedBy = []
367
+ expect(subject.motivatedBy).to eq []
368
+ subject.persist!
369
+ subject.reload
370
+ expect(subject.annotatedAt).to eq []
371
+ expect(@repo.statements.to_a.length).to eq 1 # Only the type statement
372
+ end
373
+
374
+ context "and body is set" do
375
+ before do
376
+ subject.setTag('foo')
377
+ subject.persist!
378
+ end
379
+ it "should persist body to the repository" do
380
+ tb = LD4L::OpenAnnotationRDF::TagBody.new(subject.getBody.rdf_subject)
381
+ expect(tb).to be_persisted
382
+ tb = LD4L::OpenAnnotationRDF::TagBody.fetch_by_tag_value('foo')
383
+ expect(tb).to be_persisted
384
+ expect(subject.getBody.rdf_subject.to_s).to eq tb.rdf_subject.to_s
385
+ end
386
+ end
387
+ end
388
+ end
389
+ end
390
+
391
+ describe '#destroy!' do
392
+ before do
393
+ subject << RDF::Statement(RDF::DC.LicenseDocument, RDF::DC.title, 'LICENSE')
394
+ end
395
+
396
+ subject { LD4L::OpenAnnotationRDF::TagAnnotation.new('123') }
397
+
398
+ it 'should return true' do
399
+ expect(subject.destroy!).to be true
400
+ expect(subject.destroy).to be true
401
+ end
402
+
403
+ it 'should delete the graph' do
404
+ subject.destroy
405
+ expect(subject).to be_empty
406
+ end
407
+
408
+ context 'with a parent' do
409
+ before do
410
+ subject.annotatedBy = child
411
+ end
412
+
413
+ let(:child) do
414
+ LD4L::FoafRDF::Person.new('456')
415
+ end
416
+
417
+ it 'should empty the graph and remove it from the parent' do
418
+ child.destroy
419
+ expect(subject.annotatedBy).to be_empty
420
+ end
421
+
422
+ it 'should remove its whole graph from the parent' do
423
+ child.destroy
424
+ child.each_statement do |s|
425
+ expect(subject.statements).not_to include s
426
+ end
427
+ end
428
+ end
429
+
430
+ context 'with annotation body' do
431
+ # TODO Need fuller set of tests for all potential behaviours.
432
+ before do
433
+ subject.setTag('foo')
434
+ end
435
+
436
+ context 'and body is set on the annotation' do
437
+ let(:child) do
438
+ subject.getBody
439
+ end
440
+
441
+ it 'should empty the graph and remove it from the parent' do
442
+ child.destroy
443
+ expect(subject.hasBody).to be_empty
444
+ end
445
+
446
+ it 'should remove its whole graph from the parent' do
447
+ child.destroy
448
+ child.each_statement do |s|
449
+ expect(subject.statements).not_to include s
450
+ end
451
+ end
452
+ end
453
+ end
454
+ end
455
+
456
+ describe 'attributes' do
457
+ before do
458
+ subject.annotatedBy = annotatedBy
459
+ subject.motivatedBy = 'commenting'
460
+ end
461
+
462
+ subject {LD4L::OpenAnnotationRDF::TagAnnotation.new("123")}
463
+
464
+ let(:annotatedBy) { LD4L::FoafRDF::Person.new('456') }
465
+
466
+ it 'should return an attributes hash' do
467
+ expect(subject.attributes).to be_a Hash
468
+ end
469
+
470
+ it 'should contain data' do
471
+ expect(subject.attributes['motivatedBy']).to eq ['commenting']
472
+ end
473
+
474
+ it 'should contain child objects' do
475
+ expect(subject.attributes['annotatedBy']).to eq [annotatedBy]
476
+ end
477
+
478
+ context 'with unmodeled data' do
479
+ before do
480
+ subject << RDF::Statement(subject.rdf_subject, RDF::DC.contributor, 'Tove Jansson')
481
+ subject << RDF::Statement(subject.rdf_subject, RDF::DC.relation, RDF::URI('http://example.org/moomi'))
482
+ node = RDF::Node.new
483
+ subject << RDF::Statement(RDF::URI('http://example.org/moomi'), RDF::DC.relation, node)
484
+ subject << RDF::Statement(node, RDF::DC.title, 'bnode')
485
+ end
486
+
487
+ it 'should include data with URIs as attribute names' do
488
+ expect(subject.attributes[RDF::DC.contributor.to_s]).to eq ['Tove Jansson']
489
+ end
490
+
491
+ it 'should return generic Resources' do
492
+ expect(subject.attributes[RDF::DC.relation.to_s].first).to be_a ActiveTriples::Resource
493
+ end
494
+
495
+ it 'should build deep data for Resources' do
496
+ expect(subject.attributes[RDF::DC.relation.to_s].first.get_values(RDF::DC.relation).
497
+ first.get_values(RDF::DC.title)).to eq ['bnode']
498
+ end
499
+
500
+ it 'should include deep data in serializable_hash' do
501
+ expect(subject.serializable_hash[RDF::DC.relation.to_s].first.get_values(RDF::DC.relation).
502
+ first.get_values(RDF::DC.title)).to eq ['bnode']
503
+ end
504
+ end
505
+
506
+ describe 'attribute_serialization' do
507
+ describe '#to_json' do
508
+ it 'should return a string with correct objects' do
509
+ json_hash = JSON.parse(subject.to_json)
510
+ expect(json_hash['annotatedBy'].first['id']).to eq annotatedBy.rdf_subject.to_s
511
+ end
512
+ end
513
+ end
514
+ end
515
+
516
+ describe 'property methods' do
517
+ it 'should set and get properties' do
518
+ subject.motivatedBy = 'commenting'
519
+ expect(subject.motivatedBy).to eq ['commenting']
520
+ end
521
+ end
522
+
523
+ describe 'child nodes' do
524
+ it 'should return an object of the correct class when the value is built from the base URI' do
525
+ subject.annotatedBy = LD4L::FoafRDF::Person.new('456')
526
+ expect(subject.annotatedBy.first).to be_kind_of LD4L::FoafRDF::Person
527
+ end
528
+
529
+ it 'should return an object with the correct URI when created with a URI' do
530
+ subject.annotatedBy = LD4L::FoafRDF::Person.new("http://vivo.cornell.edu/individual/JohnSmith")
531
+ expect(subject.annotatedBy.first.rdf_subject).to eq RDF::URI("http://vivo.cornell.edu/individual/JohnSmith")
532
+ end
533
+
534
+ it 'should return an object of the correct class when the value is a bnode' do
535
+ subject.annotatedBy = LD4L::FoafRDF::Person.new
536
+ expect(subject.annotatedBy.first).to be_kind_of LD4L::FoafRDF::Person
537
+ end
538
+ end
539
+
540
+ describe '#type' do
541
+ it 'should return the type configured on the parent class' do
542
+ expect(subject.type).to eq [LD4L::OpenAnnotationRDF::TagAnnotation.type]
543
+ end
544
+
545
+ it 'should set the type' do
546
+ subject.type = RDF::URI('http://example.org/AnotherClass')
547
+ expect(subject.type).to eq [RDF::URI('http://example.org/AnotherClass')]
548
+ end
549
+
550
+ it 'should be the type in the graph' do
551
+ subject.query(:subject => subject.rdf_subject, :predicate => RDF.type).statements do |s|
552
+ expect(s.object).to eq RDF::URI('http://example.org/AnotherClass')
553
+ end
554
+ end
555
+ end
556
+
557
+ describe '#rdf_label' do
558
+ subject {LD4L::OpenAnnotationRDF::TagAnnotation.new("123")}
559
+
560
+ it 'should return an array of label values' do
561
+ expect(subject.rdf_label).to be_kind_of Array
562
+ end
563
+
564
+ it 'should return the default label as URI when no title property exists' do
565
+ expect(subject.rdf_label).to eq [RDF::URI("#{LD4L::OpenAnnotationRDF::TagAnnotation.base_uri}123")]
566
+ end
567
+
568
+ it 'should prioritize configured label values' do
569
+ custom_label = RDF::URI('http://example.org/custom_label')
570
+ subject.class.configure :rdf_label => custom_label
571
+ subject << RDF::Statement(subject.rdf_subject, custom_label, RDF::Literal('New Label'))
572
+ expect(subject.rdf_label).to eq ['New Label']
573
+ end
574
+ end
575
+
576
+ describe '#solrize' do
577
+ it 'should return a label for bnodes' do
578
+ expect(subject.solrize).to eq subject.rdf_label
579
+ end
580
+
581
+ it 'should return a string of the resource uri' do
582
+ subject.set_subject! 'http://example.org/moomin'
583
+ expect(subject.solrize).to eq 'http://example.org/moomin'
584
+ end
585
+ end
586
+
587
+ describe 'editing the graph' do
588
+ it 'should write properties when statements are added' do
589
+ subject << RDF::Statement.new(subject.rdf_subject, RDFVocabularies::OA.motivatedBy, 'commenting')
590
+ expect(subject.motivatedBy).to include 'commenting'
591
+ end
592
+
593
+ it 'should delete properties when statements are removed' do
594
+ subject << RDF::Statement.new(subject.rdf_subject, RDFVocabularies::OA.annotatedBy, 'John Smith')
595
+ subject.delete RDF::Statement.new(subject.rdf_subject, RDFVocabularies::OA.annotatedBy, 'John Smith')
596
+ expect(subject.annotatedBy).to eq []
597
+ end
598
+ end
599
+
600
+ describe 'big complex graphs' do
601
+ before do
602
+ class DummyPerson < ActiveTriples::Resource
603
+ configure :type => RDF::URI('http://example.org/Person')
604
+ property :foafname, :predicate => RDF::FOAF.name
605
+ property :publications, :predicate => RDF::FOAF.publications, :class_name => 'DummyDocument'
606
+ property :knows, :predicate => RDF::FOAF.knows, :class_name => DummyPerson
607
+ end
608
+
609
+ class DummyDocument < ActiveTriples::Resource
610
+ configure :type => RDF::URI('http://example.org/Document')
611
+ property :title, :predicate => RDF::DC.title
612
+ property :creator, :predicate => RDF::DC.creator, :class_name => 'DummyPerson'
613
+ end
614
+
615
+ LD4L::OpenAnnotationRDF::TagAnnotation.property :item, :predicate => RDF::DC.relation, :class_name => DummyDocument
616
+ end
617
+
618
+ subject { LD4L::OpenAnnotationRDF::TagAnnotation.new }
619
+
620
+ let (:document1) do
621
+ d = DummyDocument.new
622
+ d.title = 'Document One'
623
+ d
624
+ end
625
+
626
+ let (:document2) do
627
+ d = DummyDocument.new
628
+ d.title = 'Document Two'
629
+ d
630
+ end
631
+
632
+ let (:person1) do
633
+ p = DummyPerson.new
634
+ p.foafname = 'Alice'
635
+ p
636
+ end
637
+
638
+ let (:person2) do
639
+ p = DummyPerson.new
640
+ p.foafname = 'Bob'
641
+ p
642
+ end
643
+
644
+ let (:data) { <<END
645
+ _:1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/SomeClass> .
646
+ _:1 <http://purl.org/dc/terms/relation> _:2 .
647
+ _:2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Document> .
648
+ _:2 <http://purl.org/dc/terms/title> "Document One" .
649
+ _:2 <http://purl.org/dc/terms/creator> _:3 .
650
+ _:2 <http://purl.org/dc/terms/creator> _:4 .
651
+ _:4 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> .
652
+ _:4 <http://xmlns.com/foaf/0.1/name> "Bob" .
653
+ _:3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> .
654
+ _:3 <http://xmlns.com/foaf/0.1/name> "Alice" .
655
+ _:3 <http://xmlns.com/foaf/0.1/knows> _:4 ."
656
+ END
657
+ }
658
+
659
+ after do
660
+ Object.send(:remove_const, "DummyDocument")
661
+ Object.send(:remove_const, "DummyPerson")
662
+ end
663
+
664
+ it 'should allow access to deep nodes' do
665
+ document1.creator = [person1, person2]
666
+ document2.creator = person1
667
+ person1.knows = person2
668
+ subject.item = [document1]
669
+ expect(subject.item.first.creator.first.knows.first.foafname).to eq ['Bob']
670
+ end
671
+ end
672
+ end