paul-resourceful 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/Manifest +30 -0
  2. data/Rakefile +44 -14
  3. data/lib/resourceful.rb +11 -21
  4. data/lib/resourceful/authentication_manager.rb +3 -2
  5. data/lib/resourceful/cache_manager.rb +58 -1
  6. data/lib/resourceful/exceptions.rb +34 -0
  7. data/lib/resourceful/header.rb +95 -0
  8. data/lib/resourceful/http_accessor.rb +0 -2
  9. data/lib/resourceful/memcache_cache_manager.rb +3 -13
  10. data/lib/resourceful/net_http_adapter.rb +15 -5
  11. data/lib/resourceful/request.rb +180 -18
  12. data/lib/resourceful/resource.rb +38 -141
  13. data/lib/resourceful/response.rb +142 -95
  14. data/resourceful.gemspec +9 -7
  15. data/spec/acceptance/authorization_spec.rb +16 -0
  16. data/spec/acceptance/caching_spec.rb +191 -0
  17. data/spec/acceptance/header_spec.rb +24 -0
  18. data/spec/acceptance/redirecting_spec.rb +12 -0
  19. data/spec/acceptance/resource_spec.rb +84 -0
  20. data/spec/acceptance_shared_specs.rb +12 -17
  21. data/spec/{acceptance_spec.rb → old_acceptance_specs.rb} +27 -57
  22. data/spec/simple_sinatra_server.rb +73 -0
  23. data/spec/simple_sinatra_server_spec.rb +98 -0
  24. data/spec/spec_helper.rb +21 -7
  25. metadata +55 -42
  26. data/spec/resourceful/authentication_manager_spec.rb +0 -249
  27. data/spec/resourceful/cache_manager_spec.rb +0 -223
  28. data/spec/resourceful/header_spec.rb +0 -38
  29. data/spec/resourceful/http_accessor_spec.rb +0 -164
  30. data/spec/resourceful/memcache_cache_manager_spec.rb +0 -111
  31. data/spec/resourceful/net_http_adapter_spec.rb +0 -96
  32. data/spec/resourceful/options_interpreter_spec.rb +0 -102
  33. data/spec/resourceful/request_spec.rb +0 -186
  34. data/spec/resourceful/resource_spec.rb +0 -600
  35. data/spec/resourceful/response_spec.rb +0 -238
  36. data/spec/resourceful/stubbed_resource_proxy_spec.rb +0 -58
  37. data/spec/simple_http_server_shared_spec.rb +0 -162
  38. data/spec/simple_http_server_shared_spec_spec.rb +0 -212
@@ -5,13 +5,10 @@ require 'facets/kernel/ergo'
5
5
  require 'zlib'
6
6
 
7
7
  module Resourceful
8
- # Exception indicating that the server used a content coding scheme
9
- # that Resourceful is unable to handle.
10
- class UnsupportedContentCoding < Exception
11
- end
12
8
 
13
9
  class Response
14
10
  REDIRECT_RESPONSE_CODES = [301,302,303,307]
11
+ NORMALLY_CACHEABLE_RESPONSE_CODES = [200, 203, 300, 301, 410]
15
12
 
16
13
  attr_reader :uri, :code, :header, :body, :response_time
17
14
  alias headers header
@@ -24,98 +21,15 @@ module Resourceful
24
21
  @response_time = Time.now
25
22
  end
26
23
 
