ladder 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,8 +14,13 @@ describe Ladder::Searchable::Background do
14
14
  class Thing
15
15
  include Ladder::Resource
16
16
  include Ladder::Searchable::Background
17
+ configure type: RDF::DC.BibliographicResource
18
+
19
+ field :alt
20
+ property :alt, predicate: RDF::DC.alternative # non-localized literal
21
+ property :title, predicate: RDF::DC.title # localized literal
17
22
  end
18
-
23
+
19
24
  class Datastream
20
25
  include Ladder::File
21
26
  include Ladder::Searchable::Background
@@ -24,37 +29,27 @@ describe Ladder::Searchable::Background do
24
29
 
25
30
  after do
26
31
  Object.send(:remove_const, :LADDER_BASE_URI) if Object
27
- Object.send(:remove_const, "Thing") if Object
28
- Object.send(:remove_const, "Datastream") if Object
32
+ Object.send(:remove_const, 'Thing') if Object
33
+ Object.send(:remove_const, 'Datastream') if Object
29
34
  end
30
35
 
31
- shared_context 'with data' do
32
- before do
33
- subject.class.configure type: RDF::DC.BibliographicResource
36
+ shared_context 'with relations' do
37
+ let(:person) { Person.new }
34
38
 
35
- # non-localized literal
36
- subject.class.field :alt
37
- subject.class.property :alt, predicate: RDF::DC.alternative
38
- subject.alt = 'Mumintrollet pa kometjakt'
39
+ before do
40
+ class Person
41
+ include Ladder::Resource
42
+ include Ladder::Searchable::Background
43
+ configure type: RDF::FOAF.Person
39
44
 
40
- # localized literal
41
- subject.class.property :title, predicate: RDF::DC.title
42
- subject.title = 'Comet in Moominland'
45
+ property :foaf_name, predicate: RDF::FOAF.name
46
+ property :things, predicate: RDF::DC.relation, class_name: 'Thing'
47
+ end
43
48
  end
44
- end
45
-
46
- shared_context 'with relations' do
47
- before do
48
- # related object
49
- person.class.configure type: RDF::FOAF.Person
50
- person.class.property :foaf_name, predicate: RDF::FOAF.name
51
- person.foaf_name = 'Tove Jansson'
52
49
 
53
- # many-to-many relation
54
- person.class.property :things, predicate: RDF::DC.relation, class_name: 'Thing'
55
- subject.class.property :people, predicate: RDF::DC.creator, class_name: 'Person'
56
- subject.people << person
57
- end
50
+ after do
51
+ Object.send(:remove_const, 'Person') if Object
52
+ end
58
53
  end
59
54
 
60
55
  context 'with data' do
@@ -67,22 +62,19 @@ describe Ladder::Searchable::Background do
67
62
 
68
63
  context 'with relations' do
69
64
  let(:subject) { Thing.new }
70
- let(:person) { Person.new }
71
-
72
- before do
73
- class Person
74
- include Ladder::Resource
75
- include Ladder::Searchable::Background
76
- end
77
- end
78
-
79
- after do
80
- Object.send(:remove_const, "Person") if Object
81
- end
82
65
 
83
66
  include_context 'with data'
84
67
  include_context 'with relations'
85
68
 
69
+ before do
70
+ # many-to-many relation
71
+ Thing.property :people, predicate: RDF::DC.creator, class_name: 'Person'
72
+
73
+ # related object
74
+ person.foaf_name = 'Tove Jansson'
75
+ subject.people << person
76
+ end
77
+
86
78
  it_behaves_like 'a Searchable'
87
79
  it_behaves_like 'a Searchable with related'
88
80
  end
@@ -97,8 +89,12 @@ describe Ladder::Searchable::Background do
97
89
  end
98
90
 
99
91
  context 'with data from string after creation' do
