rdf-ldp 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eb5f41c7209feb6a7669bf5a39284adb6ec15339
4
- data.tar.gz: 2e5fd941cad72b6a32f2724a57210db788d56c92
3
+ metadata.gz: 54ea7ba54f7ff921a5670547db56d81fcfc686ed
4
+ data.tar.gz: 3dfdbb9baa8e06b2e8ce34ef3c5f5c99f6af3a38
5
5
  SHA512:
6
- metadata.gz: 147ea229c52f3c64eb2ba925b80b71fb3adb4bdbee938ad8df991b88e7356ed38e014866fd2e416ba209d26710a75b9ed6d55992f3c2a177111650bfa94e97a4
7
- data.tar.gz: e1104014e6961b8a10ff2d6fcc7c76b7785fa0514975d372ba74a4d44dc5d1c547336ac4bc8b8633e710b5309136e6626e17d0aa6755700e621ecf28d97824c8
6
+ metadata.gz: 0df494fa4d14ffe6ccc5533bd042b567e9d5a166e35af5b4b759af9e738b486eac010f3eee40e5e2abdd3b0bcf6087f624b6b80756a575157498b5482a0e2d97
7
+ data.tar.gz: 8d69b716de23c7cf1a08d83e2b6620770d561c3aa4bfa43bf29a4024d74b60442c7fc19d58d4c8daf0b65e2d95502789d86fec192dad677b96c5576833c3ed51
data/README.md CHANGED
@@ -53,8 +53,6 @@ Content-Length: 0
53
53
  Connection: Keep-Alive
54
54
  ```
55
55
 
56
- See
57
-
58
56
  Rack::LDP
59
57
  ==========
60
58
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.0
data/app/lamprey.rb ADDED
@@ -0,0 +1,54 @@
1
+ require 'rack/ldp'
2
+ require 'sinatra/base'
3
+
4
+ class RDF::Lamprey < Sinatra::Base
5
+
6
+ use Rack::LDP::ContentNegotiation
7
+ use Rack::LDP::Errors
8
+ use Rack::LDP::Responses
9
+ use Rack::ConditionalGet
10
+ use Rack::LDP::Requests
11
+
12
+ # Set defaults in case user has not configured values
13
+ configure do
14
+ set :repository, RDF::Repository.new
15
+ end
16
+
17
+ get '/*' do
18
+ RDF::LDP::Container.new(RDF::URI(request.url), settings.repository)
19
+ .create('', 'text/turtle') if settings.repository.empty?
20
+ RDF::LDP::Resource.find(RDF::URI(request.url), settings.repository)
21
+ end
22
+
23
+ patch '/*' do
24
+ RDF::LDP::Resource.find(RDF::URI(request.url), settings.repository)
25
+ end
26
+
27
+ post '/*' do
28
+ RDF::LDP::Resource.find(RDF::URI(request.url), settings.repository)
29
+ end
30
+
31
+ put '/*' do
32
+ begin
33
+ RDF::LDP::Resource.find(RDF::URI(request.url), settings.repository)
34
+ rescue RDF::LDP::NotFound
35
+ model = request.env.fetch('HTTP_LINK', '')
36
+ RDF::LDP::Resource.interaction_model(model)
37
+ .new(RDF::URI(request.url), settings.repository)
38
+ end
39
+ end
40
+
41
+ options '/*' do
42
+ RDF::LDP::Resource.find(RDF::URI(request.url), settings.repository)
43
+ end
44
+
45
+ head '/*' do
46
+ RDF::LDP::Resource.find(RDF::URI(request.url), settings.repository)
47
+ end
48
+
49
+ delete '/*' do
50
+ RDF::LDP::Resource.find(RDF::URI(request.url), settings.repository)
51
+ end
52
+
53
+ run! if app_file == $0
54
+ end
data/bin/lamprey CHANGED
@@ -2,8 +2,4 @@
2
2
  $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'app')))
3
3
  require 'lamprey'
4
4
 
5
- begin
6
- require 'rdf/sparql'
7
- rescue LoadError => e; end
8
-
9
5
  RDF::Lamprey.run!
data/lib/rack/ldp.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rack'
2
2
  begin
3
3
  require 'linkeddata'
4
- rescue LoadError => e
4
+ rescue LoadError
5
5
  require 'rdf/turtle'
6
6
  require 'json/ld'
7
7
  end
@@ -11,7 +11,7 @@ require 'rdf/ldp'
11
11
 
12
12
  module Rack
13
13
  ##
14
- # Provides Rack middleware for handling Linked Data Platform requirements
14
+ # Provides Rack middleware for handling Linked Data Platform requirements
15
15
  # when passed {RDF::LDP::Resource} and its subclasses as response objects.
16
16
  #
17
17
  # Response objects that are not an {RDF::LDP::Resource} are passed over
@@ -20,16 +20,17 @@ module Rack
20
20
  #
21
21
  # The suite can be mix-and-matched as needed. This allows easy swap in of
22
22
  # custom handlers for parts of the behavior. It is recommended that you use
23
- # {Rack::LDP::ContentNegotiation}, {Rack::LDP::Errors}, and
24
- # {Rack::LDP::Responses} as the outer three services. With these in place,
25
- # you can handle requests as needed in your application, giving responses
26
- # conforming to the core {RDF::LDP::Resource} interface.
23
+ # {Rack::LDP::ContentNegotiation}, {Rack::LDP::Errors}, {Rack::LDP::Responses}
24
+ # and {Rack::LDP::Reousets} as the outer middleware layers. With these in
25
+ # place, you can handle requests as needed in your application, giving
26
+ # responses conforming to the core {RDF::LDP::Resource} interface.
27
27
  #
28
28
  # @example
29
29
  # run Rack:;Builder.new do
30
30
  # use Rack::LDP::ContentNegotiation
31
31
  # use Rack::LDP::Errors
32
32
  # use Rack::LDP::Responses
33
+ # use Rack::LDP::Requests
33
34
  # # ...
34
35
  # end
35
36
  #
@@ -110,10 +111,29 @@ module Rack
110
111
 
111
112
  ##
112
113
  # Specializes {Rack::LinkedData::ContentNegotiation}, making the default
113
- # return type 'text/turtle'
114
+ # return type 'text/turtle'.
115
+ #
116
+ # @see Rack::LinkedData::ContentNegotiation}, making
114
117
  class ContentNegotiation < Rack::LinkedData::ContentNegotiation
118
+ DEFAULT_PREFIXES =
119
+ Hash[*RDF::Vocabulary.map { |v| [v.__prefix__, v.to_uri] }.flatten]
120
+ .freeze
121
+
115
122
  def initialize(app, options = {})
116
123
  options[:default] ||= 'text/turtle'
124
+ options[:prefixes] ||= DEFAULT_PREFIXES.dup
125
+ super
126
+ end
127
+
128
+ ##
129
+ # The default LinkedData Conneg doesn't support wildcard operators. We
130
+ # patch in support for 'text/*' manually, giving Turtle. This should be
131
+ # considered helpful by LDP clients.
132
+ #
133
+ # @see Rack::LinkedData::ContentNegotiation#find_writer_for_content_type
134
+ def find_writer_for_content_type(content_type)
135
+ return [RDF::Writer.for(:ttl), 'text/turtle'] if
136
+ content_type == 'text/*'
117
137
  super
118
138
  end
119
139
  end
@@ -34,35 +34,63 @@ module RDF::LDP
34
34
  end
35
35
 
36
36
  ##
37
+ # Create with validation as required for the LDP container.
38
+ #
39
+ # @raise [RDF::LDP::Conflict] if the create inserts triples that are not
40
+ # allowed by LDP for the container type
37
41
  # @see RDFSource#create
38
- def create(input, content_type)
39
- super { |statements| validate_triples!(statements) }
42
+ def create(input, content_type, &block)
43
+ super do |transaction|
44
+ validate_triples!(transaction)
45
+ yield transaction if block_given?
46
+ end
47
+ self
40
48
  end
41
49
 
42
50
  ##
51
+ # Updates with validation as required for the LDP container.
52
+ #
53
+ # @raise [RDF::LDP::Conflict] if the update edits triples that are not
54
+ # allowed by LDP for the container type
43
55
  # @see RDFSource#update
44
- def update(input, content_type)
45
- super { |statements| validate_triples!(statements) }
56
+ def update(input, content_type, &block)
57
+ super do |transaction|
58
+ validate_triples!(transaction)
59
+ yield transaction if block_given?
60
+ end
61
+ self
46
62
  end
47
63
 
48
64
  ##
49
65
  # Adds a member `resource` to the container. Handles containment and
50
66
  # membership triples as appropriate for the container type.
51
67
  #
68
+ # If a transaction is passed as the second argument, the additon of the
69
+ # containment triple is completed when the transaction closes; otherwise it is
70
+ # handled atomically.
71
+ #
52
72
  # @param [RDF::Term] a new member for this container
73
+ # @param transaction [RDF::Transaction] an active transaction as context for
74
+ # the addition
53
75
  # @return [Container] self
54
- def add(resource)
55
- add_containment_triple(resource.to_uri)
76
+ def add(resource, transaction = nil)
77
+ add_containment_triple(resource.to_uri, transaction)
56
78
  end
57
79
 
58
80
  ##
59
81
  # Removes a member `resource` from the container. Handles containment and
60
82
  # membership triples as appropriate for the container type.
61
83
  #
84
+ # If a transaction is passed as the second argument, the removal of the
85
+ # containment triple is completed when the transaction closes; otherwise it is
86
+ # handled atomically.
87
+ #
62
88
  # @param [RDF::Term] a new member for this container
89
+ # @param transaction [RDF::Transaction] an active transaction as context for
90
+ # the removal
63
91
  # @return [Container] self
64
- def remove(resource)
65
- remove_containment_triple(resource.to_uri)
92
+ def remove(resource, transaction = nil)
93
+ remove_containment_triple(resource.to_uri, transaction)
66
94
  end
67
95
 
68
96
  ##
@@ -77,39 +105,50 @@ module RDF::LDP
77
105
  # @param [RDF::Statement] statement
78
106
  #
79
107
  # @return [Boolean] true if the containment triple exists
80
- #
81
- # @todo for some reason `#include?` doesn't work! figure out why, this is
82
- # clumsy.
83
108
  def has_containment_triple?(statement)
