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,242 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'rdf/spec'
|
3
|
+
require 'rdf/spec/matchers'
|
4
|
+
require 'timecop'
|
5
|
+
|
6
|
+
shared_examples 'a Resource' do
|
7
|
+
describe '.to_uri' do
|
8
|
+
it { expect(described_class.to_uri).to be_a RDF::URI }
|
9
|
+
end
|
10
|
+
|
11
|
+
subject { described_class.new(uri) }
|
12
|
+
let(:uri) { RDF::URI 'http://example.org/moomin' }
|
13
|
+
|
14
|
+
it { is_expected.to be_ldp_resource }
|
15
|
+
it { is_expected.to respond_to :container? }
|
16
|
+
it { is_expected.to respond_to :rdf_source? }
|
17
|
+
it { is_expected.to respond_to :non_rdf_source? }
|
18
|
+
|
19
|
+
it { subject.send(:set_last_modified) }
|
20
|
+
|
21
|
+
describe '#exists?' do
|
22
|
+
it 'does not exist' do
|
23
|
+
expect(subject).not_to exist
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'while existing' do
|
27
|
+
before { subject.create(StringIO.new, 'application/n-triples') }
|
28
|
+
|
29
|
+
subject { described_class.new(uri, repository) }
|
30
|
+
let(:repository) { RDF::Repository.new }
|
31
|
+
|
32
|
+
it 'exists' do
|
33
|
+
expect(subject).to exist
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'is different from same URI with trailing /' do
|
37
|
+
expect(described_class.new(uri + '/', repository)).not_to exist
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#allowed_methods' do
|
43
|
+
it 'responds to all methods returned' do
|
44
|
+
subject.allowed_methods.each do |method|
|
45
|
+
expect(subject.respond_to?(method.downcase, true)).to be true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'includes the MUST methods' do
|
50
|
+
expect(subject.allowed_methods).to include(:GET, :OPTIONS, :HEAD)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#create' do
|
55
|
+
it 'accepts two args' do
|
56
|
+
expect(described_class.instance_method(:create).arity).to eq 2
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'modified time' do
|
60
|
+
before { Timecop.freeze }
|
61
|
+
after { Timecop.return }
|
62
|
+
|
63
|
+
it 'sets last_modified' do
|
64
|
+
subject.create(StringIO.new, 'text/turtle')
|
65
|
+
expect(subject.last_modified).to eq DateTime.now
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'adds a type triple to metagraph' do
|
70
|
+
subject.create(StringIO.new, 'application/n-triples')
|
71
|
+
expect(subject.metagraph)
|
72
|
+
.to have_statement RDF::Statement(subject.subject_uri,
|
73
|
+
RDF.type,
|
74
|
+
described_class.to_uri)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'yields a transaction' do
|
78
|
+
expect { |b| subject.create(StringIO.new, 'application/n-triples', &b) }
|
79
|
+
.to yield_with_args(be_kind_of(RDF::Transaction))
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'marks resource as existing' do
|
83
|
+
expect { subject.create(StringIO.new, 'application/n-triples') }
|
84
|
+
.to change { subject.exists? }.from(false).to(true)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'returns self' do
|
88
|
+
expect(subject.create(StringIO.new, 'application/n-triples'))
|
89
|
+
.to eq subject
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'raises Conlict when already exists' do
|
93
|
+
subject.create(StringIO.new, 'application/n-triples')
|
94
|
+
expect { subject.create(StringIO.new, 'application/n-triples') }
|
95
|
+
.to raise_error RDF::LDP::Conflict
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#update' do
|
100
|
+
it 'accepts two args' do
|
101
|
+
expect(described_class.instance_method(:update).arity).to eq 2
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'returns self' do
|
105
|
+
expect(subject.update(StringIO.new, 'application/n-triples'))
|
106
|
+
.to eq subject
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'yields a changeset' do
|
110
|
+
expect { |b| subject.update(StringIO.new, 'application/n-triples', &b) }
|
111
|
+
.to yield_with_args(be_kind_of(RDF::Transaction))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe '#destroy' do
|
116
|
+
it 'accepts no args' do
|
117
|
+
expect(described_class.instance_method(:destroy).arity).to eq 0
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#metagraph' do
|
122
|
+
it 'returns a graph' do
|
123
|
+
expect(subject.metagraph).to be_a RDF::Graph
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'has the metagraph name for the resource' do
|
127
|
+
expect(subject.metagraph.graph_name).to eq subject.subject_uri / '#meta'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '#etag' do
|
132
|
+
before { subject.create(StringIO.new, 'application/n-triples') }
|
133
|
+
|
134
|
+
it 'has an etag' do
|
135
|
+
expect(subject.etag).to be_a String
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'updates etag on change' do
|
139
|
+
expect { subject.update(StringIO.new, 'application/n-triples') }
|
140
|
+
.to change { subject.etag }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#last_modified' do
|
145
|
+
it 'returns nil when no dc:modified triple is present' do
|
146
|
+
expect(subject.last_modified).to be_nil
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'raises an error when exists without dc:modified triple is present' do
|
150
|
+
allow(subject).to receive(:exists?).and_return true
|
151
|
+
expect { subject.last_modified }.to raise_error RDF::LDP::RequestError
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'with dc:modified triple' do
|
155
|
+
before do
|
156
|
+
subject.metagraph.update([subject.subject_uri,
|
157
|
+
RDF::Vocab::DC.modified,
|
158
|
+
datetime])
|
159
|
+
end
|
160
|
+
|
161
|
+
let(:datetime) { DateTime.now }
|
162
|
+
|
163
|
+
it 'returns date in `dc:modified`' do
|
164
|
+
expect(subject.last_modified).to eq datetime
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#to_response' do
|
170
|
+
it 'returns an object that responds to #each' do
|
171
|
+
expect(subject.to_response).to respond_to :each
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe '#request' do
|
176
|
+
it 'sends the message to itself' do
|
177
|
+
expect(subject).to receive(:blah)
|
178
|
+
subject.request(:BLAH, 200, {}, {})
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'raises MethodNotAllowed when method is unimplemented' do
|
182
|
+
allow(subject).to receive(:not_implemented)
|
183
|
+
.and_raise NotImplementedError
|
184
|
+
expect { subject.request(:not_implemented, 200, {}, {}) }
|
185
|
+
.to raise_error(RDF::LDP::MethodNotAllowed)
|
186
|
+
end
|
187
|
+
|
188
|
+
[:GET, :OPTIONS, :HEAD].each do |method|
|
189
|
+
it "responds to #{method}" do
|
190
|
+
expect(subject.request(method, 200, {}, {}).size).to eq 3
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
[:PATCH, :POST, :PUT, :DELETE, :TRACE, :CONNECT].each do |method|
|
195
|
+
it "responds to or errors on #{method}" do
|
196
|
+
g = RDF::Graph.new << [RDF::Node.new, RDF.type, 'moomin']
|
197
|
+
env = { 'CONTENT_TYPE' => 'application/n-triples',
|
198
|
+
'rack.input' => StringIO.new(g.dump(:ntriples)) }
|
199
|
+
|
200
|
+
begin
|
201
|
+
response = subject.request(method, 200, {}, env)
|
202
|
+
expect(response.size).to eq 3
|
203
|
+
rescue => e
|
204
|
+
expect(e).to be_a RDF::LDP::RequestError
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe 'HTTP headers' do
|
210
|
+
before { subject.create(StringIO.new, 'text/turtle') }
|
211
|
+
let(:headers) { subject.request(:GET, 200, {}, {})[1] }
|
212
|
+
|
213
|
+
it 'has ETag' do
|
214
|
+
expect(headers['ETag']).to eq subject.etag
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'has Last-Modified' do
|
218
|
+
expect(headers['Last-Modified']).to eq subject.last_modified.httpdate
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'has Allow' do
|
222
|
+
expect(headers['Allow']).to be_a String
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'has Link' do
|
226
|
+
expect(headers['Link']).to be_a String
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'responds to :GET' do
|
231
|
+
expect { subject.request(:GET, 200, {}, {}) }.not_to raise_error
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'responds to :HEAD' do
|
235
|
+
expect { subject.request(:OPTIONS, 200, {}, {}) }.not_to raise_error
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'responds to :OPTIONS' do
|
239
|
+
expect { subject.request(:OPTIONS, 200, {}, {}) }.not_to raise_error
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
data/lib/rdf/ldp/spec.rb
ADDED
@@ -63,7 +63,7 @@ module RDF::LDP
|
|
63
63
|
#
|
64
64
|
# @return [IO] an object conforming to the Ruby IO interface
|
65
65
|
def io(&block)
|
66
|
-
FileUtils.mkdir_p(path_dir) unless Dir.
|
66
|
+
FileUtils.mkdir_p(path_dir) unless Dir.exist?(path_dir)
|
67
67
|
FileUtils.touch(path) unless file_exists?
|
68
68
|
|
69
69
|
File.open(path, 'r+b', &block)
|
@@ -72,7 +72,7 @@ module RDF::LDP
|
|
72
72
|
##
|
73
73
|
# @return [Boolean] 1 if the file has been deleted, otherwise false
|
74
74
|
def delete
|
75
|
-
return false unless File.
|
75
|
+
return false unless File.exist?(path)
|
76
76
|
File.delete(path)
|
77
77
|
end
|
78
78
|
|
@@ -81,7 +81,7 @@ module RDF::LDP
|
|
81
81
|
##
|
82
82
|
# @return [Boolean]
|
83
83
|
def file_exists?
|
84
|
-
File.
|
84
|
+
File.exist?(path)
|
85
85
|
end
|
86
86
|
|
87
87
|
##
|
@@ -99,4 +99,4 @@ module RDF::LDP
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
end
|
102
|
-
end
|
102
|
+
end
|
data/lib/rdf/ldp/version.rb
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
module RDF::LDP
|
2
|
+
##
|
3
|
+
# Version Number
|
4
|
+
#
|
5
|
+
# @example getting a version string
|
6
|
+
# RDF::LDP::VERSION # or
|
7
|
+
# "#{RDF::LDP::VERSION}"
|
8
|
+
#
|
2
9
|
module VERSION
|
3
10
|
FILE = File.expand_path('../../../../VERSION', __FILE__)
|
4
11
|
MAJOR, MINOR, TINY, EXTRA = File.read(FILE).chomp.split('.')
|
@@ -6,14 +13,20 @@ module RDF::LDP
|
|
6
13
|
|
7
14
|
##
|
8
15
|
# @return [String]
|
9
|
-
def self.to_s
|
16
|
+
def self.to_s
|
17
|
+
STRING
|
18
|
+
end
|
10
19
|
|
11
20
|
##
|
12
21
|
# @return [String]
|
13
|
-
def self.to_str
|
22
|
+
def self.to_str
|
23
|
+
STRING
|
24
|
+
end
|
14
25
|
|
15
26
|
##
|
16
27
|
# @return [Array(Integer, Integer, Integer)]
|
17
|
-
def self.to_a
|
28
|
+
def self.to_a
|
29
|
+
[MAJOR, MINOR, TINY]
|
30
|
+
end
|
18
31
|
end
|
19
32
|
end
|
data/lib/rdf/ldp.rb
CHANGED
@@ -19,14 +19,15 @@ module RDF
|
|
19
19
|
# Rack servers.
|
20
20
|
#
|
21
21
|
# @see RDF::LDP::Resource
|
22
|
-
# @see
|
22
|
+
# @see https://www.w3.org/TR/ldp/ for the Linked Data platform specification
|
23
23
|
module LDP
|
24
24
|
InteractionModel.register(RDF::LDP::RDFSource, default: true)
|
25
|
-
InteractionModel.register(RDF::LDP::Container,
|
25
|
+
InteractionModel.register(RDF::LDP::Container,
|
26
|
+
for: RDF::Vocab::LDP.BasicContainer)
|
26
27
|
InteractionModel.register(RDF::LDP::DirectContainer)
|
27
28
|
InteractionModel.register(RDF::LDP::IndirectContainer)
|
28
29
|
InteractionModel.register(RDF::LDP::NonRDFSource)
|
29
|
-
|
30
|
+
|
30
31
|
CONTAINER_CLASSES = {
|
31
32
|
basic: RDF::Vocab::LDP.BasicContainer.freeze,
|
32
33
|
direct: RDF::LDP::DirectContainer.to_uri.freeze,
|