100
- data = "And so Moomintroll was helplessly thrown out into a strange and dangerous world and dropped up to his ears in the first snowdrift of his experience. It felt unpleasantly prickly to his velvet skin, but at the same time his nose caught a new smell. It was a more serious smell than any he had met before, and slightly frightening. But it made him wide awake and greatly interested."
101
-
92
+ data = 'And so Moomintroll was helplessly thrown out into a strange and dangerous world and dropped
93
+ up to his ears in the first snowdrift of his experience. It felt unpleasantly prickly to his
94
+ velvet skin, but at the same time his nose caught a new smell. It was a more serious smell
95
+ than any he had met before, and slightly frightening. But it made him wide awake and greatly
96
+ interested.'
97
+
102
98
  let(:subject) { Datastream.new }
103
99
  let(:source) { data } # UTF-8 (string)
104
100
 
@@ -108,5 +104,4 @@ describe Ladder::Searchable::Background do
108
104
 
109
105
  it_behaves_like 'a Searchable File'
110
106
  end
111
-
112
- end
107
+ end
@@ -18,7 +18,7 @@ describe Ladder::Searchable::File do
18
18
  end
19
19
 
20
20
  after do
21
- Object.send(:remove_const, "Datastream") if Object
21
+ Object.send(:remove_const, 'Datastream') if Object
22
22
  end
23
23
 
24
24
  context 'with data from file' do
@@ -32,8 +32,12 @@ describe Ladder::Searchable::File do
32
32
  end
33
33
 
34
34
  context 'with data from string after creation' do
35
- data = "And so Moomintroll was helplessly thrown out into a strange and dangerous world and dropped up to his ears in the first snowdrift of his experience. It felt unpleasantly prickly to his velvet skin, but at the same time his nose caught a new smell. It was a more serious smell than any he had met before, and slightly frightening. But it made him wide awake and greatly interested."
36
-
35
+ data = 'And so Moomintroll was helplessly thrown out into a strange and dangerous world and dropped
36
+ up to his ears in the first snowdrift of his experience. It felt unpleasantly prickly to his
37
+ velvet skin, but at the same time his nose caught a new smell. It was a more serious smell
38
+ than any he had met before, and slightly frightening. But it made him wide awake and greatly
39
+ interested.'
40
+
37
41
  let(:subject) { Datastream.new }
38
42
  let(:source) { data } # UTF-8 (string)
39
43
 
@@ -44,5 +48,4 @@ describe Ladder::Searchable::File do
44
48
  it_behaves_like 'a File'
45
49
  it_behaves_like 'a Searchable File'
46
50
  end
47
-
48
- end
51
+ end
@@ -14,41 +14,36 @@ describe Ladder::Searchable::Resource do
14
14
  class Thing
15
15
  include Ladder::Resource
16
16
  include Ladder::Searchable
17
+ configure type: RDF::DC.BibliographicResource
18
+
19
+ field :alt
20
+ property :alt, predicate: RDF::DC.alternative # non-localized literal
21
+ property :title, predicate: RDF::DC.title # localized literal
17
22
  end
18
23
  end
19
24
 
20
25
  after do
21
26
  Object.send(:remove_const, :LADDER_BASE_URI) if Object
22
- Object.send(:remove_const, "Thing") if Object
27
+ Object.send(:remove_const, 'Thing') if Object
23
28
  end
24
29
 
25
- shared_context 'with data' do
26
- before do
27
- subject.class.configure type: RDF::DC.BibliographicResource
30
+ shared_context 'with relations' do
31
+ let(:person) { Person.new }
28
32
 
29
- # non-localized literal
30
- subject.class.field :alt
31
- subject.class.property :alt, predicate: RDF::DC.alternative
32
- subject.alt = 'Mumintrollet pa kometjakt'
33
+ before do
34
+ class Person
35
+ include Ladder::Resource
36
+ include Ladder::Searchable
37
+ configure type: RDF::FOAF.Person
33
38
 
