rdf-ldp 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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.