84
- !(containment_triples.select { |t| statement == t }.empty?)
109
+ !(containment_triples.find { |t| statement == t }.nil?)
85
110
  end
86
111
 
87
112
  ##
88
113
  # Adds a containment triple for `resource` to the container's `#graph`.
89
114
  #
90
- # @param [RDF::Term] a new member for this container
115
+ # If a transaction is passed as the second argument, the triple is added to
116
+ # the transaction's inserts; otherwise it is added directly to `#graph`.
117
+ #
118
+ # @param resource [RDF::Term] a new member for this container
119
+ # @param transaction [RDF::Transaction]
91
120
  # @return [Container] self
92
- def add_containment_triple(resource)
93
- graph << make_containment_triple(resource)
121
+ def add_containment_triple(resource, transaction = nil)
122
+ target = transaction || graph
123
+ target << make_containment_triple(resource)
124
+ set_last_modified(transaction)
94
125
  self
95
126
  end
96
127
 
97
128
  ##
98
129
  # Remove a containment triple for `resource` to the container's `#graph`.
99
130
  #
100
- # @param [RDF::Term] a member to remove from this container
131
+ # If a transaction is passed as the second argument, the triple is added to
132
+ # the transaction's deletes; otherwise it is deleted directly from `#graph`.
133
+ #
134
+ # @param resource [RDF::Term] a member to remove from this container
135
+ # @param transaction [RDF::Transaction]
101
136
  # @return [Container] self
102
- def remove_containment_triple(resource)
103
- graph.delete(make_containment_triple(resource))
137
+ def remove_containment_triple(resource, transaction = nil)
138
+ target = transaction || graph
139
+ target.delete(make_containment_triple(resource))
140
+ set_last_modified(transaction)
104
141
  self
105
142
  end
106
143
 
107
144
  ##
108
- # @param [RDF::Term] a member for this container
145
+ # @param [RDF::Term] a member to be represented in the containment triple
109
146
  #
110
- # @return [RDF::URI] the containment triple
147
+ # @return [RDF::URI] the containment triple, with a graph_name pointing
148
+ # to `#graph`
111
149
  def make_containment_triple(resource)
112
- RDF::Statement(subject_uri, RDF::Vocab::LDP.contains, resource)
150
+ RDF::Statement(subject_uri, RDF::Vocab::LDP.contains, resource,
151
+ graph_name: subject_uri)
113
152
  end