34
- # localized literal
35
- subject.class.property :title, predicate: RDF::DC.title
36
- subject.title = 'Comet in Moominland'
39
+ property :foaf_name, predicate: RDF::FOAF.name
40
+ property :things, predicate: RDF::DC.relation, class_name: 'Thing'
41
+ end
37
42
  end
38
- end
39
43
 
40
- shared_context 'with relations' do
41
- before do
42
- # related object
43
- person.class.configure type: RDF::FOAF.Person
44
- person.class.property :foaf_name, predicate: RDF::FOAF.name
45
- person.foaf_name = 'Tove Jansson'
46
-
47
- # many-to-many relation
48
- person.class.property :things, predicate: RDF::DC.relation, class_name: 'Thing'
49
- subject.class.property :people, predicate: RDF::DC.creator, class_name: 'Person'
50
- subject.people << person
51
- end
44
+ after do
45
+ Object.send(:remove_const, 'Person') if Object
46
+ end
52
47
  end
53
48
 
54
49
  context 'with data' do
@@ -61,23 +56,20 @@ describe Ladder::Searchable::Resource do
61
56
 
62
57
  context 'with relations' do
63
58
  let(:subject) { Thing.new }
64
- let(:person) { Person.new }
65
-
66
- before do
67
- class Person
68
- include Ladder::Resource
69
- include Ladder::Searchable
70
- end
71
- end
72
-
73
- after do
74
- Object.send(:remove_const, "Person") if Object
75
- end
76
59
 
77
60
  include_context 'with data'
78
61
  include_context 'with relations'
79
62
 
63
+ before do
64
+ # many-to-many relation
65
+ Thing.property :people, predicate: RDF::DC.creator, class_name: 'Person'
66
+
67
+ # related object
68
+ person.foaf_name = 'Tove Jansson'
69
+ subject.people << person
70
+ end
71
+
72
+ it_behaves_like 'a Searchable'
80
73
  it_behaves_like 'a Searchable with related'
81
74
  end
82
-
83
- end
75
+ end
data/spec/shared/file.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'mimemagic'
2
2
 
3
3
  shared_examples 'a File' do
4
-
5
4
  describe 'LADDER_BASE_URI' do
6
5
  it 'should automatically have a base URI' do
7
6
  expect(subject.rdf_subject.parent).to eq RDF::URI('http://example.org/datastreams/')
@@ -34,7 +33,9 @@ shared_examples 'a File' do
34
33
  end
35
34
 
36
35
  describe '#grid' do
37
- # TODO
36
+ it 'should be an instance of a GridFS object' do
37
+ expect(subject.class.grid.parent.name).to eq 'Mongoid::GridFs'
38
+ end
38
39
  end
39
40
 
40
41
  describe '#attributes' do
@@ -42,18 +43,18 @@ shared_examples 'a File' do
42
43
  subject.save
43
44
  subject.reload
44
45
  end
45
-
46
+
46
47
  it 'should have a #length' do
47
48
  expect(subject.length).to eq source.force_encoding(subject.data.encoding).length
48
49
  end
49
-
50
+
50
51
  it 'should have a #md5' do
51
52
  expect(subject.md5).to eq Digest::MD5.hexdigest(source)
52
53
  end
53
-
54
+
54
55
  it 'should have a #content_type' do
55
56
  source_type = MimeMagic.by_magic(source).to_s
56
- expect(subject.content_type).to eq source_type.empty? ? "application/octet-stream" : source_type
57
+ expect(subject.content_type).to eq source_type.empty? ? 'application/octet-stream' : source_type
57
58
  end
58
59
  end
59
60
 
@@ -64,7 +65,6 @@ shared_examples 'a File' do
64
65
 
65
66
  it 'should not have any statements' do
66
67
  expect(subject.update_resource.statements).to be_empty
67
- end
68
+ end
68
69
  end