27
- # Is the response code sucessful? True for only 2xx series
28
- # response codes.
29
- #
30
- # @return true|false
31
- def is_success?
32
- @code.in? 200..299
33
- end
34
- alias was_successful? is_success?
35
-
36
- # Is the response the result of a server error? True for
37
- # 5xx series response codes
38
- #
39
- # @return true|false
40
- def is_server_error?
41
- @code.in? 500..599
42
- end
43
- alias was_server_error? is_server_error?
44
-
45
- # Is the response the result of a client error? True for
46
- # 4xx series response codes
47
- #
48
- # @return true|false
49
- def is_client_error?
50
- @code.in? 400..499
51
- end
52
- alias was_client_error? is_client_error?
53
-
54
- # Is the response the result of any kind of error? True for
55
- # 4xx and 5xx series response codes
56
- #
57
- # @return true|false
58
- def is_error?
59
- is_server_error? || is_client_error?
60
- end
61
- alias was_error? is_error?
62
-
63
- # Is the response not a success? True for
64
- # 3xx, 4xx and 5xx series response codes
65
- #
66
- # @return true|false
67
- def is_unsuccesful?
68
- is_error? || is_redirect?
69
- end
70
- alias was_unsuccessful? is_unsuccesful?
71
-
72
- # Is the response a redirect response code? True for
73
- # 3xx codes that are redirects (301, 302, 303, 307)
74
- #
75
- # @return true|false
76
- def is_redirect?
77
- @code.in? REDIRECT_RESPONSE_CODES
78
- end
79
- alias was_redirect? is_redirect?
80
-
81
- # Is the response a Permanent Redirect (301) ?
82
- #
83
- # @return true|false
84
- def is_permanent_redirect?
85
- @code == 301
86
- end
87
-
88
- # Is the response a Temporary Redirect (anything but 301) ?
89
- #
90
- # @return true|false
91
- def is_temporary_redirect?
92
- is_redirect? and not is_permanent_redirect?
93
- end
94
-
95
- # Is the response a client error of Not Authorized (401) ?
96
- #
97
- # @return true|false
98
- def is_not_authorized?
99
- @code == 401
100
- end
101
-
102
- # Is the response not modified (304) ?
103
- #
104
- # @return true|false
105
- def is_not_modified?
106
- @code == 304
107
- end
108
-
109
24
  # Is this a cached response that has expired?
110
25
  #
111
26
  # @return true|false
112
27
  def expired?
113
- if header['Expire']
114
- return true if Time.httpdate(header['Expire'].first) < Time.now
115
- end
116
28
  if header['Cache-Control'] and header['Cache-Control'].first.include?('max-age')
117
29
  max_age = header['Cache-Control'].first.split(',').grep(/max-age/).first.split('=').last.to_i
118
30
  return true if current_age > max_age
31
+ elsif header['Expire']
32
+ return true if Time.httpdate(header['Expire'].first) < Time.now
119
33
  end
120
34
 
121
35
  false
@@ -137,21 +51,37 @@ module Resourceful
137
51
  # Is this response cachable?
138
52
  #
139
53
  # @return true|false
140
- def cachable?
141
- return false if header['Vary'] and header['Vary'].include?('*')
142
- return false if header['Cache-Control'] and header['Cache-Control'].include?('no-store')
54
+ def cacheable?
55
+ @cacheable ||= begin
56
+ @cacheable = true if NORMALLY_CACHEABLE_RESPONSE_CODES.include?(code.to_i)
57
+ @cacheable = false if header.vary && header.vary.include?('*')
58
+ @cacheable = false if header.cache_control && header.cache_control.include?('no-cache')
59
+ @cacheable = true if header.cache_control && header.cache_control.include?('public')
60
+ @cacheable = true if header.cache_control && header.cache_control.include?('private')
61
+ @cacheable || false
62
+ end
63
+ end
143
64
 
144
- true
65
+ # Does this response force revalidation?
66
+ def must_be_revalidated?
67
+ header.cache_control && header.cache_control.include?('must-revalidate')
68
+ end
69
+
70
+ # Update our headers from a later 304 response
71
+ def revalidate!(not_modified_response)
72
+ header.merge!(not_modified_response.header)
73
+ request_time = not_modified_response.request_time
74
+ @authoritative = true
145
75
  end
146
76
 
147
77
  # Algorithm taken from RCF2616#13.2.3
148
78
  def current_age
149
- age_value = Time.httpdate(header['Age'].first) if header['Age']
79
+ age_value = header['Age'] ? header['Age'].first.to_i : 0
150
80
  date_value = Time.httpdate(header['Date'].first)
151
81
  now = Time.now
152
82
 
153
83
  apparent_age = [0, response_time - date_value].max
154
- corrected_received_age = [apparent_age, age_value || 0].max
84
+ corrected_received_age = [apparent_age, age_value].max
155
85
  current_age = corrected_received_age + (response_time - request_time) + (now - response_time)
156
86
  end
157
87
 
@@ -170,6 +100,123 @@ module Resourceful
170
100
  raise UnsupportedContentCoding, "Resourceful does not support #{header['Content-Encoding'].ergo.first} content coding"