114
153
 
115
154
  private
@@ -123,10 +162,11 @@ module RDF::LDP
123
162
  temp_graph = RDF::Graph.new << graph.statements
124
163
  send(method, env['rack.input'], temp_graph)
125
164
 
126
- validate_triples!(temp_graph)
165
+ validate_statements!(temp_graph)
127
166
  graph.clear!
128
167
  graph << temp_graph.statements
129
168
 
169
+ set_last_modified
130
170
  [200, update_headers(headers), self]
131
171
  end
132
172
 
@@ -149,14 +189,45 @@ module RDF::LDP
149
189
  id = (subject_uri / slug).canonicalize
150
190
 
151
191
  created = klass.new(id, @data)
152
- .create(env['rack.input'], env['CONTENT_TYPE'])
192
+
193
+ created.create(env['rack.input'], env['CONTENT_TYPE']) do |transaction|
194
+ add(created, transaction)
195
+ end
153
196
 
154
- add(created)
155
197
  headers['Location'] = created.subject_uri.to_s
156
198
  [201, created.send(:update_headers, headers), created]
157
199
  end
158
200
 
159
- def validate_triples!(statements)
201
+ def validate_triples!(transaction)
202
+ existing_triples = containment_triples.to_a
203
+
204
+ inserts = transaction.inserts.select do |st|
205
+ st.subject == subject_uri && st.predicate == RDF::Vocab::LDP.contains
206
+ end
207
+
208
+ inserts.each do |statement|
209
+ existing_triples.delete(statement) do
210
+ raise Conflict.new('Attempted to write unacceptable LDP ' \
211
+ "containment-triple: #{statement}")
212
+ end
213
+ end
214
+
215
+ deletes = transaction.deletes.select do |st|
216
+ st.subject == subject_uri &&
217
+ predicate == RDF::Vocab::LDP.contains &&
218
+ !inserts.include?(st)
219
+ end
220
+
221
+ deletes = deletes + existing_triples
222
+
223
+ raise Conflict.new('Cannot remove containment triples in updates. ' \
224
+ "Attepted to remove #{deletes}") unless
225
+ deletes.empty?
226
+ end
227
+
228
+ ##
229
+ # supports Patch.
230
+ def validate_statements!(statements)
160
231
  existing_triples = containment_triples.to_a
161
232
  statements.query(subject: subject_uri,
162
233
  predicate: RDF::Vocab::LDP.contains) do |statement|
@@ -36,11 +36,14 @@ module RDF::LDP
36
36
  # membership triple to the memebership resource.
37
37
  #
38
38
  # @see RDF::LDP::Container#add
39
- def add(resource)
40
- process_membership_resource(resource) do |membership, triple|
41
- super
42
- membership.graph << triple
39
+ def add(resource, transaction = nil)
40
+ target = transaction || graph
41
+ process_membership_resource(resource) do |membership, quad, resource|
42
+ super(resource, transaction)
43
+ target = transaction || membership.graph
44
+ target << quad
43
45
  end
46
+ self
44
47
  end
45
48
 
46
49
  ##
@@ -48,11 +51,13 @@ module RDF::LDP
48
51
  # removes membership triple to the memebership resource.
49
52
  #
50
53
  # @see RDF::LDP::Container#remove
51
- def remove(resource)
52
- process_membership_resource(resource) do |membership, triple|
53
- super
54
- membership.graph.delete(triple)
54
+ def remove(resource, transaction = nil)
55
+ process_membership_resource(resource) do |membership, quad, resource|
56
+ super(resource, transaction)
57
+ target = transaction || membership.graph
58
+ target.delete(quad)
55
59
  end
60
+ self
56
61
  end
57
62
 
58
63
  ##
@@ -142,7 +147,7 @@ module RDF::LDP
142
147
  end
143
148
 
144
149
  def process_membership_resource(resource, &block)
145
- triple = make_membership_triple(resource.to_uri)
150
+ statement = make_membership_triple(resource.to_uri)
146
151
 
147
152
  begin
148
153
  membership_rs = membership_resource
@@ -151,9 +156,8 @@ module RDF::LDP
151
156
  "#{membership_constant_uri} does not exist")
152
157
  end
153
158
 
154
- yield(membership_resource, triple, resource) if block_given?
155
-
156
- self
159
+ statement.graph_name = membership_rs.subject_uri
160
+ yield(membership_rs, statement, resource) if block_given?
157
161
  end
158
162
  end
159
163
  end
@@ -17,7 +17,7 @@ module RDF::LDP
17
17
  # a definition of NonRDFSource in LDP
18
18
  class NonRDFSource < Resource
19
19
  # Use DC elements format
20
- FORMAT_TERM = RDF::DC11.format
20
+ FORMAT_TERM = RDF::Vocab::DC11.format
21
21
  DESCRIBED_BY_TERM = RDF::URI('http://www.w3.org/2007/05/powder-s#describedby')
22
22
 
23
23
  ##
@@ -50,7 +50,7 @@ module RDF::LDP
50
50
  storage.io { |io| IO.copy_stream(input.binmode, io) }
51
51
  super
52
52
  self.content_type = c_type
53
- RDFSource.new(description_uri, @data).create('', 'text/plain')
53
+ RDFSource.new(description_uri, @data).create('', 'application/n-triples')
54
54
  self
55
55
  end
56
56
 
@@ -58,6 +58,7 @@ module RDF::LDP
58
58
  # @see RDF::LDP::Resource#update
59
59
  def update(input, c_type)
60
60
  storage.io { |io| IO.copy_stream(input.binmode, io) }
61
+ super
61
62
  self.content_type = c_type
62
63
  self
63
64
  end
@@ -72,10 +73,6 @@ module RDF::LDP
72
73
  super
73
74
  end
74
75
 
75
- def etag
76
- "#{Digest::SHA1.base64digest(storage.io.read)}"
77
- end
78
-
79
76
  ##
80
77
  # @raise [RDF::LDP::NotFound] if the describedby resource doesn't exist
81
78
  #
@@ -9,25 +9,26 @@ module RDF::LDP
9
9
  # - a `#graph` representing the "entire persistent state"
10
10
  # - a `#metagraph` containing internal properties of the RDFSource
11
11
  #
