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 +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
+