69
-
70
- end
70
+ end
@@ -0,0 +1,31 @@
1
+ {
2
+ "@context": {
3
+ "dc": "http://purl.org/dc/terms/",
4
+ "skos": "http://www.w3.org/2004/02/skos/core#"
5
+ },
6
+ "@id": "_:b1",
7
+ "@type": "dc:BibliographicResource",
8
+ "dc:alternative": "Mumintrollet pa kometjakt",
9
+ "dc:creator": {
10
+ "@id": "_:b2",
11
+ "@type": "dc:AgentClass",
12
+ "dc:relation": {
13
+ "@id": "_:b1"
14
+ }
15
+ },
16
+ "dc:hasPart": {
17
+ "@id": "_:b3",
18
+ "@type": "dc:PhysicalResource",
19
+ "dc:relation": {
20
+ "@id": "_:b1"
21
+ }
22
+ },
23
+ "dc:subject": {
24
+ "@id": "_:b4",
25
+ "@type": "skos:Concept"
26
+ },
27
+ "dc:title": {
28
+ "@value": "Comet in Moominland",
29
+ "@language": "en"
30
+ }
31
+ }
@@ -1,8 +1,164 @@
1
- shared_examples 'a Resource' do
1
+ shared_context 'with data' do
2
+ before do
3
+ subject.alt = 'Mumintrollet pa kometjakt' # non-localized literal
4
+ subject.title = 'Comet in Moominland' # localized literal
5
+ end
6
+ end
7
+
8
+ shared_examples 'a Dynamic Resource' do
9
+ describe '#property' do
10
+ context 'with undefined property' do
11
+ before do
12
+ subject.property :description, predicate: RDF::DC.description
13
+ subject.description = "Second in Tove Jansson's series of Moomin books"
14
+ end
15
+
16
+ it 'should create a context' do
17
+ expect(subject._context).to eq(description: RDF::DC.description.to_uri.to_s)
18
+ end
19
+
20
+ it 'should build an accessor' do
21
+ expect(subject.description).to eq "Second in Tove Jansson's series of Moomin books"
22
+ end
23
+ end
24
+
25
+ context 'with conflicting property' do
26
+ before do
27
+ subject.property :title, predicate: RDF::DC11.title
28
+ subject.dc11_title = 'Kometjakten'
29
+ end
30
+
31
+ it 'should create a context' do
32
+ expect(subject._context).to eq(dc11_title: RDF::DC11.title.to_uri.to_s)
33
+ end
34
+
35
+ it 'should build an accessor' do
36
+ expect(subject.dc11_title).to eq 'Kometjakten'
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#update_resource' do
42
+ before do
43
+ # undefined property
44
+ subject.property :description, predicate: RDF::DC.description
45
+ subject.description = "Second in Tove Jansson's series of Moomin books"
46
+
47
+ # conflicting property
48
+ subject.property :title, predicate: RDF::DC11.title
49
+ subject.dc11_title = 'Kometjakten'
50
+
51
+ # defined field
52
+ subject << RDF::Statement(nil, RDF::DC.title, 'Kometen kommer')
53
+
54
+ # conflicting property
55
+ subject << RDF::Statement(nil, RDF::DC.alternative, 'Kometjakten')
56
+
57
+ # URI value
58
+ subject << RDF::Statement(nil, RDF::DC.identifier, RDF::URI('http://some.uri'))
59
+
60
+ # RDF type
61
+ subject << RDF::Statement(nil, RDF.type, RDF::DC.PhysicalResource)
62
+
63
+ subject.update_resource
64
+ end
65
+
66
+ it 'should have updated values' do
67
+ expect(subject.resource.statements.count).to eq 7
68
+ expect(subject.resource.query(predicate: RDF::DC.description, object: "Second in Tove Jansson's series of Moomin books").count).to eq 1
69
+ expect(subject.resource.query(predicate: RDF::DC11.title, object: 'Kometjakten').count).to eq 1
70
+ expect(subject.resource.query(predicate: RDF::DC.title, object: RDF::Literal.new('Kometen kommer', language: :en)).count).to eq 1
71
+ expect(subject.resource.query(predicate: RDF::DC.alternative, object: 'Kometjakten').count).to eq 1
72
+ expect(subject.resource.query(predicate: RDF::DC.identifier, object: RDF::URI('http://some.uri')).count).to eq 1
73
+ end
74
+
75
+ it 'should contain both class and dynamic types' do
76
+ expect(subject.type.count).to eq 2
77
+ expect(subject.type).to include RDF::DC.BibliographicResource
78
+ expect(subject.type).to include RDF::DC.PhysicalResource
79
+ end
80
+ end
81
+
82
+ describe '#<<' do
83
+ context 'with defined field' do
84
+ before do
85
+ subject << RDF::Statement(nil, RDF::DC.title, 'Kometen kommer')
86
+ end
87
+
88
+ it 'should not create a context' do
89
+ expect(subject._context).to be nil
90
+ end
91
+
92
+ it 'should update existing values' do
93
+ expect(subject.title).to eq 'Kometen kommer'
94
+ end
95
+ end
96
+
97
+ context 'with undefined field' do
98
+ before do
99
+ subject << RDF::Statement(nil, RDF::DC.description, "Second in Tove Jansson's series of Moomin books")
100
+ end
101
+
102
+ it 'should create a context' do
103
+ expect(subject._context).to eq(description: RDF::DC.description.to_uri.to_s)
104
+ end
105
+
106
+ it 'should build an accessor' do
107
+ expect(subject.description).to eq "Second in Tove Jansson's series of Moomin books"
108
+ end
109
+ end
110
+
111
+ context 'with conflicting property' do
112
+ before do
113
+ subject << RDF::Statement(nil, RDF::DC11.title, 'Kometjakten')
114
+ end
115
+
116
+ it 'should create a context' do
117
+ expect(subject._context).to eq(dc11_title: RDF::DC11.title.to_uri.to_s)
118
+ end
119
+
120
+ it 'should build an accessor' do
121
+ expect(subject.dc11_title).to eq 'Kometjakten'
122
+ end
123
+ end
124
+
125
+ context 'with a URI value' do
126
+ before do
127
+ subject << RDF::Statement(nil, RDF::DC.identifier, RDF::URI('http://some.uri'))
128
+ end
2
129
 