12
- # Persistence schemes must be able to reconstruct both `#graph` and
13
- # `#metagraph` accurately and separately (e.g. by saving them as distinct
14
- # named graphs). Statements in `#metagraph` are considered canonical for the
15
- # purposes of server-side operations; in the `RDF::LDP` core, this means they
16
- # determine interaction model.
12
+ # Repository implementations must be able to reconstruct both `#graph` and
13
+ # `#metagraph` accurately and separately (e.g., by saving them as distinct
14
+ # named graphs).
15
+ #
16
+ # The implementations of `#create` and `#update` in `RDF::LDP::Resource` are
17
+ # overloaded to handle the edits to `#graph` within the same transaction as
18
+ # the base `#metagraph` updates. `#to_response` is overloaded to return an
19
+ # unnamed `RDF::Graph`, to be transformed into an HTTP Body by
20
+ # `Rack::LDP::ContentNegotiation`.
17
21
  #
18
- # Note that the contents of `#metagraph`'s are *not* the same as
19
- # LDP-server-managed triples. `#metagraph` contains statements internal
20
- # properties of the RDFSource which are necessary for the server's management
21
- # purposes, but MAY be absent from the representation of its state in `#graph`.
22
+ # @note the contents of `#metagraph`'s are *not* the same as
23
+ # LDP-server-managed triples. `#metagraph` contains internal properties of the
24
+ # RDFSource which are necessary for the server's management purposes, but MAY
25
+ # be absent from (or in conflict with) the representation of its state in
26
+ # `#graph`.
22
27
  #
23
28
  # @see http://www.w3.org/TR/ldp/#dfn-linked-data-platform-rdf-source definition
24
29
  # of ldp:RDFSource in the LDP specification
25
30
  class RDFSource < Resource
26
31
 
27
- # @!attribute [rw] graph
28
- # a graph representing the current persistent state of the resource.
29
- attr_accessor :graph
30
-
31
32
  class << self
32
33
  ##
33
34
  # @return [RDF::URI] uri with lexical representation
@@ -42,22 +43,50 @@ module RDF::LDP
42
43
  ##
43
44
  # @see RDF::LDP::Resource#initialize
44
45
  def initialize(subject_uri, data = RDF::Repository.new)
45
- @graph = RDF::Graph.new(subject_uri, data: data)
46
+ @subject_uri = subject_uri
47
+ @data = data
46
48
  super
47
49
  self
48
50
  end
49
51
 
52
+ ##
53
+ # @return [RDF::Graph] a graph representing the current persistent state of
54
+ # the resource.
55
+ def graph
56
+ @graph ||= RDF::Graph.new(@subject_uri, data: @data)
57
+ end
58
+
50
59
  ##
51
60
  # Creates the RDFSource, populating its graph from the input given
52
61
  #
62
+ # @example
63
+ # repository = RDF::Repository.new
64
+ # ldprs = RDF::LDP::RDFSource.new('http://example.org/moomin', repository)
65
+ # ldprs.create('<http://ex.org/1> <http://ex.org/prop> "moomin" .', 'text/turtle')
66
+ #
53
67
  # @param [IO, File, #to_s] input input (usually from a Rack env's
54
68
  # `rack.input` key) used to determine the Resource's initial state.
55
69
  # @param [#to_s] content_type a MIME content_type used to read the graph.
56
70
  #
57
- # @yield gives the new contents of `graph` to the caller's block before
58
- # altering the state of the resource. This is useful when validation is
59
- # required or triples are to be added by a subclass.
60
- # @yieldparam [RDF::Enumerable] the contents parsed from input.
71
+ # @yield gives an in-progress transaction (changeset) to collect changes to
72
+ # graph, metagraph and other resources' (e.g. containers) graphs.
73
+ # @yieldparam tx [RDF::Transaction] a transaction targeting `#graph` as the
74
+ # default graph name
75
+ #
76
+ # @example altering changes before execution with block syntax
77
+ # content = '<http://ex.org/1> <http://ex.org/prop> "moomin" .'
78
+ #
79
+ # ldprs.create(content, 'text/turtle') do |tx|
80
+ # tx.insert([RDF::URI('s'), RDF::URI('p'), 'custom'])
81
+ # tx.insert([RDF::URI('s'), RDF::URI('p'), 'custom', RDF::URI('g')])
82
+ # end
83
+ #
84
+ # @example validating changes before execution with block syntax
85
+ # content = '<http://ex.org/1> <http://ex.org/prop> "moomin" .'
86
+ #
87
+ # ldprs.create(content, 'text/turtle') do |tx|
88
+ # raise "cannot delete triples on create!" unless tx.deletes.empty?
89
+ # end
61
90
  #
62
91
  # @raise [RDF::LDP::RequestError]
63
92
  # @raise [RDF::LDP::UnsupportedMediaType] if no reader can be found for the
@@ -68,11 +97,12 @@ module RDF::LDP
68
97
  #
69
98
  # @return [RDF::LDP::Resource] self
70
99
  def create(input, content_type, &block)
71
- super
72
- statements = parse_graph(input, content_type)
73
- yield statements if block_given?
74
- graph << statements
75
- self
100
+ super do |transaction|
101
+ transaction.graph_name = subject_uri
102
+ statements = parse_graph(input, content_type)
103
+ transaction << statements
104
+ yield transaction if block_given?
105
+ end
76
106
  end
77
107
 
78
108
  ##
@@ -84,10 +114,18 @@ module RDF::LDP
84
114
  # @param [#to_s] content_type a MIME content_type used to interpret the
85
115
  # input.
86
116
  #
87
- # @yield gives the new contents of `graph` to the caller's block before
88
- # altering the state of the resource. This is useful when validation is
89
- # required or triples are to be added by a subclass.
90
- # @yieldparam [RDF::Enumerable] the triples parsed from input.
117
+ # @yield gives an in-progress transaction (changeset) to collect changes to
118
+ # graph, metagraph and other resources' (e.g. containers) graphs.
119
+ # @yieldparam tx [RDF::Transaction] a transaction targeting `#graph` as the
120
+ # default graph name
121
+ #
122
+ # @example altering changes before execution with block syntax
123
+ # content = '<http://ex.org/1> <http://ex.org/prop> "moomin" .'
124
+ #
125
+ # ldprs.update(content, 'text/turtle') do |tx|
126
+ # tx.insert([RDF::URI('s'), RDF::URI('p'), 'custom'])
127
+ # tx.insert([RDF::URI('s'), RDF::URI('p'), 'custom', RDF::URI('g')])
128
+ # end
91
129
  #
