rdf-ldp 0.9.2 → 0.9.3

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.
@@ -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
@@ -0,0 +1,152 @@
1
+ require 'rdf/ldp/spec/direct_container'
2
+
3
+ shared_examples 'an IndirectContainer' do
4
+ it_behaves_like 'a DirectContainer'
5
+
6
+ shared_context 'with a relation' do
7
+ before do
8
+ subject.create(StringIO.new(graph.dump(:ntriples)),
9
+ 'application/n-triples')
10
+ end
11
+
12
+ let(:graph) { RDF::Graph.new << inserted_content_statement }
13
+ let(:relation_predicate) { RDF::Vocab::DC.creator }
14
+
15
+ let(:inserted_content_statement) do
16
+ RDF::Statement(uri,
17
+ RDF::Vocab::LDP.insertedContentRelation,
18
+ relation_predicate)
19
+ end
20
+ end
21
+
22
+ describe '#inserted_content_relation' do
23
+ it 'returns a uri' do
24
+ subject.create(StringIO.new, 'application/n-triples')
25
+ expect(subject.inserted_content_relation).to be_a RDF::URI
26
+ end
27
+
28
+ context 'with a relation' do
29
+ include_context 'with a relation'
30
+
31
+ it 'gives the relation' do
32
+ expect(subject.inserted_content_relation).to eq relation_predicate
33
+ end
34
+
35
+ it 'raises an error when more than one exists' do
36
+ new_statement = inserted_content_statement.clone
37
+ new_statement.object = RDF::Vocab::DC.relation
38
+ subject.graph << new_statement
39
+ expect { subject.inserted_content_relation }
40
+ .to raise_error RDF::LDP::NotAcceptable
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#add' do
46
+ include_context 'with a relation'
47
+
48
+ subject { described_class.new(uri, repo) }
49
+
50
+ let(:repo) { RDF::Repository.new }
51
+ let(:resource_uri) { RDF::URI('http://example.org/too-ticky') }
52
+ let(:contained_resource) { RDF::LDP::RDFSource.new(resource_uri, repo) }
53
+
54
+ context 'when no derived URI is found' do
55
+ it 'raises NotAcceptable' do
56
+ expect { subject.add(contained_resource) }
57
+ .to raise_error RDF::LDP::NotAcceptable
58
+ end
59
+
60
+ it 'does not create the resource' do
61
+ begin; subject.add(contained_resource); rescue; end
62
+ expect(contained_resource).not_to exist
63
+ end
64
+ end
65
+
66
+ context 'with expected predicate' do
67
+ before { contained_resource.graph << statement }
68
+
69
+ let(:target_uri) { contained_resource.to_uri / '#me' }
70
+
71
+ let(:statement) do
72
+ RDF::Statement(contained_resource.to_uri,
73
+ relation_predicate,
74
+ target_uri)
75
+ end
76
+
77
+ it 'when membership resource does not exist raises NotAcceptable' do
78
+ new_resource = described_class.new(uri / 'new', repo)
79
+ expect { new_resource.add(contained_resource) }
80
+ .to raise_error RDF::LDP::NotAcceptable
81
+ end
82
+
83
+ context 'when the container exists' do
84
+ it 'adds membership triple' do
85
+ subject.add(contained_resource)
86
+ expect(subject.graph.statements)
87
+ .to include RDF::Statement(subject.to_uri,
88
+ subject.membership_predicate,
89
+ target_uri)
90
+ end
91
+
92
+ it 'for multiple predicates raises NotAcceptable' do
93
+ new_statement = statement.clone
94
+ new_statement.object = contained_resource.to_uri / '#you'
95
+ contained_resource.graph << new_statement
96
+ expect { subject.add(contained_resource) }
97
+ .to raise_error RDF::LDP::NotAcceptable
98
+ end
99
+
100
+ it 'for an LDP-NR raises NotAcceptable' do
101
+ nr_resource = RDF::LDP::NonRDFSource.new(resource_uri, repo)
102
+ expect { subject.add(nr_resource) }
103
+ .to raise_error RDF::LDP::NotAcceptable
104
+ end
105
+
106
+ context 'with membership resource' do
107
+ before do
108
+ subject.graph
109
+ .delete([uri, RDF::Vocab::LDP.membershipResource, nil])
110
+ subject.graph << RDF::Statement(uri,
111
+ RDF::Vocab::LDP.membershipResource,
112
+ membership_resource)
113
+ end
114
+
115
+ let(:membership_resource) { uri }
116
+
117
+ it 'raises error when resource does not exist' do
118
+ new_resource = described_class.new(uri / 'new', repo)
119
+ expect { new_resource.add(contained_resource) }
120
+ .to raise_error RDF::LDP::NotAcceptable
121
+ end
122
+
123
+ context 'when the membership resource is not in the server' do
124
+ let(:membership_resource) { uri / '#me' }
125
+
126
+ it 'adds membership triple to container' do
127
+ contained_resource.create(StringIO.new, 'application/n-triples')
128
+ subject.add(contained_resource)
129
+
130
+ expect(subject.graph.statements)
131
+ .to include RDF::Statement(membership_resource,
132
+ subject.membership_predicate,
133
+ target_uri)
134
+ end
135
+
136
+ it 'removes membership triple to container' do
137
+ contained_resource.create(StringIO.new, 'application/n-triples')
138
+
139
+ subject.add(contained_resource)
140
+ subject.remove(contained_resource)
141
+
142
+ expect(subject.graph.statements)
143
+ .not_to include RDF::Statement(membership_resource,
144
+ subject.membership_predicate,
145
+ target_uri)
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,138 @@
1
+ require 'rdf/ldp/spec/resource'
2
+
3
+ shared_examples 'a NonRDFSource' do
4
+ it_behaves_like 'a Resource'
5
+
6
+ subject { described_class.new(uri) }
7
+ let(:uri) { RDF::URI 'http://example.org/moomin' }
8
+
9
+ let(:contents) { StringIO.new('mummi') }
10
+
11
+ after { subject.destroy }
12
+
13
+ describe '#non_rdf_source?' do
14
+ it { is_expected.to be_non_rdf_source }
15
+ end
16
+
17
+ describe '#create' do
18
+ it 'writes the input to body' do
19
+ subject.create(contents, 'text/plain')
20
+ contents.rewind
21
+ expect(subject.to_response.each.to_a).to eq contents.each.to_a
22
+ end
23
+
24
+ it 'sets #content_type' do
25
+ expect { subject.create(StringIO.new(''), 'text/plain') }
26
+ .to change { subject.content_type }.to('text/plain')
27
+ end
28
+
29
+ it 'persists to resource' do
30
+ repo = RDF::Repository.new
31
+ saved = described_class.new(uri, repo)
32
+
33
+ saved.create(contents, 'text/plain')
34
+ contents.rewind
35
+
36
+ loaded = RDF::LDP::Resource.find(uri, repo)
37
+ expect(loaded.to_response.each.to_a).to eq contents.each.to_a
38
+ end
39
+
40
+ it 'creates an LDP::RDFSource' do
41
+ repo = RDF::Repository.new
42
+ saved = described_class.new(uri, repo)
43
+ description = RDF::LDP::RDFSource.new(subject.description_uri, repo)
44
+
45
+ expect { saved.create(contents, 'text/plain') }
46
+ .to change { description.exists? }.from(false).to(true)
47
+ end
48
+ end
49
+
50
+ describe '#update' do
51
+ before { subject.create(contents, 'text/plain') }
52
+
53
+ it 'writes the input to body' do
54
+ new_contents = StringIO.new('snorkmaiden')
55
+ expect { subject.update(new_contents, 'text/plain') }
56
+ .to change { subject.to_response.to_a }
57
+ .from(a_collection_containing_exactly('mummi'))
58
+ .to(a_collection_containing_exactly('snorkmaiden'))
59
+ end
60
+
61
+ it 'updates #content_type' do
62
+ expect { subject.update(contents, 'text/prs.moomin') }
63
+ .to change { subject.content_type }
64
+ .from('text/plain').to('text/prs.moomin')
65
+ end
66
+ end
67
+
68
+ describe '#description' do
69
+ it 'is not found' do
70
+ expect { subject.description }.to raise_error RDF::LDP::NotFound
71
+ end
72
+
73
+ context 'when it exists' do
74
+ before { subject.create(StringIO.new(''), 'text/plain') }
75
+
76
+ it 'is an RDFSource' do
77
+ expect(subject.description).to be_rdf_source
78
+ end
79
+
80
+ it 'is the description uri' do
81
+ expect(subject.description.to_uri).to eq subject.description_uri
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#description_uri' do
87
+ it 'is a uri' do
88
+ expect(subject.description_uri).to be_a RDF::URI
89
+ end
90
+ end
91
+
92
+ describe '#storage' do
93
+ it 'sets a default storage adapter' do
94
+ expect(subject.storage).to be_a RDF::LDP::NonRDFSource::FileStorageAdapter
95
+ end
96
+
97
+ it 'explicitly sets a storage adapter' do
98
+ class DummyAdapter < RDF::LDP::NonRDFSource::FileStorageAdapter
99
+ end
100
+
101
+ dummy_subject = described_class.new(uri, nil, DummyAdapter)
102
+ expect(dummy_subject.storage).to be_a DummyAdapter
103
+ end
104
+ end
105
+
106
+ describe '#to_response' do
107
+ it 'gives an empty response if it is new' do
108
+ expect(subject.to_response.to_a).to eq []
109
+ end
110
+
111
+ it 'does not create a non-existant file' do
112
+ subject.to_response
113
+ expect(subject.storage.send(:file_exists?)).to be false
114
+ end
115
+ end
116
+
117
+ describe '#destroy' do
118
+ before { subject.create(contents, 'text/plain') }
119
+
120
+ it 'deletes the content' do
121
+ expect { subject.destroy }
122
+ .to change { subject.to_response.to_a }
123
+ .from(a_collection_containing_exactly('mummi')).to([])
124
+ end
125
+
126
+ it 'marks resource as destroyed' do
127
+ expect { subject.destroy }
128
+ .to change { subject.destroyed? }.from(false).to(true)
129
+ end
130
+ end
131
+
132
+ describe '#content_type' do
133
+ it 'sets and gets a content_type' do
134
+ expect { subject.content_type = 'text/plain' }
135
+ .to change { subject.content_type }.to('text/plain')
136
+ end
137
+ end
138
+ end