130
+ it 'should store the URI as a string' do
131
+ expect(subject.identifier).to eq 'http://some.uri'
132
+ end
133
+
134
+ it 'should cast a URI into the resource' do
135
+ subject.update_resource
136
+ query = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.identifier)
137
+ expect(query.first_object).to be_a_kind_of RDF::URI
138
+ end
139
+ end
140
+ end
141
+
142
+ describe '#resource_class' do
143
+ before do
144
+ subject.property :description, predicate: RDF::DC.description
145
+ end
146
+
147
+ it 'should have modified properties on the instance' do
148
+ expect(subject.resource.class.properties.keys).to include 'description'
149
+ end
150
+
151
+ it 'should not modify the global class properties' do
152
+ expect(subject.class.resource_class.properties.keys).to_not include 'description'
153
+ expect(subject.class.resource_class.properties).to eq subject.class.new.class.resource_class.properties
154
+ end
155
+ end
156
+ end
157
+
158
+ shared_examples 'a Resource' do
3
159
  describe 'LADDER_BASE_URI' do
4
160
  it 'should automatically have a base URI' do
5
- expect(subject.rdf_subject.parent).to eq RDF::URI('http://example.org/things/')
161
+ expect(subject.resource.rdf_subject.parent).to eq RDF::URI('http://example.org/things/')
6
162
  end
7
163
  end
8
164
 
@@ -11,11 +167,11 @@ shared_examples 'a Resource' do
11
167
  it 'should return non-localized value' do
12
168
  expect(subject.alt).to eq 'Mumintrollet pa kometjakt'
13
169
  end
14
-
170
+
15
171
  it 'should not be a localized hash' do
