resourceful 0.3.1 → 0.5.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.
Files changed (38) hide show
  1. data/Manifest +24 -28
  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 +192 -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 +74 -0
  23. data/spec/simple_sinatra_server_spec.rb +98 -0
  24. data/spec/spec_helper.rb +21 -7
  25. metadata +50 -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-07-13}
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,192 @@
1
+
2
+ require File.dirname(__FILE__) + '/../spec_helper'
3
+ require 'resourceful'
4
+ require 'addressable/template'
5
+
6
+ describe Resourceful do
7
+
8
+ describe "caching" do
9
+
10
+ before do
11
+ @http = Resourceful::HttpAccessor.new(:cache_manager => Resourceful::InMemoryCacheManager.new)
12
+ if ENV['SPEC_LOGGING']
13
+ @http.logger = Resourceful::StdOutLogger.new
14
+ end
15
+ end
16
+
17
+ def get_with_errors(resource)
18
+ begin
19
+ resp = resource.get
20
+ rescue Resourceful::UnsuccessfulHttpRequestError => e
21
+ resp = e.http_response
22
+ end
23
+ resp
24
+ end
25
+
26
+ def uri_plus_params(uri, params = {})
27
+ uri = uri.is_a?(Addressable::URI) ? uri : Addressable::URI.parse(uri)
28
+ uri.query_values = params
29
+ uri
30
+ end
31
+
32
+ def uri_for_code(code, params = {})
33
+ template = Addressable::Template.new("http://localhost:3000/code/{code}")
34
+ uri = template.expand("code" => code.to_s)
35
+
36
+ uri_plus_params(uri, params)
37
+ end
38
+
39
+ describe "response cacheability" do
40
+ Resourceful::Response::NORMALLY_CACHEABLE_RESPONSE_CODES.each do |code|
41
+ describe "response code #{code}" do
42
+ it "should normally be cached" do
43
+ resource = @http.resource(uri_for_code(code))
44
+
45
+ resp = get_with_errors(resource)
46
+ resp.should be_cacheable
47
+ end
48
+
49
+ it "should not be cached if Vary: *" do
50
+ resource = @http.resource(uri_for_code(200, "Vary" => "*"))
51
+
52
+ resp = get_with_errors(resource)
53
+ resp.should_not be_cacheable
54
+ end
55
+
56
+ it "should not be cached if Cache-Control: no-cache'" do
57
+ resource = @http.resource(uri_for_code(200, "Cache-Control" => "no-cache"))
58
+
59
+ resp = get_with_errors(resource)
60
+ resp.should_not be_cacheable
61
+ end
62
+ end
63
+ end
64
+
65
+ # I would prefer to do all other codes, but some of them do some magic stuff (100),
66
+ # so I'll just spot check.
67
+ [201, 206, 302, 307, 404, 500].each do |code|
68
+ describe "response code #{code}" do
69
+ it "should not normally be cached" do
70
+ resource = @http.resource(uri_for_code(code))
71
+
72
+ resp = get_with_errors(resource)
73
+ resp.should_not be_cacheable
74
+ end
75
+
76
+ it "should be cached if Cache-Control: public" do
77
+ resource = @http.resource(uri_for_code(code, "Cache-Control" => "public"))
78
+
79
+ resp = get_with_errors(resource)
80
+ resp.should be_cacheable
81
+ end
82
+
83
+ it "should be cached if Cache-Control: private" do
84
+ resource = @http.resource(uri_for_code(code, "Cache-Control" => "private"))
85
+
86
+ resp = get_with_errors(resource)
87
+ resp.should be_cacheable
88
+ end
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ describe "expiration" do
95
+ it 'should use the cached response if Expire: is in the future' do
96
+ in_the_future = (Time.now + 60).httpdate
97
+ resource = @http.resource(uri_for_code(200, "Expire" => in_the_future))
98
+
99
+ resp = resource.get
100
+ resp.should_not be_expired
101
+
102
+ resp = resource.get
103
+ resp.should be_ok
104
+ resp.should_not be_authoritative
105
+ end
106
+
107
+ it 'should revalidate the cached response if the response is expired' do
108
+ in_the_past = (Time.now - 60).httpdate
109
+ resource = @http.resource(uri_for_code(200, "Expire" => in_the_past))
110
+
111
+ resp = resource.get
112
+ resp.should be_expired
113
+
114
+ resp = resource.get
115
+ resp.should be_ok
116
+ resp.should be_authoritative
117
+ end
118
+ end
119
+
120
+ describe 'authoritative' do
121
+
122
+ it "should be authoritative if the response is directly from the server" do
123
+ resource = @http.resource(
124
+ uri_plus_params('http://localhost:3000/', "Cache-Control" => 'max-age=10')
125
+ )
126
+
127
+ response = resource.get
128
+ response.should be_authoritative
129
+ end
130
+
131
+ it "should be authoritative if a cached response was revalidated with the server" do
132
+ now = Time.now.httpdate
133
+ resource = @http.resource(
134
+ uri_plus_params('http://localhost:3000/cached',
135
+ "modified" => now,
136
+ "Cache-Control" => 'max-age=0')
137
+ )
138
+
139
+ resource.get
140
+ response = resource.get("Cache-Control" => "max-age=0")
141
+ response.should be_authoritative
142
+ end
143
+
144
+ it "should not be authoritative if the cached response was not revalidated" do
145
+ now = Time.now.httpdate
146
+ resource = @http.resource(
147
+ uri_plus_params('http://localhost:3000/cached',
148
+ "modified" => now,
149
+ "Cache-Control" => 'max-age=10')
150
+ )
151
+
152
+ resource.get
153
+ response = resource.get
154
+ response.should_not be_authoritative
155
+
156
+ end
157
+
158
+ end
159
+
160
+ describe "Not Modified responses" do
161
+ before do
162
+ now = Time.now.httpdate
163
+
164
+ resource = @http.resource(
165
+ uri_plus_params('http://localhost:3000/cached',
166
+ "modified" => now,
167
+ "Cache-Control" => 'max-age=0')
168
+ )
169
+
170
+ @first_response = resource.get
171
+ @second_response = resource.get("Cache-Control" => "max-age=0") # Force revalidation
172
+ end
173
+
174
+ it "should replace the 304 response with whats in the cache" do
175
+ @second_response.code.should == @first_response.code
176
+ end
177
+
178
+ it "should provide a body identical to the original response" do
179
+ @second_response.body.should == @first_response.body
180
+ end
181
+
182
+ it "should override any cached headers with new ones"
183
+ end
184
+
185
+ describe "cache invalidation" do
186
+
187
+ end
188
+
189
+ end
190
+
191
+ end
192
+