rdf-ldp 0.9.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +10 -0
- data/CREDITS +1 -0
- data/README.md +66 -10
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/app/lamprey.rb +119 -40
- data/bin/lamprey +15 -1
- data/lib/rack/ldp.rb +23 -25
- data/lib/rdf/ldp/container.rb +32 -22
- data/lib/rdf/ldp/direct_container.rb +9 -7
- data/lib/rdf/ldp/indirect_container.rb +8 -6
- data/lib/rdf/ldp/interaction_model.rb +39 -29
- data/lib/rdf/ldp/non_rdf_source.rb +10 -6
- data/lib/rdf/ldp/rdf_source.rb +3 -3
- data/lib/rdf/ldp/resource.rb +33 -31
- data/lib/rdf/ldp/spec/container.rb +337 -0
- data/lib/rdf/ldp/spec/direct_container.rb +245 -0
- data/lib/rdf/ldp/spec/indirect_container.rb +152 -0
- data/lib/rdf/ldp/spec/non_rdf_source.rb +138 -0
- data/lib/rdf/ldp/spec/rdf_source.rb +370 -0
- data/lib/rdf/ldp/spec/resource.rb +242 -0
- data/lib/rdf/ldp/spec.rb +6 -0
- data/lib/rdf/ldp/storage_adapters/file_storage_adapter.rb +4 -4
- data/lib/rdf/ldp/version.rb +16 -3
- data/lib/rdf/ldp.rb +4 -3
- metadata +89 -55
@@ -0,0 +1,337 @@
|
|
1
|
+
require 'rdf/ldp/spec/rdf_source'
|
2
|
+
|
3
|
+
shared_examples 'a Container' do
|
4
|
+
it_behaves_like 'an RDFSource'
|
5
|
+
|
6
|
+
let(:uri) { RDF::URI('http://ex.org/moomin') }
|
7
|
+
subject { described_class.new(uri) }
|
8
|
+
|
9
|
+
it { is_expected.to be_container }
|
10
|
+
|
11
|
+
describe '#container_class' do
|
12
|
+
it 'returns a uri' do
|
13
|
+
expect(subject.container_class).to be_a RDF::URI
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#containment_triples' do
|
18
|
+
let(:resource) { RDF::URI('http://ex.org/mymble') }
|
19
|
+
|
20
|
+
it 'returns a uri' do
|
21
|
+
subject.add_containment_triple(resource)
|
22
|
+
expect(subject.containment_triples)
|
23
|
+
.to contain_exactly(an_instance_of(RDF::Statement))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#add' do
|
28
|
+
let(:resource) { RDF::URI('http://ex.org/mymble') }
|
29
|
+
before { subject.create(StringIO.new, 'application/n-triples') }
|
30
|
+
|
31
|
+
it 'returns self' do
|
32
|
+
expect(subject.add(resource)).to eq subject
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'containment triple is added to graph' do
|
36
|
+
expect(subject.add(resource).graph)
|
37
|
+
.to include subject.make_containment_triple(resource)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#add_containment_triple' do
|
42
|
+
let(:resource) { RDF::URI('http://ex.org/mymble') }
|
43
|
+
|
44
|
+
it 'returns self' do
|
45
|
+
expect(subject.add_containment_triple(resource)).to eq subject
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'containment triple is added to graph' do
|
49
|
+
expect(subject.add_containment_triple(resource).graph)
|
50
|
+
.to include subject.make_containment_triple(resource)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#remove_containment_triple' do
|
55
|
+
before { subject.add_containment_triple(resource) }
|
56
|
+
|
57
|
+
let(:resource) { RDF::URI('http://ex.org/mymble') }
|
58
|
+
|
59
|
+
it 'returns self' do
|
60
|
+
expect(subject.remove_containment_triple(resource)).to eq subject
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'membership triple is added to graph' do
|
64
|
+
expect(subject.remove_containment_triple(resource).graph)
|
65
|
+
.not_to include subject.make_containment_triple(resource)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'updates last_modified for container' do
|
69
|
+
expect { subject.remove_containment_triple(resource) }
|
70
|
+
.to change { subject.last_modified }
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'updates etag for container' do
|
74
|
+
expect { subject.remove_containment_triple(resource) }
|
75
|
+
.to change { subject.etag }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#make_containment_triple' do
|
80
|
+
let(:resource) { uri / 'papa' }
|
81
|
+
|
82
|
+
it 'returns a statement' do
|
83
|
+
expect(subject.make_containment_triple(resource)).to be_a RDF::Statement
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'statement subject *or* object is #subject_uri' do
|
87
|
+
sub = subject.make_containment_triple(resource).subject
|
88
|
+
obj = subject.make_containment_triple(resource).object
|
89
|
+
expect([sub, obj]).to include subject.subject_uri
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'converts Resource classes to URI' do
|
93
|
+
sub = subject.make_containment_triple(subject).subject
|
94
|
+
obj = subject.make_containment_triple(subject).object
|
95
|
+
expect([sub, obj]).to include subject.subject_uri
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#request' do
|
100
|
+
let(:graph) { RDF::Graph.new }
|
101
|
+
|
102
|
+
let(:env) do
|
103
|
+
{ 'rack.input' => StringIO.new(graph.dump(:ntriples)),
|
104
|
+
'CONTENT_TYPE' => 'application/n-triples' }
|
105
|
+
end
|
106
|
+
|
107
|
+
let(:statement) do
|
108
|
+
RDF::Statement(subject.subject_uri,
|
109
|
+
RDF::Vocab::LDP.contains,
|
110
|
+
'moomin')
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'with :PATCH',
|
114
|
+
if: described_class.private_method_defined?(:patch) do
|
115
|
+
|
116
|
+
it 'raises conflict error when editing containment triples' do
|
117
|
+
patch_statement = statement.clone
|
118
|
+
patch_statement.object = 'snorkmaiden'
|
119
|
+
patch = '@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .' \
|
120
|
+
"\n\nAdd { #{statement.subject.to_base} " \
|
121
|
+
"#{statement.predicate.to_base} #{statement.object.to_base} } ."
|
122
|
+
env = { 'CONTENT_TYPE' => 'text/ldpatch',
|
123
|
+
'rack.input' => StringIO.new(patch) }
|
124
|
+
|
125
|
+
expect { subject.request(:patch, 200, {}, env) }
|
126
|
+
.to raise_error RDF::LDP::Conflict
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'with :PUT',
|
131
|
+
if: described_class.private_method_defined?(:put) do
|
132
|
+
context 'when PUTing containment triples' do
|
133
|
+
it 'when creating a resource raises a Conflict error' do
|
134
|
+
graph << statement
|
135
|
+
|
136
|
+
expect { subject.request(:PUT, 200, { 'abc' => 'def' }, env) }
|
137
|
+
.to raise_error RDF::LDP::Conflict
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'when resource exists raises a Conflict error' do
|
141
|
+
subject.create(StringIO.new, 'application/n-triples')
|
142
|
+
graph << statement
|
143
|
+
expect { subject.request(:PUT, 200, { 'abc' => 'def' }, env) }
|
144
|
+
.to raise_error RDF::LDP::Conflict
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'can put existing containment triple' do
|
148
|
+
subject.create(StringIO.new, 'application/n-triples')
|
149
|
+
subject.graph << statement
|
150
|
+
graph << statement
|
151
|
+
expect(subject.request(:PUT, 200, { 'abc' => 'def' }, env).first)
|
152
|
+
.to eq 200
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'writes data when putting existing containment triple' do
|
156
|
+
subject.create(StringIO.new, 'application/n-triples')
|
157
|
+
subject.graph << statement
|
158
|
+
graph << statement
|
159
|
+
|
160
|
+
new_st = RDF::Statement(RDF::URI('http://example.org/new_moomin'),
|
161
|
+
RDF::Vocab::DC.title,
|
162
|
+
'moomin')
|
163
|
+
graph << new_st
|
164
|
+
expect(subject.request(:PUT, 200, { 'abc' => 'def' }, env).last.graph)
|
165
|
+
.to have_statement new_st
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'raises conflict error when without existing containment triples' do
|
169
|
+
subject.create(StringIO.new, 'application/n-triples')
|
170
|
+
subject.graph << statement
|
171
|
+
expect { subject.request(:PUT, 200, { 'abc' => 'def' }, env) }
|
172
|
+
.to raise_error RDF::LDP::Conflict
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'when POST is implemented',
|
178
|
+
if: described_class.private_method_defined?(:post) do
|
179
|
+
let(:graph) { RDF::Graph.new }
|
180
|
+
before { subject.create(StringIO.new, 'application/n-triples') }
|
181
|
+
|
182
|
+
let(:env) do
|
183
|
+
{ 'rack.input' => StringIO.new(graph.dump(:ntriples)),
|
184
|
+
'CONTENT_TYPE' => 'application/n-triples' }
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'returns status 201' do
|
188
|
+
expect(subject.request(:POST, 200, {}, env).first).to eq 201
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'gives created resource as body' do
|
192
|
+
expect(subject.request(:POST, 200, {}, env).last)
|
193
|
+
.to be_a RDF::LDP::Resource
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'generates an id' do
|
197
|
+
expect(subject.request(:POST, 200, {}, env).last.subject_uri)
|
198
|
+
.to be_starts_with subject.subject_uri.to_s
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'adds containment statement to resource' do
|
202
|
+
expect { subject.request(:POST, 200, {}, env) }
|
203
|
+
.to change { subject.containment_triples.count }.from(0).to(1)
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'updates last_modified for container' do
|
207
|
+
expect { subject.request(:POST, 200, {}, env) }
|
208
|
+
.to change { subject.last_modified }
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'updates etag for container' do
|
212
|
+
expect { subject.request(:POST, 200, {}, env) }
|
213
|
+
.to change { subject.etag }
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'with Container interaction model' do
|
217
|
+
it 'creates a basic container' do
|
218
|
+
env['HTTP_LINK'] = "<#{RDF::LDP::Container.to_uri}>;rel=\"type\""
|
219
|
+
expect(subject.request(:POST, 200, {}, env).last)
|
220
|
+
.to be_a RDF::LDP::Container
|
221
|
+
end
|
222
|
+
|
223
|
+
context 'BasicContainer' do
|
224
|
+
it 'creates a basic container' do
|
225
|
+
env['HTTP_LINK'] =
|
226
|
+
'<http://www.w3.org/ns/ldp#BasicContainer>;rel="type"'
|
227
|
+
expect(subject.request(:POST, 200, {}, env).last)
|
228
|
+
.to be_a RDF::LDP::Container
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'DirectContainer' do
|
233
|
+
it 'creates a direct container' do
|
234
|
+
env['HTTP_LINK'] =
|
235
|
+
"<#{RDF::LDP::DirectContainer.to_uri}>;rel=\"type\""
|
236
|
+
|
237
|
+
expect(subject.request(:POST, 200, {}, env).last)
|
238
|
+
.to be_a RDF::LDP::DirectContainer
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context 'IndirectContainer' do
|
243
|
+
it 'creates a indirect container' do
|
244
|
+
env['HTTP_LINK'] =
|
245
|
+
"<#{RDF::LDP::IndirectContainer.to_uri}>;rel=\"type\""
|
246
|
+
|
247
|
+
expect(subject.request(:POST, 200, {}, env).last)
|
248
|
+
.to be_a RDF::LDP::IndirectContainer
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
context 'with a Slug' do
|
254
|
+
it 'creates resource with Slug' do
|
255
|
+
env['HTTP_SLUG'] = 'snork'
|
256
|
+
expect(subject.request(:POST, 200, {}, env).last.subject_uri)
|
257
|
+
.to eq subject.subject_uri / env['HTTP_SLUG']
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'mints a uri when empty Slug is given' do
|
261
|
+
env['HTTP_SLUG'] = ''
|
262
|
+
expect(subject.request(:POST, 200, {}, env).last.subject_uri)
|
263
|
+
.to be_starts_with subject.subject_uri
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'raises a 409 Conflict when slug is already taken' do
|
267
|
+
env['HTTP_SLUG'] = 'snork'
|
268
|
+
subject.request(:POST, 200, {}, env)
|
269
|
+
|
270
|
+
expect { subject.request(:POST, 200, {}, env) }
|
271
|
+
.to raise_error RDF::LDP::Conflict
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'raises a 409 Conflict when slug is already taken but destroyed' do
|
275
|
+
env['HTTP_SLUG'] = 'snork'
|
276
|
+
created = subject.request(:POST, 200, {}, env).last
|
277
|
+
allow(created).to receive(:destroyed?).and_return true
|
278
|
+
|
279
|
+
expect { subject.request(:POST, 200, {}, env) }
|
280
|
+
.to raise_error RDF::LDP::Conflict
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'raises a 406 NotAcceptable if slug has a uri fragment `#`' do
|
284
|
+
env['HTTP_SLUG'] = 'snork#maiden'
|
285
|
+
|
286
|
+
expect { subject.request(:POST, 200, {}, env) }
|
287
|
+
.to raise_error RDF::LDP::NotAcceptable
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'url-encodes Slug' do
|
291
|
+
env['HTTP_SLUG'] = 'snork maiden'
|
292
|
+
expect(subject.request(:POST, 200, {}, env).last.subject_uri)
|
293
|
+
.to eq subject.subject_uri / 'snork%20maiden'
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
context 'with graph content' do
|
298
|
+
before do
|
299
|
+
graph << RDF::Statement(uri, RDF::Vocab::DC.title, 'moomin')
|
300
|
+
graph << RDF::Statement(RDF::Node.new, RDF::Vocab::DC.creator, 'tove')
|
301
|
+
graph <<
|
302
|
+
RDF::Statement(RDF::Node.new, RDF.type, RDF::Vocab::FOAF.Person)
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'parses graph into created resource' do
|
306
|
+
expect(subject.request(:POST, 200, {}, env).last.to_response)
|
307
|
+
.to be_isomorphic_with graph
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'adds a Location header' do
|
311
|
+
expect(subject.request(:POST, 200, {}, env)[1]['Location'])
|
312
|
+
.to start_with subject.subject_uri.to_s
|
313
|
+
end
|
314
|
+
|
315
|
+
context 'with quads' do
|
316
|
+
let(:graph) do
|
317
|
+
RDF::Graph.new(graph_name: subject.subject_uri,
|
318
|
+
data: RDF::Repository.new)
|
319
|
+
end
|
320
|
+
|
321
|
+
let(:env) do
|
322
|
+
{ 'rack.input' => StringIO.new(graph.dump(:nquads)),
|
323
|
+
'CONTENT_TYPE' => 'application/n-quads' }
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'parses graph into created resource without regard for context' do
|
327
|
+
context_free_graph = RDF::Graph.new
|
328
|
+
context_free_graph << graph.statements
|
329
|
+
|
330
|
+
expect(subject.request(:POST, 200, {}, env).last.to_response)
|
331
|
+
.to be_isomorphic_with context_free_graph
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
require 'rdf/ldp/spec/container'
|
2
|
+
|
3
|
+
# @todo: make this set of examples less opinionated about #add behavior.
|
4
|
+
# Break #add tests into another group shared between DirectContainer &
|
5
|
+
# IndirectContainer. This way other implementations can use these specs
|
6
|
+
# but make different intrepretations of loose parts in the LDP spec.
|
7
|
+
shared_examples 'a DirectContainer' do
|
8
|
+
it_behaves_like 'a Container'
|
9
|
+
|
10
|
+
let(:uri) { RDF::URI('http://ex.org/moomin') }
|
11
|
+
let(:repo) { RDF::Repository.new }
|
12
|
+
subject { described_class.new(uri, repo) }
|
13
|
+
|
14
|
+
let(:has_member_statement) do
|
15
|
+
RDF::Statement(subject.subject_uri,
|
16
|
+
RDF::Vocab::LDP.hasMemberRelation,
|
17
|
+
RDF::Vocab::DC.hasPart)
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:is_member_of_statement) do
|
21
|
+
RDF::Statement(subject.subject_uri,
|
22
|
+
RDF::Vocab::LDP.isMemberOfRelation,
|
23
|
+
RDF::Vocab::DC.isPartOf)
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#add' do
|
27
|
+
let(:added) { described_class.new(added_uri, repo) }
|
28
|
+
let(:added_uri) { RDF::URI('http://ex.org/too-ticky') }
|
29
|
+
|
30
|
+
context 'when the membership resource does not exist' do
|
31
|
+
before do
|
32
|
+
subject.create(StringIO.new, 'application/n-triples')
|
33
|
+
|
34
|
+
subject.graph.update has_member_statement
|
35
|
+
subject.graph.update RDF::Statement(subject.subject_uri,
|
36
|
+
RDF::Vocab::LDP.membershipResource,
|
37
|
+
membership_resource_uri)
|
38
|
+
end
|
39
|
+
|
40
|
+
let(:membership_resource_uri) do
|
41
|
+
RDF::URI('http://example.com/moomin_resource')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'adds membership triple to the container' do
|
45
|
+
expect { subject.add(added) }
|
46
|
+
.to change { subject.graph.statements }
|
47
|
+
.to include subject.make_membership_triple(added_uri)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when the membership resource exists' do
|
52
|
+
before { subject.create(StringIO.new, 'application/n-triples') }
|
53
|
+
|
54
|
+
it 'adds membership triple to container' do
|
55
|
+
expect(subject.add(added).graph)
|
56
|
+
.to have_statement subject.make_membership_triple(added_uri)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'updates last_modified for membership resource' do
|
60
|
+
expect { subject.add(added).graph }
|
61
|
+
.to change { subject.last_modified }
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'updates etag for for membership resource' do
|
65
|
+
expect { subject.add(added).graph }
|
66
|
+
.to change { subject.etag }
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'adds membership triple to container for custom membership resource' do
|
70
|
+
repo = RDF::Repository.new
|
71
|
+
subject = described_class.new(uri, repo)
|
72
|
+
mem_rs = RDF::LDP::RDFSource.new(RDF::URI('http://ex.org/mymble'),
|
73
|
+
repo)
|
74
|
+
|
75
|
+
g = RDF::Graph.new << RDF::Statement(subject.subject_uri,
|
76
|
+
RDF::Vocab::LDP.membershipResource,
|
77
|
+
mem_rs.subject_uri)
|
78
|
+
|
79
|
+
subject.create(StringIO.new(g.dump(:ntriples)), 'application/n-triples')
|
80
|
+
mem_rs.create(StringIO.new, 'application/n-triples')
|
81
|
+
|
82
|
+
subject.add(added)
|
83
|
+
|
84
|
+
expect(subject.graph)
|
85
|
+
.to have_statement subject.make_membership_triple(added_uri)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'adds membership triple to membership resource with #fragment' do
|
89
|
+
repo = RDF::Repository.new
|
90
|
+
subject = described_class.new(uri, repo)
|
91
|
+
|
92
|
+
mem_rs = subject.subject_uri / '#membership'
|
93
|
+
|
94
|
+
g = RDF::Graph.new << RDF::Statement(subject.subject_uri,
|
95
|
+
RDF::Vocab::LDP.membershipResource,
|
96
|
+
mem_rs)
|
97
|
+
|
98
|
+
subject.create(StringIO.new(g.dump(:ttl)), 'application/n-triples')
|
99
|
+
expect(subject.add(added).graph)
|
100
|
+
.to have_statement subject.make_membership_triple(added_uri)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'adds membership triple to container for LDP-NR membership resource' do
|
104
|
+
repo = RDF::Repository.new
|
105
|
+
container = described_class.new(uri, repo)
|
106
|
+
|
107
|
+
nr = RDF::LDP::NonRDFSource.new('http://example.org/moomin_file',
|
108
|
+
repo)
|
109
|
+
|
110
|
+
g = RDF::Graph.new << RDF::Statement(subject.subject_uri,
|
111
|
+
RDF::Vocab::LDP.membershipResource,
|
112
|
+
nr.to_uri)
|
113
|
+
|
114
|
+
container
|
115
|
+
.create(StringIO.new(g.dump(:ntriples)), 'application/n-triples')
|
116
|
+
|
117
|
+
nr.create(StringIO.new, 'application/n-triples')
|
118
|
+
|
119
|
+
container.add(added)
|
120
|
+
expect(container.graph)
|
121
|
+
.to have_statement container.make_membership_triple(added_uri)
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'with multiple membership resources' do
|
125
|
+
it 'raises an error' do
|
126
|
+
subject.graph << RDF::Statement(subject.subject_uri,
|
127
|
+
RDF::Vocab::LDP.membershipResource,
|
128
|
+
subject.subject_uri)
|
129
|
+
subject.graph << RDF::Statement(subject.subject_uri,
|
130
|
+
RDF::Vocab::LDP.membershipResource,
|
131
|
+
(subject.subject_uri / 'moomin'))
|
132
|
+
|
133
|
+
expect { subject.add(added) }
|
134
|
+
.to raise_error RDF::LDP::RequestError
|
135
|
+
expect(subject.containment_triples).to be_empty
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#remove' do
|
142
|
+
let(:added) { described_class.new(added_uri, repo) }
|
143
|
+
let(:added_uri) { RDF::URI('http://ex.org/too-ticky') }
|
144
|
+
|
145
|
+
context 'when the membership resource exists' do
|
146
|
+
before { subject.create(StringIO.new, 'application/n-triples') }
|
147
|
+
|
148
|
+
it 'removes membership triple to membership resource' do
|
149
|
+
subject.graph << subject.make_membership_triple(added_uri)
|
150
|
+
expect(subject.remove(added_uri).graph)
|
151
|
+
.not_to have_statement subject.make_membership_triple(added_uri)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe '#membership_constant_uri' do
|
157
|
+
it 'when created defaults to #subject_uri' do
|
158
|
+
subject.create(StringIO.new, 'application/n-triples')
|
159
|
+
expect(subject.membership_constant_uri).to eq subject.subject_uri
|
160
|
+
expect(subject.graph)
|
161
|
+
.to have_statement RDF::Statement(subject.subject_uri,
|
162
|
+
RDF::Vocab::LDP.membershipResource,
|
163
|
+
subject.subject_uri)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'gives membership resource' do
|
167
|
+
membership_resource = (subject.subject_uri / '#too-ticky')
|
168
|
+
subject.graph << RDF::Statement(subject.subject_uri,
|
169
|
+
RDF::Vocab::LDP.membershipResource,
|
170
|
+
membership_resource)
|
171
|
+
expect(subject.membership_constant_uri).to eq membership_resource
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'raises an error if multiple are present' do
|
175
|
+
membership_resource = (subject.subject_uri / '#too-ticky')
|
176
|
+
subject.graph << RDF::Statement(subject.subject_uri,
|
177
|
+
RDF::Vocab::LDP.membershipResource,
|
178
|
+
membership_resource)
|
179
|
+
|
180
|
+
subject.graph << RDF::Statement(subject.subject_uri,
|
181
|
+
RDF::Vocab::LDP.membershipResource,
|
182
|
+
subject.subject_uri)
|
183
|
+
|
184
|
+
expect { subject.membership_constant_uri }
|
185
|
+
.to raise_error RDF::LDP::RequestError
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe '#membership_predicate' do
|
190
|
+
it 'when created returns a uri' do
|
191
|
+
subject.create(StringIO.new, 'application/n-triples')
|
192
|
+
expect(subject.membership_predicate).to be_a RDF::URI
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'gives assigned member relation predicate for hasMember' do
|
196
|
+
subject.graph << has_member_statement
|
197
|
+
|
198
|
+
expect(subject.membership_predicate).to eq RDF::Vocab::DC.hasPart
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'gives assigned member relation predicate for isMemberOf' do
|
202
|
+
subject.graph << is_member_of_statement
|
203
|
+
|
204
|
+
expect(subject.membership_predicate).to eq RDF::Vocab::DC.isPartOf
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'raises an error if multiple relation predicates are present' do
|
208
|
+
subject.graph << has_member_statement
|
209
|
+
subject.graph << is_member_of_statement
|
210
|
+
|
211
|
+
expect { subject.membership_predicate }
|
212
|
+
.to raise_error RDF::LDP::RequestError
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe '#make_membership_triple' do
|
217
|
+
context 'with hasMember' do
|
218
|
+
before do
|
219
|
+
g = RDF::Graph.new << has_member_statement
|
220
|
+
subject.create(StringIO.new(g.dump(:ntriples)), 'application/n-triples')
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'is constant - predicate - derived' do
|
224
|
+
expect(subject.make_membership_triple(uri))
|
225
|
+
.to eq RDF::Statement(subject.membership_constant_uri,
|
226
|
+
subject.membership_predicate,
|
227
|
+
uri)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'with isMemberOf' do
|
232
|
+
before do
|
233
|
+
g = RDF::Graph.new << is_member_of_statement
|
234
|
+
subject.create(StringIO.new(g.dump(:ntriples)), 'application/n-triples')
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'is derived - predicate - constant' do
|
238
|
+
expect(subject.make_membership_triple(uri))
|
239
|
+
.to eq RDF::Statement(uri,
|
240
|
+
subject.membership_predicate,
|
241
|
+
subject.membership_constant_uri)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|