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,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