rdf-ldp 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +1 -1
- data/VERSION +1 -1
- data/lib/rdf/ldp.rb +12 -12
- data/lib/rdf/ldp/container.rb +41 -44
- data/lib/rdf/ldp/direct_container.rb +38 -38
- data/lib/rdf/ldp/indirect_container.rb +33 -29
- data/lib/rdf/ldp/non_rdf_source.rb +42 -131
- data/lib/rdf/ldp/rdf_source.rb +57 -55
- data/lib/rdf/ldp/resource.rb +46 -42
- data/lib/rdf/ldp/storage_adapters/file_storage_adapter.rb +102 -0
- metadata +13 -13
data/lib/rdf/ldp/resource.rb
CHANGED
@@ -33,7 +33,7 @@ module RDF::LDP
|
|
33
33
|
# resource = RDF::LDP::Resource.new('http://example.org/moomin', repository)
|
34
34
|
# resource.exists? # => false
|
35
35
|
#
|
36
|
-
# resource.create('', 'text/plain')
|
36
|
+
# resource.create(StringIO.new(''), 'text/plain')
|
37
37
|
#
|
38
38
|
# resource.exists? # => true
|
39
39
|
# resource.metagraph.dump :ttl
|
@@ -56,8 +56,8 @@ module RDF::LDP
|
|
56
56
|
# resource.exists? # => true
|
57
57
|
# resource.destroyed? # => true
|
58
58
|
#
|
59
|
-
# Rack (via `RDF::LDP::Rack`) uses the `#request` method to dispatch requests
|
60
|
-
# interpret responses. Disallowed HTTP methods result in
|
59
|
+
# Rack (via `RDF::LDP::Rack`) uses the `#request` method to dispatch requests
|
60
|
+
# and interpret responses. Disallowed HTTP methods result in
|
61
61
|
# `RDF::LDP::MethodNotAllowed`. Individual Resources populate `Link`, `Allow`,
|
62
62
|
# `ETag`, `Last-Modified`, and `Accept-*` headers as required by LDP. All
|
63
63
|
# subclasses (MUST) return `self` as the Body, and respond to `#each`/
|
@@ -80,10 +80,14 @@ module RDF::LDP
|
|
80
80
|
#
|
81
81
|
# resource.request(:put, 200, {}, {}) # RDF::LDP::MethodNotAllowed: put
|
82
82
|
#
|
83
|
-
# @see http://www.w3.org/TR/ldp/
|
84
|
-
# @see http://www.w3.org/TR/ldp/#dfn-linked-data-platform-resource
|
85
|
-
#
|
83
|
+
# @see http://www.w3.org/TR/ldp/ Linked Data platform Specification
|
84
|
+
# @see http://www.w3.org/TR/ldp/#dfn-linked-data-platform-resource Definition
|
85
|
+
# of 'Resource' in LDP
|
86
86
|
class Resource
|
87
|
+
CONTAINS_URI = RDF::Vocab::LDP.contains.freeze
|
88
|
+
INVALIDATED_AT_URI = RDF::Vocab::PROV.invalidatedAtTime.freeze
|
89
|
+
MODIFIED_URI = RDF::Vocab::DC.modified.freeze
|
90
|
+
|
87
91
|
# @!attribute [r] subject_uri
|
88
92
|
# an rdf term identifying the `Resource`
|
89
93
|
attr_reader :subject_uri
|
@@ -149,18 +153,19 @@ module RDF::LDP
|
|
149
153
|
# @return [Class] a subclass of {RDF::LDP::Resource} matching the
|
150
154
|
# requested interaction model;
|
151
155
|
def interaction_model(link_header)
|
152
|
-
models =
|
153
|
-
|
154
|
-
|
156
|
+
models =
|
157
|
+
LinkHeader.parse(link_header)
|
158
|
+
.links.select { |link| link['rel'].casecmp 'type' }
|
159
|
+
.map { |link| link.href }
|
155
160
|
|
156
161
|
return RDFSource if models.empty?
|
157
162
|
match = INTERACTION_MODELS.keys.reverse.find { |u| models.include? u }
|
158
163
|
|
159
164
|
if match == RDF::LDP::NonRDFSource.to_uri
|
160
165
|
raise NotAcceptable if
|
161
|
-
models.include?(RDF::LDP::RDFSource.to_uri)
|
162
|
-
models.include?(RDF::LDP::Container.to_uri)
|
163
|
-
models.include?(RDF::LDP::DirectContainer.to_uri)
|
166
|
+
models.include?(RDF::LDP::RDFSource.to_uri) ||
|
167
|
+
models.include?(RDF::LDP::Container.to_uri) ||
|
168
|
+
models.include?(RDF::LDP::DirectContainer.to_uri) ||
|
164
169
|
models.include?(RDF::LDP::IndirectContainer.to_uri) ||
|
165
170
|
models.include?(RDF::URI('http://www.w3.org/ns/ldp#BasicContainer'))
|
166
171
|
end
|
@@ -217,7 +222,7 @@ module RDF::LDP
|
|
217
222
|
# @raise [RDF::LDP::RequestError] when creation fails. May raise various
|
218
223
|
# subclasses for the appropriate response codes.
|
219
224
|
# @raise [RDF::LDP::Conflict] when the resource exists
|
220
|
-
def create(
|
225
|
+
def create(_input, _content_type)
|
221
226
|
raise Conflict if exists?
|
222
227
|
|
223
228
|
@data.transaction(mutable: true) do |transaction|
|
@@ -268,13 +273,13 @@ module RDF::LDP
|
|
268
273
|
#
|
269
274
|
# @todo Use of owl:Nothing is probably problematic. Define an internal
|
270
275
|
# namespace and class represeting deletion status as a stateful property.
|
271
|
-
def destroy
|
276
|
+
def destroy
|
272
277
|
@data.transaction(mutable: true) do |transaction|
|
273
278
|
containers.each { |c| c.remove(self, transaction) if c.container? }
|
274
279
|
transaction.insert RDF::Statement(subject_uri,
|
275
|
-
|
276
|
-
|
277
|
-
|
280
|
+
INVALIDATED_AT_URI,
|
281
|
+
DateTime.now,
|
282
|
+
graph_name: metagraph_name)
|
278
283
|
yield transaction if block_given?
|
279
284
|
end
|
280
285
|
self
|
@@ -294,8 +299,8 @@ module RDF::LDP
|
|
294
299
|
##
|
295
300
|
# @return [Boolean] true if resource has been destroyed
|
296
301
|
def destroyed?
|
297
|
-
times = @metagraph.query([subject_uri,
|
298
|
-
!
|
302
|
+
times = @metagraph.query([subject_uri, INVALIDATED_AT_URI, nil])
|
303
|
+
!times.empty?
|
299
304
|
end
|
300
305
|
|
301
306
|
##
|
@@ -303,13 +308,15 @@ module RDF::LDP
|
|
303
308
|
#
|
304
309
|
# @return [String] an HTTP Etag
|
305
310
|
#
|
306
|
-
# @note these etags are
|
307
|
-
#
|
311
|
+
# @note these etags are weak, but we allow clients to use them in
|
312
|
+
# `If-Match` headers, and use weak comparison. This is in conflict with
|
313
|
+
# https://tools.ietf.org/html/rfc7232#section-3.1. See:
|
314
|
+
# https://github.com/ruby-rdf/rdf-ldp/issues/68
|
308
315
|
#
|
309
316
|
# @see http://www.w3.org/TR/ldp#h-ldpr-gen-etags LDP ETag clause for GET
|
310
317
|
# @see http://www.w3.org/TR/ldp#h-ldpr-put-precond LDP ETag clause for PUT
|
311
|
-
# @see
|
312
|
-
#
|
318
|
+
# @see https://tools.ietf.org/html/rfc7232#section-2.1
|
319
|
+
# Weak vs. strong validators
|
313
320
|
def etag
|
314
321
|
return nil unless exists?
|
315
322
|
"W/\"#{last_modified.new_offset(0).iso8601(9)}\""
|
@@ -382,7 +389,7 @@ module RDF::LDP
|
|
382
389
|
##
|
383
390
|
# @return [Array<RDF::LDP::Resource>] the container for this resource
|
384
391
|
def containers
|
385
|
-
@data.query([:s,
|
392
|
+
@data.query([:s, CONTAINS_URI, subject_uri]).map do |st|
|
386
393
|
RDF::LDP::Resource.find(st.subject, @data)
|
387
394
|
end
|
388
395
|
end
|
@@ -392,11 +399,11 @@ module RDF::LDP
|
|
392
399
|
# conforming to the Rack interfare.
|
393
400
|
#
|
394
401
|
# @see http://www.rubydoc.info/github/rack/rack/master/file/SPEC#The_Body
|
395
|
-
#
|
402
|
+
# Rack body documentation
|
396
403
|
def to_response
|
397
404
|
[]
|
398
405
|
end
|
399
|
-
|
406
|
+
alias each to_response
|
400
407
|
|
401
408
|
##
|
402
409
|
# Build the response for the HTTP `method` given.
|
@@ -426,7 +433,7 @@ module RDF::LDP
|
|
426
433
|
raise Gone if destroyed?
|
427
434
|
begin
|
428
435
|
send(method.to_sym.downcase, status, headers, env)
|
429
|
-
rescue NotImplementedError
|
436
|
+
rescue NotImplementedError
|
430
437
|
raise MethodNotAllowed, method
|
431
438
|
end
|
432
439
|
end
|
@@ -436,27 +443,27 @@ module RDF::LDP
|
|
436
443
|
##
|
437
444
|
# Generate response for GET requests. Returns existing status and headers,
|
438
445
|
# with `self` as the body.
|
439
|
-
def get(status, headers,
|
446
|
+
def get(status, headers, _env)
|
440
447
|
[status, update_headers(headers), self]
|
441
448
|
end
|
442
449
|
|
443
450
|
##
|
444
451
|
# Generate response for HEAD requsets. Adds appropriate headers and returns
|
445
452
|
# an empty body.
|
446
|
-
def head(status, headers,
|
453
|
+
def head(status, headers, _env)
|
447
454
|
[status, update_headers(headers), []]
|
448
455
|
end
|
449
456
|
|
450
457
|
##
|
451
458
|
# Generate response for OPTIONS requsets. Adds appropriate headers and
|
452
459
|
# returns an empty body.
|
453
|
-
def options(status, headers,
|
460
|
+
def options(status, headers, _env)
|
454
461
|
[status, update_headers(headers), []]
|
455
462
|
end
|
456
463
|
|
457
464
|
##
|
458
465
|
# Process & generate response for DELETE requests.
|
459
|
-
def delete(
|
466
|
+
def delete(_status, headers, _env)
|
460
467
|
destroy
|
461
468
|
headers.delete('Content-Type')
|
462
469
|
[204, headers, []]
|
@@ -503,7 +510,7 @@ module RDF::LDP
|
|
503
510
|
# @return [Hash<String, String>] the updated headers
|
504
511
|
def update_headers(headers)
|
505
512
|
headers['Link'] =
|
506
|
-
([headers['Link']] + link_headers).compact.join(
|
513
|
+
([headers['Link']] + link_headers).compact.join(',')
|
507
514
|
|
508
515
|
headers['Allow'] = allowed_methods.join(', ')
|
509
516
|
headers['Accept-Post'] = accept_post if respond_to?(:post, true)
|
@@ -561,18 +568,15 @@ module RDF::LDP
|
|
561
568
|
# transactions do not support updates or pattern deletes, so we must
|
562
569
|
# ask the Repository for the current last_modified to delete the statement
|
563
570
|
# transactionally
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
modified,
|
568
|
-
graph_name: metagraph_name) if modified
|
571
|
+
transaction
|
572
|
+
.delete RDF::Statement(subject_uri, MODIFIED_URI, last_modified,
|
573
|
+
graph_name: metagraph_name) if last_modified
|
569
574
|
|
570
|
-
transaction
|
571
|
-
|
572
|
-
|
573
|
-
graph_name: metagraph_name)
|
575
|
+
transaction
|
576
|
+
.insert RDF::Statement(subject_uri, MODIFIED_URI, DateTime.now,
|
577
|
+
graph_name: metagraph_name)
|
574
578
|
else
|
575
|
-
metagraph.update([subject_uri,
|
579
|
+
metagraph.update([subject_uri, MODIFIED_URI, DateTime.now])
|
576
580
|
end
|
577
581
|
end
|
578
582
|
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module RDF::LDP
|
2
|
+
class NonRDFSource < Resource
|
3
|
+
##
|
4
|
+
# StorageAdapters bundle the logic for mapping a `NonRDFSource` to a
|
5
|
+
# specific IO stream. Implementations must conform to a minimal interface:
|
6
|
+
#
|
7
|
+
# - `#initailize` must accept a `resource` parameter. The input should be
|
8
|
+
# a `NonRDFSource` (LDP-NR).
|
9
|
+
# - `#io` must yield and return a IO object in binary mode that represents
|
10
|
+
# the current state of the LDP-NR.
|
11
|
+
# - If a block is passed to `#io`, the implementation MUST allow return a
|
12
|
+
# writable IO object and that anything written to the stream while
|
13
|
+
# yielding is synced with the source in a thread-safe manner.
|
14
|
+
# - Clients not passing a block to `#io` SHOULD call `#close` on the
|
15
|
+
# object after reading it.
|
16
|
+
# - If the `#io` object responds to `#to_path` it MUST give the location
|
17
|
+
# of a file whose contents are identical the IO object's. This supports
|
18
|
+
# Rack's response body interface.
|
19
|
+
# - `#delete` remove the contents from the corresponding storage. This MAY
|
20
|
+
# be a no-op if is undesirable or impossible to delete the contents
|
21
|
+
# from the storage medium.
|
22
|
+
#
|
23
|
+
# @see http://www.rubydoc.info/github/rack/rack/master/file/SPEC#The_Body
|
24
|
+
# for details about `#to_path` in Rack response bodies.
|
25
|
+
#
|
26
|
+
# @example reading from a `StorageAdapter`
|
27
|
+
# storage = StorageAdapter.new(an_nr_source)
|
28
|
+
# storage.io.read # => [string contents of `an_nr_source`]
|
29
|
+
#
|
30
|
+
# @example writing to a `StorageAdapter`
|
31
|
+
# storage = StorageAdapter.new(an_nr_source)
|
32
|
+
# storage.io { |io| io.write('moomin') }
|
33
|
+
#
|
34
|
+
# Beyond this interface, implementations are permitted to behave as desired.
|
35
|
+
# They may, for instance, reject undesirable content or alter the graph (or
|
36
|
+
# metagraph) of the resource. They should throw appropriate `RDF::LDP`
|
37
|
+
# errors when failing to allow the middleware to handle response codes and
|
38
|
+
# messages.
|
39
|
+
#
|
40
|
+
# The base storage adapter class provides a simple File storage
|
41
|
+
# implementation.
|
42
|
+
#
|
43
|
+
# @todo check thread saftey on write for base implementation
|
44
|
+
class FileStorageAdapter
|
45
|
+
STORAGE_PATH = '.storage'.freeze
|
46
|
+
|
47
|
+
##
|
48
|
+
# Initializes the storage adapter.
|
49
|
+
#
|
50
|
+
# @param [NonRDFSource] resource
|
51
|
+
def initialize(resource)
|
52
|
+
@resource = resource
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Gives an IO object which represents the current state of @resource.
|
57
|
+
# Opens the file for read-write (mode: r+), if it already exists;
|
58
|
+
# otherwise, creates the file and opens it for read-write (mode: w+).
|
59
|
+
#
|
60
|
+
# @yield [IO] yields a read-writable object conforming to the Ruby IO
|
61
|
+
# interface for storage. The IO object will be closed when the block
|
62
|
+
# ends.
|
63
|
+
#
|
64
|
+
# @return [IO] an object conforming to the Ruby IO interface
|
65
|
+
def io(&block)
|
66
|
+
FileUtils.mkdir_p(path_dir) unless Dir.exists?(path_dir)
|
67
|
+
FileUtils.touch(path) unless file_exists?
|
68
|
+
|
69
|
+
File.open(path, 'r+b', &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# @return [Boolean] 1 if the file has been deleted, otherwise false
|
74
|
+
def delete
|
75
|
+
return false unless File.exists?(path)
|
76
|
+
File.delete(path)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
##
|
82
|
+
# @return [Boolean]
|
83
|
+
def file_exists?
|
84
|
+
File.exists?(path)
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Build the path to the file on disk.
|
89
|
+
# @return [String]
|
90
|
+
def path
|
91
|
+
File.join(STORAGE_PATH, @resource.subject_uri.path)
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Build the path to the file's directory on disk
|
96
|
+
# @return [String]
|
97
|
+
def path_dir
|
98
|
+
File.split(path).first
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdf-ldp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Johnson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2.0
|
33
|
+
version: '2.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 2.0
|
40
|
+
version: '2.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rdf-turtle
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
47
|
+
version: '2.0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
54
|
+
version: '2.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: ld-patch
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '2.0'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '2.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: json-ld
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -363,10 +363,11 @@ files:
|
|
363
363
|
- lib/rdf/ldp/non_rdf_source.rb
|
364
364
|
- lib/rdf/ldp/rdf_source.rb
|
365
365
|
- lib/rdf/ldp/resource.rb
|
366
|
+
- lib/rdf/ldp/storage_adapters/file_storage_adapter.rb
|
366
367
|
- lib/rdf/ldp/version.rb
|
367
368
|
homepage: http://ruby-rdf.github.com/
|
368
369
|
licenses:
|
369
|
-
-
|
370
|
+
- Unlicense
|
370
371
|
metadata: {}
|
371
372
|
post_install_message:
|
372
373
|
rdoc_options: []
|
@@ -377,7 +378,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
377
378
|
requirements:
|
378
379
|
- - ">="
|
379
380
|
- !ruby/object:Gem::Version
|
380
|
-
version: 2.
|
381
|
+
version: 2.2.2
|
381
382
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
382
383
|
requirements:
|
383
384
|
- - ">="
|
@@ -390,4 +391,3 @@ signing_key:
|
|
390
391
|
specification_version: 4
|
391
392
|
summary: A suite of LDP software and middleware for RDF.rb.
|
392
393
|
test_files: []
|
393
|
-
has_rdoc: false
|