92
130
  # @raise [RDF::LDP::RequestError]
93
131
  # @raise [RDF::LDP::UnsupportedMediaType] if no reader can be found for the
@@ -95,11 +133,13 @@ module RDF::LDP
95
133
  #
96
134
  # @return [RDF::LDP::Resource] self
97
135
  def update(input, content_type, &block)
98
- return create(input, content_type) unless exists?
99
- statements = parse_graph(input, content_type)
100
- yield statements if block_given?
101
- graph.clear!
102
- graph << statements
136
+ super do |transaction|
137
+ transaction.graph_name = subject_uri
138
+ transaction << parse_graph(input, content_type)
139
+ yield transaction if block_given?
140
+ graph.clear
141
+ end
142
+
103
143
  self
104
144
  end
105
145
 
@@ -107,42 +147,12 @@ module RDF::LDP
107
147
  # Clears the graph and marks as destroyed.
108
148
  #
109
149
  # @see RDF::LDP::Resource#destroy
110
- def destroy
111
- @graph.clear
112
- super
113
- end
114
-
115
- ##
116
- # Returns an Etag. This may be a strong or a weak ETag.
117
- #
118
- # @return [String] an HTTP Etag
119
- #
120
- # @note the current implementation is a naive one that combines a couple of
121
- # blunt heurisitics.
122
- #
123
- # @todo add an efficient hash function for RDF Graphs to RDF.rb and use that
124
- # here?
125
- #
126
- # @see http://ceur-ws.org/Vol-1259/proceedings.pdf#page=65 for a recent
127
- # treatment of digests for RDF graphs
128
- #
129
- # @see http://www.w3.org/TR/ldp#h-ldpr-gen-etags LDP ETag clause for GET
130
- # @see http://www.w3.org/TR/ldp#h-ldpr-put-precond LDP ETag clause for PUT
131
- # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3
132
- # description of strong vs. weak validators
133
- def etag
134
- subs = graph.subjects.map { |s| s.node? ? nil : s.to_s }
135
- .compact.sort.join()
136
- "\"#{Digest::SHA1.base64digest(subs)}#{graph.statements.count}\""
150
+ def destroy(&block)
151
+ super do |_|
152
+ graph.clear
153
+ end
137
154
  end
138
155
 
139
- ##
140
- # @param [String] tag a tag to compare to `#etag`
141
- # @return [Boolean] whether the given tag matches `#etag`
142
- # def match?(tag)
143
- # return false unless tag.split('==').last == graph.statements.count.to_s
144
- # end
145
-
146
156
  ##
147
157
  # @return [Boolean] whether this is an ldp:RDFSource
148
158
  def rdf_source?
@@ -161,6 +171,8 @@ module RDF::LDP
161
171
  ##
162
172
  # Process & generate response for PUT requsets.
163
173
  #
174
+ # @note patch is currently not transactional.
175
+ #
164
176
  # @raise [RDF::LDP::UnsupportedMediaType] when a media type other than
165
177
  # LDPatch is used
166
178
  # @raise [RDF::LDP::BadRequest] when an invalid document is given
@@ -171,6 +183,7 @@ module RDF::LDP
171
183
  raise UnsupportedMediaType unless method
172
184
 
173
185
  send(method, env['rack.input'], graph)
186
+ set_last_modified
174
187
  [200, update_headers(headers), self]
175
188
  end
176
189
 
@@ -182,20 +195,16 @@ module RDF::LDP
182
195
  'application/sparql-update' => :sparql_update }
183
196
  end
184
197
 
185
- def ld_patch(input, graph)
186
- begin
187
- LD::Patch.parse(input).execute(graph)
188
- rescue LD::Patch::Error => e
189
- raise BadRequest, e.message
190
- end
198
+ def ld_patch(input, graph, &block)
199
+ LD::Patch.parse(input).execute(graph)
200
+ rescue LD::Patch::Error => e
201
+ raise BadRequest, e.message
191
202
  end
192
203
 
193
204
  def sparql_update(input, graph)
194
- begin
195
- SPARQL.execute(input, graph, update: true)
196
- rescue SPARQL::MalformedQuery => e
197
- raise BadRequest, e.message
198
- end
205
+ SPARQL.execute(input, graph, update: true)
206
+ rescue SPARQL::MalformedQuery => e
207
+ raise BadRequest, e.message
199
208
  end
200
209
 
201
210
  ##
@@ -221,7 +230,6 @@ module RDF::LDP
221
230
  env.has_key?('HTTP_IF_MATCH') && !match?(env['HTTP_IF_MATCH'])
222
231
  end
223
232
 
224
-
225
233
  ##
226
234
  # Finds an {RDF::Reader} appropriate for the given content_type and attempts
227
235
  # to parse the graph string.
@@ -244,7 +252,7 @@ module RDF::LDP
244
252
  raise(RDF::LDP::UnsupportedMediaType, content_type) if reader.nil?
245
253
  input = input.read if input.respond_to? :read
246
254
  begin
247
- RDF::Graph.new << reader.new(input, base_uri: subject_uri)
255
+ RDF::Graph.new << reader.new(input, base_uri: subject_uri, validate: true)
248
256
  rescue RDF::ReaderError => e
249
257
  raise RDF::LDP::BadRequest, e.message
250
258
  end
@@ -19,14 +19,15 @@ module RDF::LDP
19
19
  # from, and may conflict with, other RDF and non-RDF information about the
20
20
  # resource (e.g. representations suitable for a response body). Metagraph
21
21
  # contains a canonical `rdf:type` statement, which specifies the resource's
22
- # interaction model. If the resource is deleted, a flag in metagraph
23
- # indicates this.
22
+ # interaction model and a (dcterms:modified) last-modified date. If the
23
+ # resource is deleted, a (prov:invalidatedAt) flag in metagraph indicates
24
+ # this.
24
25
  #
25
26
  # The contents of `#metagraph` should not be confused with LDP
26
27
  # server-managed-triples, Those triples are included in the state of the
27
28
  # resource as represented by the response body. `#metagraph` is invisible to
28
29
  # the client except where a subclass mirrors its contents in the body.
29
- #
30
+ #
30
31
  # @example creating a new Resource
31
32
  # repository = RDF::Repository.new
