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.
@@ -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 and
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/ for the Linked Data platform specification
84
- # @see http://www.w3.org/TR/ldp/#dfn-linked-data-platform-resource for a
85
- # definition of 'Resource' in LDP
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 = LinkHeader.parse(link_header)
153
- .links.select { |link| link['rel'].downcase == 'type' }
154
- .map { |link| link.href }
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(input, content_type, &block)
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(&block)
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
- RDF::Vocab::PROV.invalidatedAtTime,
276
- DateTime.now,
277
- graph_name: metagraph_name)
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, RDF::Vocab::PROV.invalidatedAtTime, nil])
298
- !(times.empty?)
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 strong if (and only if) all software that updates
307
- # the resource also updates the ETag
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 http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3
312
- # description of strong vs. weak validators
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, RDF::Vocab::LDP.contains, subject_uri]).map do |st|
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
- # for Rack body documentation
402
+ # Rack body documentation
396
403
  def to_response
397
404
  []
398
405
  end
399
- alias_method :each, :to_response
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 => e
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, env)
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, env)
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, env)
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(status, headers, env)
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
- modified = last_modified
565
- transaction.delete RDF::Statement(subject_uri,
566
- RDF::Vocab::DC.modified,
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.insert RDF::Statement(subject_uri,
571
- RDF::Vocab::DC.modified,
572
- DateTime.now,
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, RDF::Vocab::DC.modified, DateTime.now])
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.7.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-06-26 00:00:00.000000000 Z
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.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.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: '1.1'
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: '1.1'
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
- - Public Domain
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.0.0
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