rdf-ldp 0.7.0 → 0.8.0
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.
- 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
|