32
33
  # resource = RDF::LDP::Resource.new('http://example.org/moomin', repository)
@@ -36,14 +37,55 @@ module RDF::LDP
36
37
  #
37
38
  # resource.exists? # => true
38
39
  # resource.metagraph.dump :ttl
39
- # # => "<http://example.org/moomin> a <http://www.w3.org/ns/ldp#Resource> ."
40
+ # # => "<http://example.org/moomin> a <http://www.w3.org/ns/ldp#Resource>;
41
+ # <http://purl.org/dc/terms/modified> "2015-10-25T14:24:56-07:00"^^<http://www.w3.org/2001/XMLSchema#dateTime> ."
42
+ #
43
+ # @example updating a Resource updates the `#last_modified` date
44
+ # resource.last_modified
45
+ # # => #<DateTime: 2015-10-25T14:32:01-07:00 ((2457321j,77521s,571858283n),-25200s,2299161j)>
46
+ # resource.update('blah', 'text/plain')
47
+ # resource.last_modified
48
+ # # => #<DateTime: 2015-10-25T14:32:04-07:00 ((2457321j,77524s,330658065n),-25200s,2299161j)>
49
+ #
50
+ # @example destroying a Resource
51
+ # resource.exists? # => true
52
+ # resource.destroyed? # => false
53
+ #
54
+ # resource.destroy
55
+ #
56
+ # resource.exists? # => true
57
+ # resource.destroyed? # => true
58
+ #
59
+ # Rack (via `RDF::LDP::Rack`) uses the `#request` method to dispatch requests and
60
+ # interpret responses. Disallowed HTTP methods result in
61
+ # `RDF::LDP::MethodNotAllowed`. Individual Resources populate `Link`, `Allow`,
62
+ # `ETag`, `Last-Modified`, and `Accept-*` headers as required by LDP. All
63
+ # subclasses (MUST) return `self` as the Body, and respond to `#each`/
64
+ # `#respond_to` with the intended body.
65
+ #
66
+ # @example using HTTP request methods to get a Rack response
67
+ # resource.request(:get, 200, {}, {})
68
+ # # => [200,
69
+ # {"Link"=>"<http://www.w3.org/ns/ldp#Resource>;rel=\"type\"",
70
+ # "Allow"=>"GET, DELETE, OPTIONS, HEAD",
71
+ # "Accept-Post"=>"",
72
+ # "Accept-Patch"=>"",
73
+ # "ETag"=>"W/\"2015-10-25T21:39:13.111500405+00:00\"",
74
+ # "Last-Modified"=>"Sun, 25 Oct 2015 21:39:13 GMT"},
75
+ # #<RDF::LDP::Resource:0x00564f4a646028
76
+ # @data=#<RDF::Repository:0x2b27a5391708()>,
77
+ # @exists=true,
78
+ # @metagraph=#<RDF::Graph:0x2b27a5322538(http://example.org/moomin#meta)>,
79
+ # @subject_uri=#<RDF::URI:0x2b27a5322fec URI:http://example.org/moomin>>]
80
+ #
81
+ # resource.request(:put, 200, {}, {}) # RDF::LDP::MethodNotAllowed: put
40
82
  #
41
83
  # @see http://www.w3.org/TR/ldp/ for the Linked Data platform specification
42
84
  # @see http://www.w3.org/TR/ldp/#dfn-linked-data-platform-resource for a
43
85
  # definition of 'Resource' in LDP
44
86
  class Resource
45
87
  # @!attribute [r] subject_uri
46
- # an rdf term
88
+ # an rdf term identifying the `Resource`
47
89
  attr_reader :subject_uri
48
90
 
49
91
  # @!attribute [rw] metagraph
@@ -159,14 +201,23 @@ module RDF::LDP
159
201
  # input. This MAY be used as a content type for the created Resource
160
202
  # (especially for `LDP::NonRDFSource`s).
161
203
  #
204
+ # @yield gives a transaction (changeset) to collect changes to graph,
205
+ # metagraph and other resources' (e.g. containers) graphs
206
+ # @yieldparam tx [RDF::Transaction]
207
+ # @return [RDF::LDP::Resource] self
208
+ #
162
209
  # @raise [RDF::LDP::RequestError] when creation fails. May raise various
163
210
  # subclasses for the appropriate response codes.
164
211
  # @raise [RDF::LDP::Conflict] when the resource exists
165
- #
166
- # @return [RDF::LDP::Resource] self
167
- def create(input, content_type)
212
+ def create(input, content_type, &block)
168
213
  raise Conflict if exists?
169
- metagraph << RDF::Statement(subject_uri, RDF.type, self.class.to_uri)
214
+
215
+ @data.transaction do |transaction|
216
+ set_interaction_model(transaction)
217
+ yield transaction if block_given?
218
+ set_last_modified(transaction)
219
+ end
220
+
170
221
  self
171
222
  end
172
223
 
@@ -178,12 +229,20 @@ module RDF::LDP
178
229
  # @param [#to_s] content_type a MIME content_type used to interpret the
179
230
  # input.
180
231
  #
232
+ # @yield gives a transaction (changeset) to collect changes to graph,
233
+ # metagraph and other resources' (e.g. containers) graphs
234
+ # @yieldparam tx [RDF::Transaction]
235
+ # @return [RDF::LDP::Resource] self
236
+ #
181
237
  # @raise [RDF::LDP::RequestError] when update fails. May raise various
182
238
  # subclasses for the appropriate response codes.
183
- #
184
- # @return [RDF::LDP::Resource] self
185
- def update(input, content_type)
186
- raise NotImplementedError
239
+ def update(input, content_type, &block)
240
+ return create(input, content_type, &block) unless exists?
241
+ @data.transaction do |transaction|
242
+ yield transaction if block_given?
243
+ set_last_modified(transaction)
244
+ end
245
+ self
187
246
  end
188
247
 
189
248
  ##
@@ -192,30 +251,69 @@ module RDF::LDP
192
251
  # This adds a statment to the metagraph expressing that the resource has
193
252
  # been deleted
194
253
  #
254
+ # @yield gives a transaction (changeset) to collect changes to graph,
255
+ # metagraph and other resources' (e.g. containers) graphs
256
+ # @yieldparam tx [RDF::Transaction]
195
257
  # @return [RDF::LDP::Resource] self
196
258
  #