16
172
  expect(subject.attributes['alt']).to eq 'Mumintrollet pa kometjakt'
17
173
  end
18
-
174
+
19
175
  it 'should have a valid predicate' do
20
176
  expect(subject.class.properties['alt'].predicate).to eq RDF::DC.alternative
21
177
  end
@@ -25,16 +181,16 @@ shared_examples 'a Resource' do
25
181
  expect(subject.class.properties['alt'].predicate).to eq RDF::DC.title
26
182
  end
27
183
  end
28
-
184
+
29
185
  context 'with localized literal' do
30
186
  it 'should return localized value' do
31
187
  expect(subject.title).to eq 'Comet in Moominland'
32
188
  end
33
-
189
+
34
190
  it 'should return all locales' do
35
- expect(subject.attributes['title']).to eq({'en' => 'Comet in Moominland'})
191
+ expect(subject.attributes['title']).to eq('en' => 'Comet in Moominland')
36
192
  end
37
-
193
+
38
194
  it 'should have a valid predicate' do
39
195
  expect(subject.class.properties['title'].predicate).to eq RDF::DC.title
40
196
  end
@@ -56,7 +212,7 @@ shared_examples 'a Resource' do
56
212
  expect(s.object.to_s).to eq 'Comet in Moominland'
57
213
  end
58
214
  end
59
-
215
+
60
216
  it 'should have a localized literal object' do
61
217
  subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.alternative).each_statement do |s|
62
218
  expect(s.object.to_s).to eq 'Mumintrollet pa kometjakt'
@@ -73,7 +229,7 @@ shared_examples 'a Resource' do
73
229
  before do
74
230
  subject << RDF::Statement(nil, RDF::DC.title, 'Kometen kommer')
75
231
  end
76
-
232
+
77
233
  it 'should update existing values' do
78
234
  expect(subject.title).to eq 'Kometen kommer'
79
235
  end
@@ -90,12 +246,22 @@ shared_examples 'a Resource' do
90
246
  end
91
247
  end
92
248
 
249
+ context 'with a RDF type' do
250
+ before do
251
+ subject << RDF::Statement(nil, RDF.type, RDF::DC.PhysicalResource)
252
+ end
253
+
254
+ it 'should only contain types defined on the class' do
255
+ # expect(subject.type.count).to eq 1
256
+ expect(subject.type).to include RDF::DC.BibliographicResource
257
+ end
258
+ end
259
+
93
260
  context 'with a URI value' do
94
261
  before do
95
- subject.class.property :identifier, predicate: RDF::DC.identifier
96
262
  subject << RDF::Statement(nil, RDF::DC.identifier, RDF::URI('http://some.uri'))
97
263
  end
98
-
264
+
99
265
  it 'should store the URI as a string' do
100
266
  expect(subject.identifier).to eq 'http://some.uri'
101
267
  end
@@ -115,7 +281,6 @@ shared_examples 'a Resource' do
115
281
  end
116
282
 
117
283
  context 'a serializable' do
118
-
119
284
  describe '#as_jsonld' do
120
285
  it 'should output a valid jsonld representation of itself' do
121
286
  graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld)
@@ -128,7 +293,225 @@ shared_examples 'a Resource' do
128
293
  # TODO
129
294
  end
130
295
  end
