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