197
259
  # @todo Use of owl:Nothing is probably problematic. Define an internal
198
260
  # namespace and class represeting deletion status as a stateful property.
199
- def destroy
200
- containers.each { |con| con.remove(self) if con.container? }
201
- @metagraph << RDF::Statement(subject_uri, RDF.type, RDF::OWL.Nothing)
261
+ def destroy(&block)
262
+ @data.transaction do |transaction|
263
+ containers.each { |c| c.remove(self, transaction) if c.container? }
264
+ transaction << RDF::Statement(subject_uri,
265
+ RDF::Vocab::PROV.invalidatedAtTime,
266
+ DateTime.now,
267
+ graph_name: metagraph_name)
268
+ yield if block_given?
269
+ end
202
270
  self
203
271
  end
204
272
 
205
273
  ##
274
+ # Gives the status of the resource's existance.
275
+ #
276
+ # @note destroyed resources continue to exist in the sense represeted by
277
+ # this method.
278
+ #
206
279
  # @return [Boolean] true if the resource exists within the repository
207
280
  def exists?
208
- @data.has_context? metagraph.context
281
+ @data.has_graph? metagraph.graph_name
209
282
  end
210
283
 
211
284
  ##
212
285
  # @return [Boolean] true if resource has been destroyed
213
286
  def destroyed?
214
- !(@metagraph.query([subject_uri, RDF.type, RDF::OWL.Nothing]).empty?)
287
+ times = @metagraph.query([subject_uri, RDF::Vocab::PROV.invalidatedAtTime, nil])
288
+ !(times.empty?)
215
289
  end
216
290
 
291
+ ##
292
+ # Returns an Etag. This may be a strong or a weak ETag.
293
+ #
294
+ # @return [String] an HTTP Etag
295
+ #
296
+ # @note these etags are strong if (and only if) all software that updates
297
+ # the resource also updates the ETag
298
+ #
299
+ # @see http://www.w3.org/TR/ldp#h-ldpr-gen-etags LDP ETag clause for GET
300
+ # @see http://www.w3.org/TR/ldp#h-ldpr-put-precond LDP ETag clause for PUT
301
+ # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3
302
+ # description of strong vs. weak validators
217
303
  def etag
218
- nil
304
+ return nil unless exists?
305
+ "W/\"#{last_modified.new_offset(0).iso8601(9)}\""
306
+ end
307
+
308
+ ##
309
+ # @return [DateTime] the time this resource was last modified
310
+ #
311
+ # @todo handle cases where there is more than one RDF::DC.modified.
312
+ # check for the most recent date
313
+ def last_modified
314
+ results = @metagraph.query([subject_uri, RDF::Vocab::DC.modified, :time])
315
+ return nil if results.empty?
316
+ results.first.object.object
219
317
  end
220
318
 
221
319
  ##
@@ -310,7 +408,7 @@ module RDF::LDP
310
408
  raise Gone if destroyed?
311
409
  begin
312
410
  send(method.to_sym.downcase, status, headers, env)
313
- rescue NotImplementedError, NoMethodError => e
411
+ rescue NotImplementedError => e
314
412
  raise MethodNotAllowed, method
315
413
  end
316
414
  end
@@ -344,6 +442,36 @@ module RDF::LDP
344
442
  [204, headers, destroy]
345
443
  end
346
444
 
445
+ ##
446
+ # @abstract implement in subclasses as needed to support HTTP PATCH
447
+ def patch(*)
448
+ raise NotImplementedError
449
+ end
450
+
451
+ ##
452
+ # @abstract implement in subclasses as needed to support HTTP POST
453
+ def post(*)
454
+ raise NotImplementedError
455
+ end
456
+
457
+ ##
458
+ # @abstract implement in subclasses as needed to support HTTP PUT
459
+ def put(*)
460
+ raise NotImplementedError
461
+ end
462
+
463
+ ##
464
+ # @abstract HTTP TRACE is not expected to be supported
465
+ def trace(*)
466
+ raise NotImplementedError
467
+ end
468
+
469
+ ##
470
+ # @abstract HTTP CONNECT is not expected to be supported
471
+ def connect(*)
472
+ raise NotImplementedError
473
+ end
474
+
347
475
  ##
348
476
  # @return [RDF::URI] the name for this resource's metagraph
349
477
  def metagraph_name
@@ -361,14 +489,19 @@ module RDF::LDP
361
489
  headers['Accept-Post'] = accept_post if respond_to?(:post, true)
362
490
  headers['Accept-Patch'] = accept_patch if respond_to?(:patch, true)
363
491
 
364
- headers['ETag'] ||= etag if etag
492
+ tag = etag
493
+ headers['ETag'] ||= tag if tag
494
+
495
+ modified = last_modified
496
+ headers['Last-Modified'] ||= modified.httpdate if modified
497
+
365
498
  headers
366
499
  end
367
500
 
368
501
  ##
369
502
  # @return [String] the Accept-Post headers
370
503
  def accept_post
371
- RDF::Reader.map { |klass| klass.format.content_type }.flatten.join(', ')
504
+ RDF::Reader.map(&:format).compact.map(&:content_type).flatten.join(', ')
372
505
  end
373
506
 
374
507
  ##
@@ -400,5 +533,36 @@ module RDF::LDP
400
533
  def link_type_header(uri)
401
534
  "<#{uri}>;rel=\"type\""
402
535
  end
536
+
537
+ ##
538
+ # Sets the last modified date/time to now
539
+ def set_last_modified(transaction = nil)
540
+ if transaction
541
+ # transactions do not support updates or pattern deletes, so we must
542
+ # ask the Repository for the current last_modified to delete the statement
543
+ # transactionally
544
+ modified = last_modified
545
+ transaction.delete RDF::Statement(subject_uri,
546
+ RDF::Vocab::DC.modified,
547
+ modified,
548
+ graph_name: metagraph_name) if modified
549
+
550
+ transaction.insert RDF::Statement(subject_uri,
551
+ RDF::Vocab::DC.modified,
552
+ DateTime.now,
553
+ graph_name: metagraph_name)
554
+ else
555
+ metagraph.update([subject_uri, RDF::Vocab::DC.modified, DateTime.now])
556
+ end
557
+ end
558
+
559
+ ##
560
+ # Sets the last modified date/time to the URI for this resource's class
561
+ def set_interaction_model(transaction)
562
+ transaction.insert(RDF::Statement(subject_uri,
563
+ RDF.type,
564
+ self.class.to_uri,
565
+ graph_name: metagraph.graph_name))
566
+ end
403
567
  end
