ld4l-open_annotation_rdf 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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,559 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'LD4L::OpenAnnotationRDF::CommentAnnotation' do
4
+
5
+ subject { LD4L::OpenAnnotationRDF::CommentAnnotation.new }
6
+
7
+ describe 'rdf_subject' do
8
+ it "should be a blank node if we haven't set it" do
9
+ expect(subject.rdf_subject.node?).to be true
10
+ end
11
+
12
+ it "should be settable when it has not been set yet" do
13
+ subject.set_subject! RDF::URI('http://example.org/moomin')
14
+ expect(subject.rdf_subject).to eq RDF::URI('http://example.org/moomin')
15
+ end
16
+
17
+ it "should append to base URI when setting to non-URI subject" do
18
+ subject.set_subject! '123'
19
+ expect(subject.rdf_subject).to eq RDF::URI("#{LD4L::OpenAnnotationRDF::CommentAnnotation.base_uri}123")
20
+ end
21
+
22
+ describe 'when changing subject' do
23
+ before do
24
+ subject << RDF::Statement.new(subject.rdf_subject, RDF::DC.title, RDF::Literal('Comet in Moominland'))
25
+ subject << RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.isPartOf, subject.rdf_subject)
26
+ subject << RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.relation, 'http://example.org/moomin_land')
27
+ subject.set_subject! RDF::URI('http://example.org/moomin')
28
+ end
29
+
30
+ it 'should update graph subjects' do
31
+ expect(subject.has_statement?(RDF::Statement.new(subject.rdf_subject, RDF::DC.title, RDF::Literal('Comet in Moominland')))).to be true
32
+ end
33
+
34
+ it 'should update graph objects' do
35
+ expect(subject.has_statement?(RDF::Statement.new(RDF::URI('http://example.org/moomin_comics'), RDF::DC.isPartOf, subject.rdf_subject))).to be true
36
+ end
37
+
38
+ it 'should leave other uris alone' do
39
+ 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
40
+ end
41
+ end
42
+
43
+ describe 'created with URI subject' do
44
+ before do
45
+ subject.set_subject! RDF::URI('http://example.org/moomin')
46
+ end
47
+
48
+ it 'should not be settable' do
49
+ expect{ subject.set_subject! RDF::URI('http://example.org/moomin2') }.to raise_error
50
+ end
51
+ end
52
+ end
53
+
54
+
55
+ # -------------------------------------------------
56
+ # START -- Test attributes specific to this model
57
+ # -------------------------------------------------
58
+
59
+ describe 'type' do
60
+ it "should be an RDFVocabularies::OA.Annotation" do
61
+ expect(subject.type.first.value).to eq RDFVocabularies::OA.Annotation.value
62
+ end
63
+ end
64
+
65
+ describe 'hasTarget' do
66
+ it "should be empty array if we haven't set it" do
67
+ expect(subject.hasTarget).to match_array([])
68
+ end
69
+
70
+ it "should be settable" do
71
+ subject.hasTarget = RDF::URI("http://example.org/b123")
72
+ expect(subject.hasTarget.first.rdf_subject.to_s).to eq "http://example.org/b123"
73
+ end
74
+
75
+ it "should be changeable" do
76
+ subject.hasTarget = RDF::URI("http://example.org/b123")
77
+ subject.hasTarget = RDF::URI("http://example.org/b123_NEW")
78
+ expect(subject.hasTarget.first.rdf_subject.to_s).to eq "http://example.org/b123_NEW"
79
+ end
80
+ end
81
+
82
+ describe 'hasBody' do
83
+ # NOTE: Preferred method to set body is to use setComment method which will
84
+ # create the appropriate annotation body object and triples.
85
+ it "should be empty array if we haven't set it" do
86
+ expect(subject.hasBody).to match_array([])
87
+ end
88
+
89
+ it "should be settable" do
90
+ a_open_annotation_body = LD4L::OpenAnnotationRDF::CommentBody.new('1')
91
+ subject.hasBody = a_open_annotation_body
92
+ expect(subject.hasBody.first).to eq a_open_annotation_body
93
+ end
94
+
95
+ it "should be changeable" do
96
+ orig_open_annotation_body = LD4L::OpenAnnotationRDF::CommentBody.new('1')
97
+ new_open_annotation_body = LD4L::OpenAnnotationRDF::CommentBody.new('2')
98
+ subject.hasBody = orig_open_annotation_body
99
+ subject.hasBody = new_open_annotation_body
100
+ expect(subject.hasBody.first).to eq new_open_annotation_body
101
+ end
102
+ end
103
+
104
+ describe '#setComment' do
105
+ it "should create an instance of LD4L::OpenAnnotationRDF::CommentBody and set hasBody property to comment URI" do
106
+ subject.setComment('I like this.')
107
+ expect(subject.hasBody.first.rdf_subject).to be_kind_of RDF::URI
108
+ expect(subject.hasBody.first.rdf_subject.to_s).to match match /http:\/\/localhost\/[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}/
109
+ expect(subject.getBody).to be_kind_of LD4L::OpenAnnotationRDF::CommentBody
110
+ expect(subject.getBody.rdf_subject.to_s).to match match /http:\/\/localhost\/[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}/
111
+ expect(subject.getBody).not_to be_persisted
112
+ end
113
+ end
114
+
115
+ describe 'annotatedBy' do
116
+ it "should be empty array if we haven't set it" do
117
+ expect(subject.annotatedBy).to match_array([])
118
+ end
119
+
120
+ it "should be settable" do
121
+ a_person = LD4L::FoafRDF::Person.new('1')
122
+ subject.annotatedBy = a_person
123
+ expect(subject.annotatedBy.first).to eq a_person
124
+ end
125
+
126
+ it "should be changeable" do
127
+ orig_person = LD4L::FoafRDF::Person.new('1')
128
+ new_person = LD4L::FoafRDF::Person.new('2')
129
+ subject.annotatedBy = orig_person
130
+ subject.annotatedBy = new_person
131
+ expect(subject.annotatedBy.first).to eq new_person
132
+ end
133
+ end
134
+
135
+ describe 'annotatedAt' do
136
+ it "should be empty array if we haven't set it" do
137
+ expect(subject.annotatedAt).to match_array([])
138
+ end
139
+
140
+ it "should be settable" do
141
+ a_time = Time::now.strftime("%Y-%m-%dT%H:%M:%S.%L%z")
142
+ subject.annotatedAt = a_time
143
+ expect(subject.annotatedAt.first).to eq a_time
144
+ end
145
+
146
+ it "should be changeable" do
147
+ orig_time = Time.local(2014, 6, 1, 8, 30).strftime("%Y-%m-%dT%H:%M:%S.%L%z")
148
+ new_time = Time.now.strftime("%Y-%m-%dT%H:%M:%S.%L%z")
149
+ subject.annotatedAt = orig_time
150
+ subject.annotatedAt = new_time
151
+ expect(subject.annotatedAt.first).to eq new_time
152
+ end
153
+ end
154
+
155
+ describe 'motivatedBy' do
156
+ it "should be OA.commenting if we haven't set it" do
157
+ expect(subject.motivatedBy.first.rdf_subject.to_s).to eq RDFVocabularies::OA.commenting
158
+ end
159
+
160
+ it "should be settable" do
161
+ subject.motivatedBy = RDFVocabularies::OA.describing
162
+ expect(subject.motivatedBy.first.rdf_subject.to_s).to eq RDFVocabularies::OA.describing
163
+ end
164
+
165
+ it "should be changeable" do
166
+ subject.motivatedBy = RDFVocabularies::OA.describing
167
+ subject.motivatedBy = RDFVocabularies::OA.classifying
168
+ expect(subject.motivatedBy.first.rdf_subject.to_s).to eq RDFVocabularies::OA.classifying
169
+ end
170
+ end
171
+
172
+ describe '#localname_prefix' do
173
+ it "should return default prefix" do
174
+ prefix = LD4L::OpenAnnotationRDF::CommentAnnotation.localname_prefix
175
+ expect(prefix).to eq "ca"
176
+ end
177
+ end
178
+
179
+ # ----------------------------------------------
180
+ # END -- Test attributes specific to this model
181
+ # ----------------------------------------------
182
+
183
+
184
+ describe "#persisted?" do
185
+ context 'with a repository' do
186
+ before do
187
+ # Create inmemory repository
188
+ repository = RDF::Repository.new
189
+ allow(subject).to receive(:repository).and_return(repository)
190
+ end
191
+
192
+ context "when the object is new" do
193
+ it "should return false" do
194
+ expect(subject).not_to be_persisted
195
+ end
196
+ end
197
+
198
+ context "when it is saved" do
199
+ before do
200
+ subject.motivatedBy = RDFVocabularies::OA.commenting
201
+ subject.persist!
202
+ end
203
+
204
+ it "should return true" do
205
+ expect(subject).to be_persisted
206
+ end
207
+
208
+ context "and then modified" do
209
+ before do
210
+ subject.motivatedBy = RDFVocabularies::OA.tagging
211
+ end
212
+
213
+ it "should return true" do
214
+ expect(subject).to be_persisted
215
+ end
216
+ end
217
+ context "and then reloaded" do
218
+ before do
219
+ subject.reload
220
+ end
221
+
222
+ it "should reset the motivatedBy" do
223
+ expect(subject.motivatedBy.first.rdf_subject.to_s).to eq RDFVocabularies::OA.commenting.to_s
224
+ end
225
+
226
+ it "should be persisted" do
227
+ expect(subject).to be_persisted
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ describe "#persist!" do
235
+ context "when the repository is set" do
236
+ context "and the item is not a blank node" do
237
+
238
+ subject {LD4L::OpenAnnotationRDF::CommentAnnotation.new("123")}
239
+
240
+ before do
241
+ # Create inmemory repository
242
+ @repo = RDF::Repository.new
243
+ allow(subject.class).to receive(:repository).and_return(nil)
244
+ allow(subject).to receive(:repository).and_return(@repo)
245
+ subject.motivatedBy = RDFVocabularies::OA.commenting
246
+ subject.persist!
247
+ end
248
+
249
+ it "should persist to the repository" do
250
+ expect(@repo.statements.first).to eq subject.statements.first
251
+ end
252
+
253
+ it "should delete from the repository" do
254
+ subject.reload
255
+ expect(subject.motivatedBy.first.rdf_subject.to_s).to eq RDFVocabularies::OA.commenting.to_s
256
+ subject.motivatedBy = []
257
+ expect(subject.motivatedBy).to eq []
258
+ subject.persist!
259
+ subject.reload
260
+ expect(subject.annotatedAt).to eq []
261
+ expect(@repo.statements.to_a.length).to eq 1 # Only the type statement
262
+ end
263
+
264
+ context "when body is set" do
265
+ before do
266
+ subject.setComment('I like this.')
267
+ subject.persist!
268
+ end
269
+ it "should persist body to the repository" do
270
+ cb = LD4L::OpenAnnotationRDF::CommentBody.new(subject.getBody.rdf_subject)
271
+ expect(cb).to be_persisted
272
+ expect(subject.getBody.rdf_subject.to_s).to eq cb.rdf_subject.to_s
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
278
+
279
+ describe '#destroy!' do
280
+ before do
281
+ subject << RDF::Statement(RDF::DC.LicenseDocument, RDF::DC.title, 'LICENSE')
282
+ end
283
+
284
+ subject { LD4L::OpenAnnotationRDF::CommentAnnotation.new('123') }
285
+
286
+ it 'should return true' do
287
+ expect(subject.destroy!).to be true
288
+ expect(subject.destroy).to be true
289
+ end
290
+
291
+ it 'should delete the graph' do
292
+ subject.destroy
293
+ expect(subject).to be_empty
294
+ end
295
+
296
+ context 'with a parent' do
297
+ before do
298
+ subject.annotatedBy = child
299
+ end
300
+
301
+ let(:child) do
302
+ LD4L::FoafRDF::Person.new('456')
303
+ end
304
+
305
+ it 'should empty the graph and remove it from the parent' do
306
+ child.destroy
307
+ expect(subject.annotatedBy).to be_empty
308
+ end
309
+
310
+ it 'should remove its whole graph from the parent' do
311
+ child.destroy
312
+ child.each_statement do |s|
313
+ expect(subject.statements).not_to include s
314
+ end
315
+ end
316
+ end
317
+
318
+ context 'with annotation body' do
319
+ before do
320
+ subject.setComment('I like this.')
321
+ end
322
+
323
+ context 'and body is set on the annotation' do
324
+ let(:child) do
325
+ subject.getBody
326
+ end
327
+
328
+ it 'should empty the graph and remove it from the parent' do
329
+ child.destroy
330
+ expect(subject.hasBody).to be_empty
331
+ end
332
+
333
+ it 'should remove its whole graph from the parent' do
334
+ child.destroy
335
+ child.each_statement do |s|
336
+ expect(subject.statements).not_to include s
337
+ end
338
+ end
339
+ end
340
+ end
341
+ end
342
+
343
+ describe 'attributes' do
344
+ before do
345
+ subject.annotatedBy = annotatedBy
346
+ subject.motivatedBy = 'commenting'
347
+ end
348
+
349
+ subject {LD4L::OpenAnnotationRDF::CommentAnnotation.new("123")}
350
+
351
+ let(:annotatedBy) { LD4L::FoafRDF::Person.new('456') }
352
+
353
+ it 'should return an attributes hash' do
354
+ expect(subject.attributes).to be_a Hash
355
+ end
356
+
357
+ it 'should contain data' do
358
+ expect(subject.attributes['motivatedBy']).to eq ['commenting']
359
+ end
360
+
361
+ it 'should contain child objects' do
362
+ expect(subject.attributes['annotatedBy']).to eq [annotatedBy]
363
+ end
364
+
365
+ context 'with unmodeled data' do
366
+ before do
367
+ subject << RDF::Statement(subject.rdf_subject, RDF::DC.contributor, 'Tove Jansson')
368
+ subject << RDF::Statement(subject.rdf_subject, RDF::DC.relation, RDF::URI('http://example.org/moomi'))
369
+ node = RDF::Node.new
370
+ subject << RDF::Statement(RDF::URI('http://example.org/moomi'), RDF::DC.relation, node)
371
+ subject << RDF::Statement(node, RDF::DC.title, 'bnode')
372
+ end
373
+
374
+ it 'should include data with URIs as attribute names' do
375
+ expect(subject.attributes[RDF::DC.contributor.to_s]).to eq ['Tove Jansson']
376
+ end
377
+
378
+ it 'should return generic Resources' do
379
+ expect(subject.attributes[RDF::DC.relation.to_s].first).to be_a ActiveTriples::Resource
380
+ end
381
+
382
+ it 'should build deep data for Resources' do
383
+ expect(subject.attributes[RDF::DC.relation.to_s].first.get_values(RDF::DC.relation).
384
+ first.get_values(RDF::DC.title)).to eq ['bnode']
385
+ end
386
+
387
+ it 'should include deep data in serializable_hash' do
388
+ expect(subject.serializable_hash[RDF::DC.relation.to_s].first.get_values(RDF::DC.relation).
389
+ first.get_values(RDF::DC.title)).to eq ['bnode']
390
+ end
391
+ end
392
+
393
+ describe 'attribute_serialization' do
394
+ describe '#to_json' do
395
+ it 'should return a string with correct objects' do
396
+ json_hash = JSON.parse(subject.to_json)
397
+ expect(json_hash['annotatedBy'].first['id']).to eq annotatedBy.rdf_subject.to_s
398
+ end
399
+ end
400
+ end
401
+ end
402
+
403
+ describe 'property methods' do
404
+ it 'should set and get properties' do
405
+ subject.motivatedBy = 'commenting'
406
+ expect(subject.motivatedBy).to eq ['commenting']
407
+ end
408
+ end
409
+
410
+ describe 'child nodes' do
411
+ it 'should return an object of the correct class when the value is built from the base URI' do
412
+ subject.annotatedBy = LD4L::FoafRDF::Person.new('456')
413
+ expect(subject.annotatedBy.first).to be_kind_of LD4L::FoafRDF::Person
414
+ end
415
+
416
+ it 'should return an object with the correct URI created with a URI' do
417
+ subject.annotatedBy = LD4L::FoafRDF::Person.new("http://vivo.cornell.edu/individual/JohnSmith")
418
+ expect(subject.annotatedBy.first.rdf_subject).to eq RDF::URI("http://vivo.cornell.edu/individual/JohnSmith")
419
+ end
420
+
421
+ it 'should return an object of the correct class when the value is a bnode' do
422
+ subject.annotatedBy = LD4L::FoafRDF::Person.new
423
+ expect(subject.annotatedBy.first).to be_kind_of LD4L::FoafRDF::Person
424
+ end
425
+ end
426
+
427
+ describe '#type' do
428
+ it 'should return the type configured on the parent class' do
429
+ expect(subject.type).to eq [LD4L::OpenAnnotationRDF::CommentAnnotation.type]
430
+ end
431
+
432
+ it 'should set the type' do
433
+ subject.type = RDF::URI('http://example.org/AnotherClass')
434
+ expect(subject.type).to eq [RDF::URI('http://example.org/AnotherClass')]
435
+ end
436
+
437
+ it 'should be the type in the graph' do
438
+ subject.query(:subject => subject.rdf_subject, :predicate => RDF.type).statements do |s|
439
+ expect(s.object).to eq RDF::URI('http://example.org/AnotherClass')
440
+ end
441
+ end
442
+ end
443
+
444
+ describe '#rdf_label' do
445
+ subject {LD4L::OpenAnnotationRDF::CommentAnnotation.new("123")}
446
+
447
+ it 'should return an array of label values' do
448
+ expect(subject.rdf_label).to be_kind_of Array
449
+ end
450
+
451
+ it 'should return the default label as URI when no title property exists' do
452
+ expect(subject.rdf_label).to eq [RDF::URI("#{LD4L::OpenAnnotationRDF::CommentAnnotation.base_uri}123")]
453
+ end
454
+
455
+ it 'should prioritize configured label values' do
456
+ custom_label = RDF::URI('http://example.org/custom_label')
457
+ subject.class.configure :rdf_label => custom_label
458
+ subject << RDF::Statement(subject.rdf_subject, custom_label, RDF::Literal('New Label'))
459
+ expect(subject.rdf_label).to eq ['New Label']
460
+ end
461
+ end
462
+
463
+ describe '#solrize' do
464
+ it 'should return a label for bnodes' do
465
+ expect(subject.solrize).to eq subject.rdf_label
466
+ end
467
+
468
+ it 'should return a string of the resource uri' do
469
+ subject.set_subject! 'http://example.org/moomin'
470
+ expect(subject.solrize).to eq 'http://example.org/moomin'
471
+ end
472
+ end
473
+
474
+ describe 'editing the graph' do
475
+ it 'should write properties when statements are added' do
476
+ subject << RDF::Statement.new(subject.rdf_subject, RDFVocabularies::OA.motivatedBy, 'commenting')
477
+ expect(subject.motivatedBy).to include 'commenting'
478
+ end
479
+
480
+ it 'should delete properties when statements are removed' do
481
+ subject << RDF::Statement.new(subject.rdf_subject, RDFVocabularies::OA.annotatedBy, 'John Smith')
482
+ subject.delete RDF::Statement.new(subject.rdf_subject, RDFVocabularies::OA.annotatedBy, 'John Smith')
483
+ expect(subject.annotatedBy).to eq []
484
+ end
485
+ end
486
+
487
+ describe 'big complex graphs' do
488
+ before do
489
+ class DummyPerson < ActiveTriples::Resource
490
+ configure :type => RDF::URI('http://example.org/Person')
491
+ property :foafname, :predicate => RDF::FOAF.name
492
+ property :publications, :predicate => RDF::FOAF.publications, :class_name => 'DummyDocument'
493
+ property :knows, :predicate => RDF::FOAF.knows, :class_name => DummyPerson
494
+ end
495
+
496
+ class DummyDocument < ActiveTriples::Resource
497
+ configure :type => RDF::URI('http://example.org/Document')
498
+ property :title, :predicate => RDF::DC.title
499
+ property :creator, :predicate => RDF::DC.creator, :class_name => 'DummyPerson'
500
+ end
501
+
502
+ LD4L::OpenAnnotationRDF::CommentAnnotation.property :item, :predicate => RDF::DC.relation, :class_name => DummyDocument
503
+ end
504
+
505
+ subject { LD4L::OpenAnnotationRDF::CommentAnnotation.new }
506
+
507
+ let (:document1) do
508
+ d = DummyDocument.new
509
+ d.title = 'Document One'
510
+ d
511
+ end
512
+
513
+ let (:document2) do
514
+ d = DummyDocument.new
515
+ d.title = 'Document Two'
516
+ d
517
+ end
518
+
519
+ let (:person1) do
520
+ p = DummyPerson.new
521
+ p.foafname = 'Alice'
522
+ p
523
+ end
524
+
525
+ let (:person2) do
526
+ p = DummyPerson.new
527
+ p.foafname = 'Bob'
528
+ p
529
+ end
530
+
531
+ let (:data) { <<END
532
+ _:1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/SomeClass> .
533
+ _:1 <http://purl.org/dc/terms/relation> _:2 .
534
+ _:2 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Document> .
535
+ _:2 <http://purl.org/dc/terms/title> "Document One" .
536
+ _:2 <http://purl.org/dc/terms/creator> _:3 .
537
+ _:2 <http://purl.org/dc/terms/creator> _:4 .
538
+ _:4 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> .
539
+ _:4 <http://xmlns.com/foaf/0.1/name> "Bob" .
540
+ _:3 <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> .
541
+ _:3 <http://xmlns.com/foaf/0.1/name> "Alice" .
542
+ _:3 <http://xmlns.com/foaf/0.1/knows> _:4 ."
543
+ END
544
+ }
545
+
546
+ after do
547
+ Object.send(:remove_const, "DummyDocument")
548
+ Object.send(:remove_const, "DummyPerson")
549
+ end
550
+
551
+ it 'should allow access to deep nodes' do
552
+ document1.creator = [person1, person2]
553
+ document2.creator = person1
554
+ person1.knows = person2
555
+ subject.item = [document1]
556
+ expect(subject.item.first.creator.first.knows.first.foafname).to eq ['Bob']
557
+ end
558
+ end
559
+ end