296
+ end
297
+
298
+ describe '#new_from_graph' do
299
+ before do
300
+ subject.update_resource(related: true)
301
+ end
302
+
303
+ let(:new_subject) { subject.class.new_from_graph subject.resource }
304
+
305
+ it 'should create a new object of the same class' do
306
+ expect(new_subject.class).to eq subject.class
307
+ end
308
+
309
+ it 'should populate the same properties' do
310
+ # TODO: clean this up
311
+ def remove_ids(x)
312
+ if x.is_a?(Hash)
313
+ x.reduce({}) do |m, (k, v)|
314
+ m[k] = remove_ids(v) unless k == '@id'
315
+ m
316
+ end
317
+ else
318
+ x
319
+ end
320
+ end
321
+
322
+ expect(remove_ids(new_subject.as_framed_jsonld)).to eq remove_ids(subject.as_framed_jsonld)
323
+ end
324
+ end
325
+ end
326
+
327
+ shared_examples 'a Resource with relations' do
328
+ describe 'serializable' do
329
+ # TODO: contexts with relations and without
330
+ # expect(subject.as_jsonld(related: true)).to eq subject.as_jsonld
331
+ # expect(subject.as_qname(related: true)).to eq subject.as_qname
332
+ # expect(subject.as_framed_jsonld).to eq subject.as_jsonld
333
+
334
+ describe '#as_jsonld related: true' do
335
+ it 'should output a valid jsonld representation of itself and related' do
336
+ graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld related: true)
337
+ expect(subject.update_resource(related: true).to_hash).to eq graph.to_hash
338
+ end
339
+ end
340
+
341
+ describe '#as_qname related: true' do
342
+ it 'should output a valid qname representation of itself and related' do
343
+ # TODO
344
+ end
345
+ end
346
+
347
+ describe '#as_framed_jsonld' do
348
+ it 'should output a valid framed jsonld representation of itself and related' do
349
+ framed_graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_framed_jsonld)
350
+ related_graph = RDF::Graph.new << JSON::LD::API.toRdf(subject.as_jsonld related: true)
351
+ expect(framed_graph.to_hash).to eq related_graph.to_hash
352
+ end
353
+ end
354
+ end
355
+
356
+ it 'should have relations' do
357
+ expect(subject.people.to_a).to include person
358
+ expect(subject.concepts.to_a).to include concept
359
+ expect(subject.part).to eq part
360
+ end
361
+
362
+ it 'should have inverse relations' do
363
+ expect(person.things.to_a).to include subject
364
+ expect(concept.relations).to be_empty
365
+ expect(part.thing).to eq subject
366
+ end
367
+
368
+ describe 'with many-to-many' do
369
+ it 'should have a relation' do
370
+ expect(subject.relations['people'].relation).to eq Mongoid::Relations::Referenced::ManyToMany
371
+ expect(subject.people.to_a).to include person
372
+ end
131
373
 
374
+ it 'should have an inverse relation' do
375
+ expect(person.relations['things'].relation).to eq Mongoid::Relations::Referenced::ManyToMany
376
+ expect(person.things.to_a).to include subject
377
+ end
378
+
379
+ it 'should have a valid predicate' do
380
+ expect(subject.class.properties['people'].predicate).to eq RDF::DC.creator
381
+ end
382
+
383
+ it 'should have a valid inverse predicate' do
384
+ expect(person.class.properties['things'].predicate).to eq RDF::DC.relation
385
+ end
132
386
  end
133
387
 