404
568
  end
metadata CHANGED
@@ -1,87 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdf-ldp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.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: 2015-08-30 00:00:00.000000000 Z
11
+ date: 2016-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '1.6'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '1.6'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rdf
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.1'
33
+ version: '1.99'
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: '1.1'
40
+ version: '1.99'
41
41
  - !ruby/object:Gem::Dependency
42
- name: ld-patch
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.1'
47
+ version: '1.1'
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 1.1.8
48
51
  type: :runtime
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
52
55
  - - "~>"
53
56
  - !ruby/object:Gem::Version
54
- version: '0.1'
57
+ version: '1.1'
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.1.8
55
61
  - !ruby/object:Gem::Dependency
56
- name: rdf-vocab
62
+ name: ld-patch
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
- - - ">="
65
+ - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: '0'
67
+ version: '0.1'
62
68
  type: :runtime
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
- - - ">="
72
+ - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: '0'
74
+ version: '0.1'
69
75
  - !ruby/object:Gem::Dependency
70
- name: rack-linkeddata
76
+ name: rdf-vocab
71
77
  requirement: !ruby/object:Gem::Requirement
72
78
  requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.8'
73
82
  - - ">="
74
83
  - !ruby/object:Gem::Version
75
- version: '0'
84
+ version: 0.8.4
76
85
  type: :runtime
77
86
  prerelease: false
78
87
  version_requirements: !ruby/object:Gem::Requirement
79
88
  requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '0.8'
80
92
  - - ">="
81
93
  - !ruby/object:Gem::Version
82
- version: '0'
94
+ version: 0.8.4
83
95
  - !ruby/object:Gem::Dependency
84
- name: rdf-turtle
96
+ name: rack-linkeddata
85
97
  requirement: !ruby/object:Gem::Requirement
86
98
  requirements:
87
99
  - - "~>"
@@ -98,44 +110,50 @@ dependencies:
98
110
  name: json-ld
99
111
  requirement: !ruby/object:Gem::Requirement
100
112
  requirements:
101
- - - ">="
113
+ - - "~>"
102
114
  - !ruby/object:Gem::Version
103
- version: '0'
115
+ version: '1.1'
104
116
  type: :runtime
105
117
  prerelease: false
106
118
  version_requirements: !ruby/object:Gem::Requirement
107
119
  requirements:
108
- - - ">="
120
+ - - "~>"
109
121
  - !ruby/object:Gem::Version
110
- version: '0'
122
+ version: '1.1'
111
123
  - !ruby/object:Gem::Dependency
112
124
  name: sinatra
113
125
  requirement: !ruby/object:Gem::Requirement
114
126
  requirements:
115
- - - ">="
127
+ - - "~>"
116
128
  - !ruby/object:Gem::Version
117
- version: '0'
129
+ version: '1.4'
118
130
  type: :runtime
119
131
  prerelease: false
120
132
  version_requirements: !ruby/object:Gem::Requirement
121
133
  requirements:
122
- - - ">="
134
+ - - "~>"
123
135
  - !ruby/object:Gem::Version
124
- version: '0'
136
+ version: '1.4'
125
137
  - !ruby/object:Gem::Dependency
126
138
  name: link_header
127
139
  requirement: !ruby/object:Gem::Requirement
128
140
  requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '0.0'
129
144
  - - ">="
130
145
  - !ruby/object:Gem::Version
131
- version: '0'
146
+ version: 0.0.8
132
147
  type: :runtime
133
148
  prerelease: false
134
149
  version_requirements: !ruby/object:Gem::Requirement
135
150
  requirements:
151
+ - - "~>"
152
+ - !ruby/object:Gem::Version
153
+ version: '0.0'
136
154
  - - ">="
137
155
  - !ruby/object:Gem::Version
138
- version: '0'
156
+ version: 0.0.8
139
157
  - !ruby/object:Gem::Dependency
140
158
  name: rdf-spec
141
159
  requirement: !ruby/object:Gem::Requirement
@@ -230,16 +248,16 @@ dependencies:
230
248
  name: rack-test
231
249
  requirement: !ruby/object:Gem::Requirement
232
250
  requirements:
233
- - - ">="
251
+ - - "~>"
234
252
  - !ruby/object:Gem::Version
235
- version: '0'
253
+ version: '0.6'
236
254
  type: :development
237
255
  prerelease: false
238
256
  version_requirements: !ruby/object:Gem::Requirement
239
257
  requirements:
240
- - - ">="
258
+ - - "~>"
241
259
  - !ruby/object:Gem::Version
242
- version: '0'
260
+ version: '0.6'
243
261
  - !ruby/object:Gem::Dependency
244
262
  name: rspec-its
245
263
  requirement: !ruby/object:Gem::Requirement
@@ -254,6 +272,20 @@ dependencies:
254
272
  - - "~>"
255
273
  - !ruby/object:Gem::Version
256
274
  version: '1.0'
275
+ - !ruby/object:Gem::Dependency
276
+ name: timecop
277
+ requirement: !ruby/object:Gem::Requirement
278
+ requirements:
279
+ - - "~>"
280
+ - !ruby/object:Gem::Version
281
+ version: '0.8'
282
+ type: :development
283
+ prerelease: false
284
+ version_requirements: !ruby/object:Gem::Requirement
285
+ requirements:
286
+ - - "~>"
287
+ - !ruby/object:Gem::Version
288
+ version: '0.8'
257
289
  - !ruby/object:Gem::Dependency
258
290
  name: webmock
259
291
  requirement: !ruby/object:Gem::Requirement
@@ -297,6 +329,7 @@ files:
297
329
  - README.md
298
330
  - UNLICENSE
299
331
  - VERSION
332
+ - app/lamprey.rb
300
333
  - bin/lamprey
301
334
  - lib/rack/ldp.rb
302
335
  - lib/rdf/ldp.rb
@@ -328,7 +361,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
328
361
  version: '0'
329
362
  requirements: []
330
363
  rubyforge_project:
331
- rubygems_version: 2.2.1
364
+ rubygems_version: 2.2.0
332
365
  signing_key:
333
366
  specification_version: 4
334
367
  summary: A suite of LDP software and middleware for RDF.rb.