171
101
  end
172
102
  end
103
+
104
+ CODE_NAMES = {
105
+ 100 => "Continue".freeze,
106
+ 101 => "Switching Protocols".freeze,
107
+
108
+ 200 => "OK".freeze,
109
+ 201 => "Created".freeze,
110
+ 202 => "Accepted".freeze,
111
+ 203 => "Non-Authoritative Information".freeze,
112
+ 204 => "No Content".freeze,
113
+ 205 => "Reset Content".freeze,
114
+ 206 => "Partial Content".freeze,
115
+
116
+ 300 => "Multiple Choices".freeze,
117
+ 301 => "Moved Permanently".freeze,
118
+ 302 => "Found".freeze,
119
+ 303 => "See Other".freeze,
120
+ 304 => "Not Modified".freeze,
121
+ 305 => "Use Proxy".freeze,
122
+ 307 => "Temporary Redirect".freeze,
123
+
124
+ 400 => "Bad Request".freeze,
125
+ 401 => "Unauthorized".freeze,
126
+ 402 => "Payment Required".freeze,
127
+ 403 => "Forbidden".freeze,
128
+ 404 => "Not Found".freeze,
129
+ 405 => "Method Not Allowed".freeze,
130
+ 406 => "Not Acceptable".freeze,
131
+ 407 => "Proxy Authentication Required".freeze,
132
+ 408 => "Request Timeout".freeze,
133
+ 409 => "Conflict".freeze,
134
+ 410 => "Gone".freeze,
135
+ 411 => "Length Required".freeze,
136
+ 412 => "Precondition Failed".freeze,
137
+ 413 => "Request Entity Too Large".freeze,
138
+ 414 => "Request-URI Too Long".freeze,
139
+ 415 => "Unsupported Media Type".freeze,
140
+ 416 => "Requested Range Not Satisfiable".freeze,
141
+ 417 => "Expectation Failed".freeze,
142
+
143
+ 500 => "Internal Server Error".freeze,
144
+ 501 => "Not Implemented".freeze,
145
+ 502 => "Bad Gateway".freeze,
146
+ 503 => "Service Unavailable".freeze,
147
+ 504 => "Gateway Timeout".freeze,
148
+ 505 => "HTTP Version Not Supported".freeze,
149
+ }.freeze
150
+
151
+ CODES = CODE_NAMES.keys
152
+
153
+ CODE_NAMES.each do |code, msg|
154
+ method_name = msg.downcase.gsub(/[- ]/, "_")
155
+
156
+ class_eval <<-RUBY
157
+ def #{method_name}? # def ok?
158
+ @code == #{code} # @code == 200
159
+ end # end
160
+ RUBY
161
+ end
162
+
163
+ # Is the response informational? True for
164
+ # 1xx series response codes
165
+ #
166
+ # @return true|false
167
+ def informational?
168
+ @code.in? 100..199
169
+ end
170
+
171
+ # Is the response code sucessful? True for only 2xx series
172
+ # response codes.
173
+ #
174
+ # @return true|false
175
+ def successful?
176
+ @code.in? 200..299
177
+ end
178
+ alias success? successful?
179
+
180
+ # Is the response a redirect? True for
181
+ # 3xx series response codes
182
+ #
183
+ # @return true|false
184
+ def redirection?
185
+ @code.in? 300..399
186
+ end
187
+
188
+ # Is the response a actual redirect? True for
189
+ # 301, 302, 303, 307 response codes
190
+ #
191
+ # @return true|false
192
+ def redirect?
193
+ @code.in? REDIRECT_RESPONSE_CODES
194
+ end
195
+
196
+ # Is the response the result of a client error? True for
197
+ # 4xx series response codes
198
+ #
199
+ # @return true|false
200
+ def client_error?
201
+ @code.in? 400..499
202
+ end
203
+
204
+ # Is the response the result of a server error? True for
205
+ # 5xx series response codes
206
+ #
207
+ # @return true|false
208
+ def server_error?
209
+ @code.in? 500..599
210
+ end
211
+
212
+ # Is the response the result of any kind of error? True for
213
+ # 4xx and 5xx series response codes
214
+ #
215
+ # @return true|false
216
+ def error?
217
+ server_error? || client_error?
218
+ end
219
+
173
220
  end