134
- end
388
+ describe 'with one-sided has-many' do
389
+ it 'should have a relation' do
390
+ expect(subject.relations['concepts'].relation).to eq Mongoid::Relations::Referenced::ManyToMany
391
+ expect(subject.concepts.to_a).to include concept
392
+ end
393
+
394
+ it 'should not have an inverse relation' do
395
+ expect(subject.relations['concepts'].inverse_of).to be nil
396
+ expect(concept.relations).to be_empty
397
+ end
398
+
399
+ it 'should have a valid predicate' do
400
+ expect(subject.class.properties['concepts'].predicate).to eq RDF::DC.subject
401
+ end
402
+
403
+ it 'should not have an inverse predicate' do
404
+ expect(concept.class.properties).to be_empty
405
+ end
406
+ end
407
+
408
+ describe 'with embedded-one' do
409
+ it 'should have a relation' do
410
+ expect(subject.relations['part'].relation).to eq Mongoid::Relations::Embedded::One
411
+ expect(subject.part).to eq part
412
+ end
413
+
414
+ it 'should have an inverse relation' do
415
+ expect(part.relations['thing'].relation).to eq Mongoid::Relations::Embedded::In
416
+ expect(part.thing).to eq subject
417
+ end
418
+
419
+ it 'should have a valid predicate' do
420
+ expect(subject.class.properties['part'].predicate).to eq RDF::DC.hasPart
421
+ end
422
+
423
+ it 'should have a valid inverse predicate' do
424
+ expect(part.class.properties['thing'].predicate).to eq RDF::DC.relation
425
+ end
426
+ end
427
+
428
+ describe '#update_resource with related' do
429
+ # TODO: add tests for autosaved relations
430
+ before do
431
+ subject.update_resource(related: true)
432
+ end
433
+
434
+ it 'should have a literal object' do
435
+ query = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.title)
436
+ expect(query.first_object.to_s).to eq 'Comet in Moominland'
437
+ end
438
+
439
+ it 'should have an embedded object' do
440
+ query = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.hasPart)
441
+ expect(query.count).to eq 1
442
+ expect(query.first_object).to eq part.rdf_subject
443
+ end
444
+
445
+ it 'should have an embedded object relation' do
446
+ query = subject.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
447
+ expect(query.count).to eq 1
448
+ expect(query.first_object).to eq subject.rdf_subject
449
+ end
450
+
451
+ it 'should have related objects' do
452
+ # many-to-many
453
+ query_creator = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.creator)
454
+ expect(query_creator.count).to eq 1
455
+ expect(query_creator.first_object).to eq person.rdf_subject
456
+
457
+ # one-sided has-many
458
+ query_subject = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.subject)
459
+ expect(query_subject.count).to eq 1
460
+ expect(query_subject.first_object).to eq concept.rdf_subject
461
+
462
+ # embedded-one
463
+ query_part = subject.resource.query(subject: subject.rdf_subject, predicate: RDF::DC.hasPart)
464
+ expect(query_part.count).to eq 1
465
+ expect(query_part.first_object).to eq part.rdf_subject
466
+ end
467
+
468
+ it 'should have related object relations' do
469
+ # many-to-many
470
+ query = person.resource.query(subject: person.rdf_subject, predicate: RDF::DC.relation)
471
+ expect(query.count).to eq 1
472
+ expect(query.first_object).to eq subject.rdf_subject
473
+
474
+ # one-sided has-many
475
+ expect(concept.resource.query(object: subject.rdf_subject)).to be_empty
476
+
477
+ # embedded-one
478
+ query = part.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
479
+ expect(query.count).to eq 1
480
+ expect(query.first_object).to eq subject.rdf_subject
481
+ end
482
+ end
483
+
484
+ describe '#update_resource with related and then without related' do
485
+ # TODO: add tests for autosaved relations
486
+ before do
487
+ subject.update_resource(related: true)
488
+ subject.update_resource # implicit false
489
+ end
490
+
491
+ it 'should not have related objects' do
492
+ expect(subject.resource.query(subject: person.rdf_subject)).to be_empty
493
+ expect(subject.resource.query(subject: concept.rdf_subject)).to be_empty
494
+ end
495
+
496
+ it 'should have embedded object relations' do
497
+ query = subject.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
498
+ expect(query.count).to eq 1
499
+ expect(query.first_object).to eq subject.rdf_subject
500
+ end
501
+
502
+ it 'should have related object relations' do
503
+ # many-to-many
504
+ query = person.resource.query(subject: person.rdf_subject, predicate: RDF::DC.relation)
505
+ expect(query.count).to eq 1
506
+ expect(query.first_object).to eq subject.rdf_subject
507
+
508
+ # one-sided has-many
509
+ expect(concept.resource.query(object: subject.rdf_subject)).to be_empty
510
+
511
+ # embedded-one
512
+ query = part.resource.query(subject: part.rdf_subject, predicate: RDF::DC.relation)
513
+ expect(query.count).to eq 1
514
+ expect(query.first_object).to eq subject.rdf_subject
515
+ end
516
+ end
517
+ end