174
221
 
175
222
  end
data/resourceful.gemspec CHANGED
@@ -2,26 +2,25 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{resourceful}
5
- s.version = "0.3.1"
5
+ s.version = "0.5.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Paul Sadauskas"]
9
- s.date = %q{2008-12-05}
9
+ s.date = %q{2009-05-18}
10
10
  s.description = %q{An HTTP library for Ruby that takes advantage of everything HTTP has to offer.}
11
11
  s.email = %q{psadauskas@gmail.com}
12
- s.extra_rdoc_files = ["lib/resourceful.rb", "lib/resourceful/authentication_manager.rb", "lib/resourceful/util.rb", "lib/resourceful/resource.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/header.rb", "lib/resourceful/cache_manager.rb", "lib/resourceful/options_interpreter.rb", "lib/resourceful/response.rb", "lib/resourceful/request.rb", "README.markdown"]
13
- s.files = ["lib/resourceful.rb", "lib/resourceful/authentication_manager.rb", "lib/resourceful/util.rb", "lib/resourceful/resource.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/header.rb", "lib/resourceful/cache_manager.rb", "lib/resourceful/options_interpreter.rb", "lib/resourceful/response.rb", "lib/resourceful/request.rb", "spec/acceptance_shared_specs.rb", "spec/spec.opts", "spec/acceptance_spec.rb", "spec/simple_http_server_shared_spec_spec.rb", "spec/spec_helper.rb", "spec/resourceful/header_spec.rb", "spec/resourceful/authentication_manager_spec.rb", "spec/resourceful/memcache_cache_manager_spec.rb", "spec/resourceful/response_spec.rb", "spec/resourceful/options_interpreter_spec.rb", "spec/resourceful/http_accessor_spec.rb", "spec/resourceful/stubbed_resource_proxy_spec.rb", "spec/resourceful/request_spec.rb", "spec/resourceful/resource_spec.rb", "spec/resourceful/cache_manager_spec.rb", "spec/resourceful/net_http_adapter_spec.rb", "spec/simple_http_server_shared_spec.rb", "Manifest", "Rakefile", "README.markdown", "MIT-LICENSE", "resourceful.gemspec"]
14
- s.has_rdoc = true
12
+ s.extra_rdoc_files = ["README.markdown", "lib/resourceful/cache_manager.rb", "lib/resourceful/exceptions.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/header.rb", "lib/resourceful/authentication_manager.rb", "lib/resourceful/request.rb", "lib/resourceful/resource.rb", "lib/resourceful/response.rb", "lib/resourceful/util.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/options_interpreter.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful.rb"]
13
+ s.files = ["Manifest", "spec/spec.opts", "spec/simple_sinatra_server_spec.rb", "spec/simple_sinatra_server.rb", "spec/acceptance/header_spec.rb", "spec/acceptance/caching_spec.rb", "spec/acceptance/resource_spec.rb", "spec/acceptance/redirecting_spec.rb", "spec/acceptance/authorization_spec.rb", "spec/acceptance_shared_specs.rb", "spec/spec_helper.rb", "spec/old_acceptance_specs.rb", "README.markdown", "resourceful.gemspec", "lib/resourceful/cache_manager.rb", "lib/resourceful/exceptions.rb", "lib/resourceful/net_http_adapter.rb", "lib/resourceful/stubbed_resource_proxy.rb", "lib/resourceful/header.rb", "lib/resourceful/authentication_manager.rb", "lib/resourceful/request.rb", "lib/resourceful/resource.rb", "lib/resourceful/response.rb", "lib/resourceful/util.rb", "lib/resourceful/http_accessor.rb", "lib/resourceful/options_interpreter.rb", "lib/resourceful/memcache_cache_manager.rb", "lib/resourceful.rb", "Rakefile", "MIT-LICENSE"]
15
14
  s.homepage = %q{http://github.com/paul/resourceful}
16
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Resourceful", "--main", "README.markdown"]
17
16
  s.require_paths = ["lib"]
18
17
  s.rubyforge_project = %q{resourceful}
19
- s.rubygems_version = %q{1.3.1}
18
+ s.rubygems_version = %q{1.3.3}
20
19
  s.summary = %q{An HTTP library for Ruby that takes advantage of everything HTTP has to offer.}
21
20
 
22
21
  if s.respond_to? :specification_version then
23
22
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
- s.specification_version = 2
23
+ s.specification_version = 3
25
24
 
26
25
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
26
  s.add_runtime_dependency(%q<addressable>, [">= 0"])
@@ -31,6 +30,7 @@ Gem::Specification.new do |s|
31
30
  s.add_runtime_dependency(%q<andand>, [">= 0"])
32
31
  s.add_development_dependency(%q<thin>, [">= 0"])
33
32
  s.add_development_dependency(%q<yard>, [">= 0"])
33
+ s.add_development_dependency(%q<sinatra>, [">= 0"])
34
34
  else
35
35
  s.add_dependency(%q<addressable>, [">= 0"])
36
36
  s.add_dependency(%q<httpauth>, [">= 0"])
@@ -39,6 +39,7 @@ Gem::Specification.new do |s|
39
39
  s.add_dependency(%q<andand>, [">= 0"])
40
40
  s.add_dependency(%q<thin>, [">= 0"])
41
41
  s.add_dependency(%q<yard>, [">= 0"])
42
+ s.add_dependency(%q<sinatra>, [">= 0"])
42
43
  end
43
44
  else
44
45
  s.add_dependency(%q<addressable>, [">= 0"])
@@ -48,5 +49,6 @@ Gem::Specification.new do |s|
48
49
  s.add_dependency(%q<andand>, [">= 0"])
49
50
  s.add_dependency(%q<thin>, [">= 0"])
50
51
  s.add_dependency(%q<yard>, [">= 0"])
52
+ s.add_dependency(%q<sinatra>, [">= 0"])
51
53
  end
52
54
  end
@@ -0,0 +1,16 @@
1
+
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+ require 'resourceful'
4
+
5
+ describe Resourceful do
6
+
7
+ describe "basic auth" do
8
+
9
+ end
10
+
11
+ describe "digest auth" do
12
+
13
+ end
14
+
15
+ end
16
+
@@ -0,0 +1,191 @@
1
+
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+ require 'resourceful'
4
+
5
+ describe Resourceful do
6
+
7
+ describe "caching" do
8
+
9
+ before do
10
+ @http = Resourceful::HttpAccessor.new(:cache_manager => Resourceful::InMemoryCacheManager.new)
11
+ if ENV['SPEC_LOGGING']
12
+ @http.logger = Resourceful::StdOutLogger.new
13
+ end
14
+ end
15
+
16
+ def get_with_errors(resource)
17
+ begin
18
+ resp = resource.get
19
+ rescue Resourceful::UnsuccessfulHttpRequestError => e
20
+ resp = e.http_response
21
+ end
22
+ resp
23
+ end
24
+
25
+ def uri_plus_params(uri, params = {})
26
+ uri = uri.is_a?(Addressable::URI) ? uri : Addressable::URI.parse(uri)
27
+ uri.query_values = params
28
+ uri
29
+ end
30
+
31
+ def uri_for_code(code, params = {})
32
+ uri = Addressable::URI.expand_template("http://localhost:3000/code/{code}",
33
+ "code" => code.to_s)
34
+
35
+ uri_plus_params(uri, params)
36
+ end
37
+
38
+ describe "response cacheability" do
39
+ Resourceful::Response::NORMALLY_CACHEABLE_RESPONSE_CODES.each do |code|
40
+ describe "response code #{code}" do
41
+ it "should normally be cached" do
42
+ resource = @http.resource(uri_for_code(code))
43
+
44
+ resp = get_with_errors(resource)
45
+ resp.should be_cacheable
46
+ end
47
+
48
+ it "should not be cached if Vary: *" do
49
+ resource = @http.resource(uri_for_code(200, "Vary" => "*"))
50
+
51
+ resp = get_with_errors(resource)
52
+ resp.should_not be_cacheable
53
+ end
54
+
55
+ it "should not be cached if Cache-Control: no-cache'" do
56
+ resource = @http.resource(uri_for_code(200, "Cache-Control" => "no-cache"))
57
+
58
+ resp = get_with_errors(resource)
59
+ resp.should_not be_cacheable
60
+ end
61
+ end
62
+ end
63
+
64
+ # I would prefer to do all other codes, but some of them do some magic stuff (100),
65
+ # so I'll just spot check.
66
+ [201, 206, 302, 307, 404, 500].each do |code|
67
+ describe "response code #{code}" do
68
+ it "should not normally be cached" do
69
+ resource = @http.resource(uri_for_code(code))
70
+
71
+ resp = get_with_errors(resource)
72
+ resp.should_not be_cacheable
73
+ end
74
+
75
+ it "should be cached if Cache-Control: public" do
76
+ resource = @http.resource(uri_for_code(code, "Cache-Control" => "public"))
77
+
78
+ resp = get_with_errors(resource)
79
+ resp.should be_cacheable
80
+ end
81
+
82
+ it "should be cached if Cache-Control: private" do
83
+ resource = @http.resource(uri_for_code(code, "Cache-Control" => "private"))
84
+
85
+ resp = get_with_errors(resource)
86
+ resp.should be_cacheable
87
+ end
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ describe "expiration" do
94
+ it 'should use the cached response if Expire: is in the future' do
95
+ in_the_future = (Time.now + 60).httpdate
96
+ resource = @http.resource(uri_for_code(200, "Expire" => in_the_future))
97
+
98
+ resp = resource.get
99
+ resp.should_not be_expired
100
+
101
+ resp = resource.get
102
+ resp.should be_ok
103
+ resp.should_not be_authoritative
104
+ end
105
+
106
+ it 'should revalidate the cached response if the response is expired' do
107
+ in_the_past = (Time.now - 60).httpdate
108
+ resource = @http.resource(uri_for_code(200, "Expire" => in_the_past))
109
+
110
+ resp = resource.get
111
+ resp.should be_expired
112
+
113
+ resp = resource.get
114
+ resp.should be_ok
115
+ resp.should be_authoritative
116
+ end
117
+ end
118
+
119
+ describe 'authoritative' do
120
+
121
+ it "should be authoritative if the response is directly from the server" do
122
+ resource = @http.resource(
123
+ uri_plus_params('http://localhost:3000/', "Cache-Control" => 'max-age=10')
124
+ )
125
+
126
+ response = resource.get
127
+ response.should be_authoritative
128
+ end
129
+
130
+ it "should be authoritative if a cached response was revalidated with the server" do
131
+ now = Time.now.httpdate
132
+ resource = @http.resource(
133
+ uri_plus_params('http://localhost:3000/cached',
134
+ "modified" => now,
135
+ "Cache-Control" => 'max-age=0')
136
+ )
137
+
138
+ resource.get
139
+ response = resource.get("Cache-Control" => "max-age=0")
140
+ response.should be_authoritative
141
+ end
142
+
143
+ it "should not be authoritative if the cached response was not revalidated" do
144
+ now = Time.now.httpdate
145
+ resource = @http.resource(
146
+ uri_plus_params('http://localhost:3000/cached',
147
+ "modified" => now,
148
+ "Cache-Control" => 'max-age=10')
149
+ )
150
+
151
+ resource.get
152
+ response = resource.get
153
+ response.should_not be_authoritative
154
+
155
+ end
156
+
157
+ end
158
+
159
+ describe "Not Modified responses" do
160
+ before do
161
+ now = Time.now.httpdate
162
+
163
+ resource = @http.resource(
164
+ uri_plus_params('http://localhost:3000/cached',
165
+ "modified" => now,
166
+ "Cache-Control" => 'max-age=0')
167
+ )
168
+
169
+ @first_response = resource.get
170
+ @second_response = resource.get("Cache-Control" => "max-age=0") # Force revalidation
171
+ end
172
+
173
+ it "should replace the 304 response with whats in the cache" do
174
+ @second_response.code.should == @first_response.code
175
+ end
176
+
177
+ it "should provide a body identical to the original response" do
178
+ @second_response.body.should == @first_response.body
179
+ end
180
+
181
+ it "should override any cached headers with new ones"
182
+ end
183
+
184
+ describe "cache invalidation" do
185
+
186
+ end
187
+
188
+ end
189
+
190
+ end
191
+