resourceful 0.2 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +6 -2
- data/lib/resourceful/cache_manager.rb +18 -4
- data/lib/resourceful/header.rb +1 -1
- data/lib/resourceful/http_accessor.rb +17 -11
- data/lib/resourceful/net_http_adapter.rb +3 -1
- data/lib/resourceful/request.rb +5 -12
- data/lib/resourceful/resource.rb +63 -17
- data/lib/resourceful/response.rb +75 -1
- data/spec/acceptance_spec.rb +19 -0
- data/spec/resourceful/cache_manager_spec.rb +18 -9
- data/spec/resourceful/header_spec.rb +1 -1
- data/spec/resourceful/http_accessor_spec.rb +1 -1
- data/spec/resourceful/net_http_adapter_spec.rb +6 -0
- data/spec/resourceful/request_spec.rb +55 -137
- data/spec/resourceful/resource_spec.rb +105 -27
- data/spec/resourceful/response_spec.rb +39 -0
- data/spec/simple_http_server_shared_spec.rb +14 -2
- data/spec/simple_http_server_shared_spec_spec.rb +8 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +4 -1
- metadata +8 -12
data/Rakefile
CHANGED
@@ -12,6 +12,7 @@ task :test => :spec
|
|
12
12
|
|
13
13
|
desc "Verify Resourceful against it's specs"
|
14
14
|
Spec::Rake::SpecTask.new(:spec) do |t|
|
15
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
15
16
|
t.libs << 'lib'
|
16
17
|
t.pattern = 'spec/**/*_spec.rb'
|
17
18
|
end
|
@@ -34,7 +35,7 @@ task :clean
|
|
34
35
|
# Packaging & Installation
|
35
36
|
##############################################################################
|
36
37
|
|
37
|
-
RESOURCEFUL_VERSION = "0.2"
|
38
|
+
RESOURCEFUL_VERSION = "0.2.1"
|
38
39
|
|
39
40
|
windows = (PLATFORM =~ /win32|cygwin/) rescue nil
|
40
41
|
|
@@ -62,7 +63,6 @@ spec = Gem::Specification.new do |s|
|
|
62
63
|
s.add_dependency "addressable"
|
63
64
|
s.add_dependency "httpauth"
|
64
65
|
s.add_dependency "rspec"
|
65
|
-
s.add_dependency "thin"
|
66
66
|
s.add_dependency "facets"
|
67
67
|
|
68
68
|
s.required_ruby_version = ">= 1.8.6"
|
@@ -87,3 +87,7 @@ task :uninstall => :clean do
|
|
87
87
|
sh %{#{SUDO} gem uninstall resourceful}
|
88
88
|
end
|
89
89
|
|
90
|
+
desc "Update rubyforge documentation"
|
91
|
+
task :update_docs do
|
92
|
+
puts %x{rsync -aPz doc/* psadauskas@resourceful.rubyforge.org:/var/www/gforge-projects/resourceful/}
|
93
|
+
end
|
@@ -27,6 +27,15 @@ module Resourceful
|
|
27
27
|
# The response to be stored.
|
28
28
|
def store(request, response); end
|
29
29
|
|
30
|
+
# Invalidates a all cached entries for a uri.
|
31
|
+
#
|
32
|
+
# This is used, for example, to invalidate the cache for a resource
|
33
|
+
# that gets POSTed to.
|
34
|
+
#
|
35
|
+
# @param uri<String>
|
36
|
+
# The uri of the resource to be invalidated
|
37
|
+
def invalidate(uri); end
|
38
|
+
|
30
39
|
# Selects the headers from the request named by the response's Vary header
|
31
40
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.6
|
32
41
|
#
|
@@ -37,7 +46,8 @@ module Resourceful
|
|
37
46
|
def select_request_headers(request, response)
|
38
47
|
header = Resourceful::Header.new
|
39
48
|
|
40
|
-
response.header['Vary'].each do |name|
|
49
|
+
response.header['Vary'].first.split(',').each do |name|
|
50
|
+
name.strip!
|
41
51
|
header[name] = request.header[name]
|
42
52
|
end if response.header['Vary']
|
43
53
|
|
@@ -63,11 +73,11 @@ module Resourceful
|
|
63
73
|
class InMemoryCacheManager < CacheManager
|
64
74
|
|
65
75
|
def initialize
|
66
|
-
@collection = Hash.new
|
76
|
+
@collection = Hash.new{ |h,k| h[k] = CacheEntryCollection.new}
|
67
77
|
end
|
68
78
|
|
69
79
|
def lookup(request)
|
70
|
-
entry = @collection[request.
|
80
|
+
entry = @collection[request.uri.to_s][request]
|
71
81
|
response = entry.response if entry
|
72
82
|
response.authoritative = false if response
|
73
83
|
|
@@ -81,7 +91,11 @@ module Resourceful
|
|
81
91
|
select_request_headers(request, response),
|
82
92
|
response)
|
83
93
|
|
84
|
-
@collection[request.
|
94
|
+
@collection[request.uri.to_s][request] = entry
|
95
|
+
end
|
96
|
+
|
97
|
+
def invalidate(uri)
|
98
|
+
@collection.delete(uri)
|
85
99
|
end
|
86
100
|
|
87
101
|
# The collection of all cached entries for a single resource (uri).
|
data/lib/resourceful/header.rb
CHANGED
@@ -8,6 +8,22 @@ require 'resourceful/resource'
|
|
8
8
|
require 'resourceful/stubbed_resource_proxy'
|
9
9
|
|
10
10
|
module Resourceful
|
11
|
+
# This is an imitation Logger used when no real logger is
|
12
|
+
# registered. This allows most of the code to assume that there
|
13
|
+
# is always a logger available, which significantly improved the
|
14
|
+
# readability of the logging related code.
|
15
|
+
class BitBucketLogger
|
16
|
+
def warn(*args); end
|
17
|
+
def info(*args); end
|
18
|
+
def debug(*args); end
|
19
|
+
end
|
20
|
+
|
21
|
+
# This is the simplest logger. It just writes everything to STDOUT.
|
22
|
+
class StdOutLogger
|
23
|
+
def warn(*args); puts args; end
|
24
|
+
def info(*args); puts args; end
|
25
|
+
def debug(*args); puts args; end
|
26
|
+
end
|
11
27
|
|
12
28
|
# This class provides a simple interface to the functionality
|
13
29
|
# provided by the Resourceful library. Conceptually this object
|
@@ -15,16 +31,6 @@ module Resourceful
|
|
15
31
|
class HttpAccessor
|
16
32
|
RESOURCEFUL_USER_AGENT_TOKEN = "Resourceful/#{RESOURCEFUL_VERSION}(Ruby/#{RUBY_VERSION})"
|
17
33
|
|
18
|
-
# This is an imitation Logger used when no real logger is
|
19
|
-
# registered. This allows most of the code to assume that there
|
20
|
-
# is always a logger available, which significantly improved the
|
21
|
-
# readability of the logging related code.
|
22
|
-
class BitBucketLogger
|
23
|
-
def warn(*args); end
|
24
|
-
def info(*args); end
|
25
|
-
def debug(*args); end
|
26
|
-
end
|
27
|
-
|
28
34
|
# A logger object to which messages about the activities of this
|
29
35
|
# object will be written. This should be an object that responds
|
30
36
|
# to +#info(message)+ and +#debug(message)+.
|
@@ -38,7 +44,7 @@ module Resourceful
|
|
38
44
|
attr_reader :user_agent_tokens
|
39
45
|
|
40
46
|
INIT_OPTIONS = OptionsInterpreter.new do
|
41
|
-
option(:logger, :default => BitBucketLogger.new)
|
47
|
+
option(:logger, :default => Resourceful::BitBucketLogger.new)
|
42
48
|
option(:user_agent, :default => []) {|ua| [ua].flatten}
|
43
49
|
option(:cache_manager, :default => NullCacheManager.new)
|
44
50
|
end
|
@@ -8,8 +8,10 @@ require Pathname(__FILE__).dirname + 'header'
|
|
8
8
|
module Addressable
|
9
9
|
class URI
|
10
10
|
def absolute_path
|
11
|
-
absolute_path =
|
11
|
+
absolute_path = ""
|
12
|
+
absolute_path << self.path.to_s
|
12
13
|
absolute_path << "?#{self.query}" if self.query != nil
|
14
|
+
absolute_path << "##{self.fragment}" if self.fragment != nil
|
13
15
|
return absolute_path
|
14
16
|
end
|
15
17
|
end
|
data/lib/resourceful/request.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pathname'
|
2
|
+
require 'benchmark'
|
2
3
|
require Pathname(__FILE__).dirname + 'response'
|
3
4
|
require Pathname(__FILE__).dirname + 'net_http_adapter'
|
4
5
|
|
@@ -21,21 +22,9 @@ module Resourceful
|
|
21
22
|
def response
|
22
23
|
@request_time = Time.now
|
23
24
|
|
24
|
-
cached_response = resource.accessor.cache_manager.lookup(self)
|
25
|
-
return cached_response if cached_response and not cached_response.stale?
|
26
|
-
|
27
|
-
set_validation_headers(cached_response) if cached_response and cached_response.stale?
|
28
|
-
|
29
25
|
http_resp = NetHttpAdapter.make_request(@method, @resource.uri, @body, @header)
|
30
26
|
response = Resourceful::Response.new(uri, *http_resp)
|
31
27
|
|
32
|
-
if response.code == 304
|
33
|
-
cached_response.header.merge(response.header)
|
34
|
-
response = cached_response
|
35
|
-
end
|
36
|
-
|
37
|
-
resource.accessor.cache_manager.store(self, response)
|
38
|
-
|
39
28
|
response.authoritative = true
|
40
29
|
response
|
41
30
|
end
|
@@ -58,6 +47,10 @@ module Resourceful
|
|
58
47
|
def uri
|
59
48
|
resource.uri
|
60
49
|
end
|
50
|
+
|
51
|
+
def logger
|
52
|
+
resource.logger
|
53
|
+
end
|
61
54
|
|
62
55
|
end
|
63
56
|
|
data/lib/resourceful/resource.rb
CHANGED
@@ -75,8 +75,10 @@ module Resourceful
|
|
75
75
|
#
|
76
76
|
# @raise [UnsuccessfulHttpRequestError] unless the request is a
|
77
77
|
# success, ie the final request returned a 2xx response code
|
78
|
-
def get
|
79
|
-
|
78
|
+
def get(header = {})
|
79
|
+
log_request_with_time "GET [#{uri}]" do
|
80
|
+
do_read_request(:get, header)
|
81
|
+
end
|
80
82
|
end
|
81
83
|
|
82
84
|
# :call-seq:
|
@@ -98,7 +100,9 @@ module Resourceful
|
|
98
100
|
def post(data = "", options = {})
|
99
101
|
raise ArgumentError, ":content_type must be specified" unless options.has_key?(:content_type)
|
100
102
|
|
101
|
-
|
103
|
+
log_request_with_time "POST [#{uri}]" do
|
104
|
+
do_write_request(:post, data, options)
|
105
|
+
end
|
102
106
|
end
|
103
107
|
|
104
108
|
# :call-seq:
|
@@ -120,7 +124,9 @@ module Resourceful
|
|
120
124
|
def put(data = "", options = {})
|
121
125
|
raise ArgumentError, ":content_type must be specified" unless options.has_key?(:content_type)
|
122
126
|
|
123
|
-
|
127
|
+
log_request_with_time "PUT [#{uri}]" do
|
128
|
+
do_write_request(:put, data, options)
|
129
|
+
end
|
124
130
|
end
|
125
131
|
|
126
132
|
# Performs a DELETE on the resource, following redirects as neccessary.
|
@@ -129,8 +135,10 @@ module Resourceful
|
|
129
135
|
#
|
130
136
|
# @raise [UnsuccessfulHttpRequestError] unless the request is a
|
131
137
|
# success, ie the final request returned a 2xx response code
|
132
|
-
def delete
|
133
|
-
|
138
|
+
def delete(options = {})
|
139
|
+
log_request_with_time "DELETE [#{uri}]" do
|
140
|
+
do_write_request(:delete, {}, options)
|
141
|
+
end
|
134
142
|
end
|
135
143
|
|
136
144
|
# Performs a read request (HEAD, GET). Users should use the #get, etc methods instead.
|
@@ -144,32 +152,51 @@ module Resourceful
|
|
144
152
|
# @raise [UnsuccessfulHttpRequestError] unless the request is a
|
145
153
|
# success, ie the final request returned a 2xx response code
|
146
154
|
#
|
147
|
-
|
148
|
-
|
149
|
-
def do_read_request(method)
|
150
|
-
request = Resourceful::Request.new(method, self)
|
155
|
+
def do_read_request(method, header = {})
|
156
|
+
request = Resourceful::Request.new(method, self, nil, header)
|
151
157
|
accessor.auth_manager.add_credentials(request)
|
152
158
|
|
159
|
+
cached_response = accessor.cache_manager.lookup(request)
|
160
|
+
if cached_response
|
161
|
+
logger.debug(" Retrieved from cache")
|
162
|
+
if not cached_response.stale?
|
163
|
+
# We're done!
|
164
|
+
return cached_response
|
165
|
+
else
|
166
|
+
logger.debug(" Cache entry is stale")
|
167
|
+
request.set_validation_headers(cached_response)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
153
171
|
response = request.response
|
154
172
|
|
173
|
+
if response.is_not_modified?
|
174
|
+
cached_response.header.merge(response.header)
|
175
|
+
response = cached_response
|
176
|
+
response.authoritative = true
|
177
|
+
end
|
178
|
+
|
155
179
|
if response.is_redirect? and request.should_be_redirected?
|
156
180
|
if response.is_permanent_redirect?
|
157
181
|
@uris.unshift response.header['Location'].first
|
158
|
-
response = do_read_request(method)
|
182
|
+
response = do_read_request(method, header)
|
159
183
|
else
|
160
184
|
redirected_resource = Resourceful::Resource.new(self.accessor, response.header['Location'].first)
|
161
|
-
response = redirected_resource.do_read_request(method)
|
185
|
+
response = redirected_resource.do_read_request(method, header)
|
162
186
|
end
|
163
187
|
end
|
164
188
|
|
165
189
|
if response.is_not_authorized? && !@already_tried_with_auth
|
166
190
|
@already_tried_with_auth = true
|
167
191
|
accessor.auth_manager.associate_auth_info(response)
|
168
|
-
|
192
|
+
logger.debug("Authentication Required. Retrying with auth info")
|
193
|
+
response = do_read_request(method, header)
|
169
194
|
end
|
170
195
|
|
171
196
|
raise UnsuccessfulHttpRequestError.new(request,response) unless response.is_success?
|
172
197
|
|
198
|
+
accessor.cache_manager.store(request, response) if response.is_success?
|
199
|
+
|
173
200
|
return response
|
174
201
|
end
|
175
202
|
|
@@ -186,31 +213,50 @@ module Resourceful
|
|
186
213
|
#
|
187
214
|
# @raise [UnsuccessfulHttpRequestError] unless the request is a
|
188
215
|
# success, ie the final request returned a 2xx response code
|
189
|
-
# --
|
190
|
-
# @private
|
191
216
|
def do_write_request(method, data = nil, header = {})
|
192
217
|
request = Resourceful::Request.new(method, self, data, header)
|
193
218
|
accessor.auth_manager.add_credentials(request)
|
194
219
|
|
195
220
|
response = request.response
|
196
|
-
|
221
|
+
|
197
222
|
if response.is_redirect? and request.should_be_redirected?
|
198
223
|
if response.is_permanent_redirect?
|
199
224
|
@uris.unshift response.header['Location'].first
|
200
225
|
response = do_write_request(method, data, header)
|
201
226
|
elsif response.code == 303 # see other, must use GET for new location
|
202
227
|
redirected_resource = Resourceful::Resource.new(self.accessor, response.header['Location'].first)
|
203
|
-
response = redirected_resource.do_read_request(:get)
|
228
|
+
response = redirected_resource.do_read_request(:get, header)
|
204
229
|
else
|
205
230
|
redirected_resource = Resourceful::Resource.new(self.accessor, response.header['Location'].first)
|
206
231
|
response = redirected_resource.do_write_request(method, data, header)
|
207
232
|
end
|
208
233
|
end
|
209
234
|
|
235
|
+
if response.is_not_authorized? && !@already_tried_with_auth
|
236
|
+
@already_tried_with_auth = true
|
237
|
+
accessor.auth_manager.associate_auth_info(response)
|
238
|
+
logger.debug("Authentication Required. Retrying with auth info")
|
239
|
+
response = do_write_request(method, data, header)
|
240
|
+
end
|
241
|
+
|
210
242
|
raise UnsuccessfulHttpRequestError.new(request,response) unless response.is_success?
|
243
|
+
|
244
|
+
accessor.cache_manager.invalidate(uri)
|
211
245
|
return response
|
212
246
|
end
|
213
247
|
|
248
|
+
def log_request_with_time(msg, indent = 2)
|
249
|
+
logger.info(" " * indent + msg)
|
250
|
+
result = nil
|
251
|
+
time = Benchmark.measure { result = yield }
|
252
|
+
logger.info(" " * indent + "-> Returned #{result.code} in %.4fs" % time.real)
|
253
|
+
result
|
254
|
+
end
|
255
|
+
|
256
|
+
def logger
|
257
|
+
accessor.logger
|
258
|
+
end
|
259
|
+
|
214
260
|
end
|
215
261
|
|
216
262
|
end
|
data/lib/resourceful/response.rb
CHANGED
@@ -2,6 +2,7 @@ require 'net/http'
|
|
2
2
|
require 'time'
|
3
3
|
require 'rubygems'
|
4
4
|
require 'facets/kernel/ergo'
|
5
|
+
require 'zlib'
|
5
6
|
|
6
7
|
module Resourceful
|
7
8
|
# Exception indicating that the server used a content coding scheme
|
@@ -23,35 +24,105 @@ module Resourceful
|
|
23
24
|
@response_time = Time.now
|
24
25
|
end
|
25
26
|
|
27
|
+
# Is the response code sucessful? True for only 2xx series
|
28
|
+
# response codes.
|
29
|
+
#
|
30
|
+
# @return true|false
|
26
31
|
def is_success?
|
27
32
|
@code.in? 200..299
|
28
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?
|
29
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
|
30
76
|
def is_redirect?
|
31
77
|
@code.in? REDIRECT_RESPONSE_CODES
|
32
78
|
end
|
33
79
|
alias was_redirect? is_redirect?
|
34
80
|
|
81
|
+
# Is the response a Permanent Redirect (301) ?
|
82
|
+
#
|
83
|
+
# @return true|false
|
35
84
|
def is_permanent_redirect?
|
36
85
|
@code == 301
|
37
86
|
end
|
38
87
|
|
88
|
+
# Is the response a Temporary Redirect (anything but 301) ?
|
89
|
+
#
|
90
|
+
# @return true|false
|
39
91
|
def is_temporary_redirect?
|
40
92
|
is_redirect? and not is_permanent_redirect?
|
41
93
|
end
|
42
94
|
|
95
|
+
# Is the response a client error of Not Authorized (401) ?
|
96
|
+
#
|
97
|
+
# @return true|false
|
43
98
|
def is_not_authorized?
|
44
99
|
@code == 401
|
45
100
|
end
|
46
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
|
+
# Is this a cached response that has expired?
|
110
|
+
#
|
111
|
+
# @return true|false
|
47
112
|
def expired?
|
48
113
|
if header['Expire']
|
49
114
|
return true if Time.httpdate(header['Expire'].first) < Time.now
|
50
115
|
end
|
116
|
+
if header['Cache-Control'] and header['Cache-Control'].include?('max-age')
|
117
|
+
return true if current_age > max_age
|
118
|
+
end
|
51
119
|
|
52
120
|
false
|
53
121
|
end
|
54
122
|
|
123
|
+
# Is this a cached response that is stale?
|
124
|
+
#
|
125
|
+
# @return true|false
|
55
126
|
def stale?
|
56
127
|
return true if expired?
|
57
128
|
if header['Cache-Control']
|
@@ -62,6 +133,9 @@ module Resourceful
|
|
62
133
|
false
|
63
134
|
end
|
64
135
|
|
136
|
+
# Is this response cachable?
|
137
|
+
#
|
138
|
+
# @return true|false
|
65
139
|
def cachable?
|
66
140
|
return false if header['Vary'] and header['Vary'].include?('*')
|
67
141
|
return false if header['Cache-Control'] and header['Cache-Control'].include?('no-store')
|
@@ -86,7 +160,7 @@ module Resourceful
|
|
86
160
|
# body is identity encoded; just return it
|
87
161
|
@body
|
88
162
|
when /^\s*gzip\s*$/i
|
89
|
-
gz_in = Zlib::GzipReader.new(StringIO.new(@body, 'r'))
|
163
|
+
gz_in = ::Zlib::GzipReader.new(StringIO.new(@body, 'r'))
|
90
164
|
@body = gz_in.read
|
91
165
|
gz_in.close
|
92
166
|
header.delete('Content-Encoding')
|
data/spec/acceptance_spec.rb
CHANGED
@@ -23,6 +23,14 @@ describe Resourceful do
|
|
23
23
|
resp.header['Content-Type'].should == ['text/plain']
|
24
24
|
end
|
25
25
|
|
26
|
+
it 'should set additional headers on the #get' do
|
27
|
+
resource = @accessor.resource('http://localhost:3000/echo_header')
|
28
|
+
resp = resource.get(:foo => :bar)
|
29
|
+
resp.should be_instance_of(Resourceful::Response)
|
30
|
+
resp.code.should == 200
|
31
|
+
resp.body.should =~ /"HTTP_FOO"=>"bar"/
|
32
|
+
end
|
33
|
+
|
26
34
|
it 'should #post a resource, and return the response' do
|
27
35
|
resource = @accessor.resource('http://localhost:3000/post')
|
28
36
|
resp = resource.post('Hello world from POST', :content_type => 'text/plain')
|
@@ -191,6 +199,17 @@ describe Resourceful do
|
|
191
199
|
resp2.should == resp
|
192
200
|
end
|
193
201
|
|
202
|
+
it 'should not use a cached document for a resource that has been posted to' do
|
203
|
+
resource = @accessor.resource('http://localhost:3000/get')
|
204
|
+
resp = resource.get
|
205
|
+
resp.authoritative?.should be_true
|
206
|
+
|
207
|
+
resource.post("foo", :content_type => 'text/plain')
|
208
|
+
|
209
|
+
resp2 = resource.get
|
210
|
+
resp2.should_not == resp
|
211
|
+
end
|
212
|
+
|
194
213
|
describe 'Cache-Control' do
|
195
214
|
|
196
215
|
it 'should cache anything with "Cache-Control: public"' do
|
@@ -20,6 +20,10 @@ describe Resourceful::CacheManager do
|
|
20
20
|
@cm.should respond_to(:store)
|
21
21
|
end
|
22
22
|
|
23
|
+
it 'should have a invalidate method' do
|
24
|
+
@cm.should respond_to(:invalidate)
|
25
|
+
end
|
26
|
+
|
23
27
|
describe '#select_request_headers' do
|
24
28
|
before do
|
25
29
|
@req_header = mock('header', :[] => nil)
|
@@ -35,7 +39,7 @@ describe Resourceful::CacheManager do
|
|
35
39
|
end
|
36
40
|
|
37
41
|
it 'should pull the values from the request that match keys in the vary header' do
|
38
|
-
@resp_header.should_receive(:[]).with('Vary').twice.and_return(['foo
|
42
|
+
@resp_header.should_receive(:[]).with('Vary').twice.and_return(['foo, bar'])
|
39
43
|
@req_header.should_receive(:[]).with('foo').and_return('oof')
|
40
44
|
@req_header.should_receive(:[]).with('bar').and_return('rab')
|
41
45
|
|
@@ -64,16 +68,16 @@ describe Resourceful::NullCacheManager do
|
|
64
68
|
@ncm.should respond_to(:store)
|
65
69
|
|
66
70
|
lambda { @ncm.store(:foo, :bar) }.should_not raise_error
|
67
|
-
|
68
71
|
end
|
69
72
|
|
70
73
|
end
|
71
74
|
|
72
75
|
describe Resourceful::InMemoryCacheManager do
|
73
76
|
before do
|
74
|
-
@request = mock('request', :resource => mock('resource'
|
75
|
-
:request_time => Time.utc(2008,5,22,15,00)
|
76
|
-
|
77
|
+
@request = mock('request', :resource => mock('resource'),
|
78
|
+
:request_time => Time.utc(2008,5,22,15,00),
|
79
|
+
:uri => 'uri')
|
80
|
+
@response = mock('response', :header => {}, :cachable? => true)
|
77
81
|
|
78
82
|
@entry = mock('cache entry', :response => @response, :valid_for? => true)
|
79
83
|
Resourceful::InMemoryCacheManager::CacheEntry.stub!(:new).and_return(@entry)
|
@@ -98,10 +102,6 @@ describe Resourceful::InMemoryCacheManager do
|
|
98
102
|
end
|
99
103
|
|
100
104
|
describe 'saving' do
|
101
|
-
before do
|
102
|
-
@response.stub!(:cachable?).and_return(true)
|
103
|
-
end
|
104
|
-
|
105
105
|
it 'should make a new cache entry' do
|
106
106
|
Resourceful::InMemoryCacheManager::CacheEntry.should_receive(:new).with(
|
107
107
|
Time.utc(2008,5,22,15,00),
|
@@ -131,6 +131,15 @@ describe Resourceful::InMemoryCacheManager do
|
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
+
describe 'invalidating' do
|
135
|
+
it 'should remove an entry from the cache by uri' do
|
136
|
+
@imcm.store(@request, @response)
|
137
|
+
@imcm.invalidate('uri')
|
138
|
+
col = @imcm.instance_variable_get("@collection")
|
139
|
+
col.should_not have_key('uri')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
134
143
|
end
|
135
144
|
|
136
145
|
describe Resourceful::InMemoryCacheManager::CacheEntryCollection do
|
@@ -23,7 +23,7 @@ describe Resourceful::Header do
|
|
23
23
|
|
24
24
|
h.capitalize("foo").should == "Foo"
|
25
25
|
h.capitalize("foo-bar").should == "Foo-Bar"
|
26
|
-
h.capitalize("foo_bar").should == "
|
26
|
+
h.capitalize("foo_bar").should == "Foo-Bar"
|
27
27
|
h.capitalize("foo bar").should == "Foo Bar"
|
28
28
|
h.capitalize("foo-bar-quux").should == "Foo-Bar-Quux"
|
29
29
|
h.capitalize("foo-bar-2quux").should == "Foo-Bar-2quux"
|
@@ -18,7 +18,7 @@ describe Resourceful::HttpAccessor, 'init' do
|
|
18
18
|
it 'should provide logger object even when no logger is specified' do
|
19
19
|
ha = Resourceful::HttpAccessor.new()
|
20
20
|
|
21
|
-
ha.logger.should be_instance_of(Resourceful::
|
21
|
+
ha.logger.should be_instance_of(Resourceful::BitBucketLogger)
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'should raise arg error if unrecognized options are passed' do
|
@@ -87,4 +87,10 @@ describe Addressable::URI, '#absolute_path monkey patch' do
|
|
87
87
|
uri.absolute_path.should == '/foo'
|
88
88
|
end
|
89
89
|
|
90
|
+
it 'should not add the query parameter twice' do
|
91
|
+
uri = Addressable::URI.parse('http://localhost/foo?bar=baz')
|
92
|
+
uri.absolute_path.should == '/foo?bar=baz'
|
93
|
+
uri.absolute_path.should == '/foo?bar=baz'
|
94
|
+
end
|
95
|
+
|
90
96
|
end
|
@@ -6,7 +6,7 @@ require 'addressable/uri'
|
|
6
6
|
describe Resourceful::Request do
|
7
7
|
before do
|
8
8
|
@uri = Addressable::URI.parse('http://www.example.com')
|
9
|
-
@resource = mock('resource')
|
9
|
+
@resource = mock('resource', :logger => Resourceful::BitBucketLogger.new)
|
10
10
|
@resource.stub!(:uri).and_return(@uri)
|
11
11
|
|
12
12
|
@request = Resourceful::Request.new(:get, @resource)
|
@@ -14,7 +14,7 @@ describe Resourceful::Request do
|
|
14
14
|
@cachemgr = mock('cache_mgr')
|
15
15
|
@cachemgr.stub!(:lookup).and_return(nil)
|
16
16
|
@cachemgr.stub!(:store)
|
17
|
-
@resource.stub!(:accessor).and_return(mock('accessor', :cache_manager => @cachemgr))
|
17
|
+
@resource.stub!(:accessor).and_return(mock('accessor', :cache_manager => @cachemgr, :logger => Resourceful::BitBucketLogger.new))
|
18
18
|
end
|
19
19
|
|
20
20
|
describe 'init' do
|
@@ -50,7 +50,7 @@ describe Resourceful::Request do
|
|
50
50
|
@net_http_adapter_response = mock('net_http_adapter_response')
|
51
51
|
Resourceful::NetHttpAdapter.stub!(:make_request).and_return(@net_http_adapter_response)
|
52
52
|
|
53
|
-
@response = mock('response', :code => 200, :authoritative= => true)
|
53
|
+
@response = mock('response', :code => 200, :authoritative= => true, :was_unsuccessful? => false)
|
54
54
|
Resourceful::Response.stub!(:new).and_return(@response)
|
55
55
|
end
|
56
56
|
|
@@ -63,145 +63,13 @@ describe Resourceful::Request do
|
|
63
63
|
end
|
64
64
|
|
65
65
|
it 'should set the request_time to now' do
|
66
|
-
now =
|
66
|
+
now = Time.now
|
67
67
|
Time.stub!(:now).and_return(now)
|
68
68
|
|
69
69
|
@request.response
|
70
70
|
@request.request_time.should == now
|
71
71
|
end
|
72
72
|
|
73
|
-
describe 'Caching' do
|
74
|
-
before do
|
75
|
-
@cached_response = mock('cached_response', :body => "", :authoritative= => true)
|
76
|
-
@cached_response.stub!(:stale?).and_return(false)
|
77
|
-
|
78
|
-
@cached_response_header = mock('header', :[] => nil, :has_key? => false)
|
79
|
-
@cached_response.stub!(:header).and_return(@cached_response_header)
|
80
|
-
|
81
|
-
@cachemgr.stub!(:lookup).and_return(@cached_response)
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'should lookup the request in the cache' do
|
85
|
-
@cachemgr.should_receive(:lookup).with(@request)
|
86
|
-
@request.response
|
87
|
-
end
|
88
|
-
|
89
|
-
it 'should check if the cached response is stale' do
|
90
|
-
@cached_response.should_receive(:stale?).and_return(false)
|
91
|
-
@request.response
|
92
|
-
end
|
93
|
-
|
94
|
-
describe 'cached' do
|
95
|
-
|
96
|
-
it 'should return the cached response if it was found and not stale' do
|
97
|
-
@cached_response.stale?.should_not be_true
|
98
|
-
@request.response.should == @cached_response
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
describe 'cached but stale' do
|
104
|
-
before do
|
105
|
-
@cached_response.stub!(:stale?).and_return(true)
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'should add the validation headers from the cached_response to it\'s header' do
|
109
|
-
@request.should_receive(:set_validation_headers).with(@cached_response)
|
110
|
-
|
111
|
-
@request.response
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'should #get the uri from the NetHttpAdapter' do
|
115
|
-
Resourceful::NetHttpAdapter.should_receive(:make_request).
|
116
|
-
with(:get, @uri, nil, anything).and_return(@net_http_adapter_response)
|
117
|
-
@request.response
|
118
|
-
end
|
119
|
-
|
120
|
-
it 'should create a Resourceful::Response object from the NetHttpAdapter response' do
|
121
|
-
Resourceful::Response.should_receive(:new).with(@request.uri, @net_http_adapter_response).and_return(@response)
|
122
|
-
@request.response
|
123
|
-
end
|
124
|
-
|
125
|
-
it 'should merge the response\'s headers with the cached response\'s if the response was a 304' do
|
126
|
-
@response_header = mock('header')
|
127
|
-
@response.stub!(:header).and_return(@response_header)
|
128
|
-
@response.stub!(:code).and_return(304)
|
129
|
-
@cached_response_header.should_receive(:merge).with(@response_header)
|
130
|
-
@request.response
|
131
|
-
end
|
132
|
-
|
133
|
-
it 'should store the response in the cache manager' do
|
134
|
-
@cachemgr.should_receive(:store).with(@request, @response)
|
135
|
-
@request.response
|
136
|
-
end
|
137
|
-
|
138
|
-
end
|
139
|
-
|
140
|
-
describe 'not cached' do
|
141
|
-
before do
|
142
|
-
@cachemgr.stub!(:lookup).and_return(nil)
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'should #get the uri from the NetHttpAdapter' do
|
146
|
-
Resourceful::NetHttpAdapter.should_receive(:make_request).
|
147
|
-
with(:get, @uri, nil, anything).and_return(@net_http_adapter_response)
|
148
|
-
@request.response
|
149
|
-
end
|
150
|
-
|
151
|
-
it 'should create a Resourceful::Response object from the NetHttpAdapter response' do
|
152
|
-
Resourceful::Response.should_receive(:new).with(@request.uri, @net_http_adapter_response).and_return(@response)
|
153
|
-
@request.response
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'should store the response in the cache manager' do
|
157
|
-
@cachemgr.should_receive(:store).with(@request, @response)
|
158
|
-
@request.response
|
159
|
-
end
|
160
|
-
|
161
|
-
end
|
162
|
-
|
163
|
-
describe '#set_validation_headers' do
|
164
|
-
it 'should have an #set_validation_headers method' do
|
165
|
-
@request.should respond_to(:set_validation_headers)
|
166
|
-
end
|
167
|
-
|
168
|
-
it 'should set If-None-Match to the cached response\'s ETag' do
|
169
|
-
@cached_response_header.should_receive(:[]).with('ETag').and_return('some etag')
|
170
|
-
@cached_response_header.should_receive(:has_key?).with('ETag').and_return(true)
|
171
|
-
@request.set_validation_headers(@cached_response)
|
172
|
-
|
173
|
-
@request.header['If-None-Match'].should == 'some etag'
|
174
|
-
end
|
175
|
-
|
176
|
-
it 'should not set If-None-Match if the cached response does not have an ETag' do
|
177
|
-
@request.set_validation_headers(@cached_response)
|
178
|
-
@request.header.should_not have_key('If-None-Match')
|
179
|
-
end
|
180
|
-
|
181
|
-
it 'should set If-Modified-Since to the cached response\'s Last-Modified' do
|
182
|
-
@cached_response_header.should_receive(:[]).with('Last-Modified').and_return('some date')
|
183
|
-
@cached_response_header.should_receive(:has_key?).with('Last-Modified').and_return(true)
|
184
|
-
@request.set_validation_headers(@cached_response)
|
185
|
-
|
186
|
-
@request.header['If-Modified-Since'].should == 'some date'
|
187
|
-
end
|
188
|
-
|
189
|
-
it 'should not set If-Modified-Since if the cached response does not have Last-Modified' do
|
190
|
-
@request.set_validation_headers(@cached_response)
|
191
|
-
@request.header.should_not have_key('If-Modified-Since')
|
192
|
-
end
|
193
|
-
|
194
|
-
it 'should add "Cache-Control: max-age=0" to the request when revalidating a response that has "Cache-Control: must-revalidate" set' do
|
195
|
-
@cached_response_header.should_receive(:[]).with('Cache-Control').and_return(['must-revalidate'])
|
196
|
-
@cached_response_header.should_receive(:has_key?).with('Cache-Control').and_return(true)
|
197
|
-
@request.set_validation_headers(@cached_response)
|
198
|
-
|
199
|
-
@request.header['Cache-Control'].should include('max-age=0')
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
end
|
204
|
-
|
205
73
|
end
|
206
74
|
|
207
75
|
describe '#should_be_redirected?' do
|
@@ -209,7 +77,7 @@ describe Resourceful::Request do
|
|
209
77
|
@net_http_adapter_response = mock('net_http_adapter_response')
|
210
78
|
Resourceful::NetHttpAdapter.stub!(:make_request).and_return(@net_http_adapter_response)
|
211
79
|
|
212
|
-
@response = mock('response', :code => 200, :authoritative= => true)
|
80
|
+
@response = mock('response', :code => 200, :authoritative= => true, :was_unsuccessful? => false)
|
213
81
|
Resourceful::Response.stub!(:new).and_return(@response)
|
214
82
|
end
|
215
83
|
|
@@ -257,5 +125,55 @@ describe Resourceful::Request do
|
|
257
125
|
end
|
258
126
|
end
|
259
127
|
|
128
|
+
describe '#set_validation_headers' do
|
129
|
+
before do
|
130
|
+
@cached_response = mock('cached_response')
|
131
|
+
|
132
|
+
@cached_response_header = mock('header', :[] => nil, :has_key? => false)
|
133
|
+
@cached_response.stub!(:header).and_return(@cached_response_header)
|
134
|
+
|
135
|
+
@cachemgr.stub!(:lookup).and_return(@cached_response)
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should have an #set_validation_headers method' do
|
139
|
+
@request.should respond_to(:set_validation_headers)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should set If-None-Match to the cached response\'s ETag' do
|
143
|
+
@cached_response_header.should_receive(:[]).with('ETag').and_return('some etag')
|
144
|
+
@cached_response_header.should_receive(:has_key?).with('ETag').and_return(true)
|
145
|
+
@request.set_validation_headers(@cached_response)
|
146
|
+
|
147
|
+
@request.header['If-None-Match'].should == 'some etag'
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should not set If-None-Match if the cached response does not have an ETag' do
|
151
|
+
@request.set_validation_headers(@cached_response)
|
152
|
+
@request.header.should_not have_key('If-None-Match')
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should set If-Modified-Since to the cached response\'s Last-Modified' do
|
156
|
+
@cached_response_header.should_receive(:[]).with('Last-Modified').and_return('some date')
|
157
|
+
@cached_response_header.should_receive(:has_key?).with('Last-Modified').and_return(true)
|
158
|
+
@request.set_validation_headers(@cached_response)
|
159
|
+
|
160
|
+
@request.header['If-Modified-Since'].should == 'some date'
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should not set If-Modified-Since if the cached response does not have Last-Modified' do
|
164
|
+
@request.set_validation_headers(@cached_response)
|
165
|
+
@request.header.should_not have_key('If-Modified-Since')
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'should add "Cache-Control: max-age=0" to the request when revalidating a response that has "Cache-Control: must-revalidate" set' do
|
169
|
+
@cached_response_header.should_receive(:[]).with('Cache-Control').and_return(['must-revalidate'])
|
170
|
+
@cached_response_header.should_receive(:has_key?).with('Cache-Control').and_return(true)
|
171
|
+
@request.set_validation_headers(@cached_response)
|
172
|
+
|
173
|
+
@request.header['Cache-Control'].should include('max-age=0')
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
260
178
|
end
|
261
179
|
|
@@ -5,13 +5,23 @@ require 'resourceful/resource'
|
|
5
5
|
|
6
6
|
describe Resourceful::Resource do
|
7
7
|
before do
|
8
|
-
@
|
8
|
+
@auth_manager = mock('auth_manager', :add_credentials => nil)
|
9
|
+
@cache_manager = mock('cache_manager', :lookup => nil, :store => nil, :invalidate => nil)
|
10
|
+
@logger = mock('logger', :debug => nil, :info => nil)
|
11
|
+
@accessor = mock('accessor', :auth_manager => @auth_manager,
|
12
|
+
:cache_manager => @cache_manager,
|
13
|
+
:logger => @logger)
|
14
|
+
|
9
15
|
@uri = 'http://www.example.com/'
|
10
16
|
@resource = Resourceful::Resource.new(@accessor, @uri)
|
11
17
|
|
12
|
-
@response = mock('response', :code => 200,
|
18
|
+
@response = mock('response', :code => 200,
|
19
|
+
:is_redirect? => false,
|
20
|
+
:is_not_authorized? => false,
|
21
|
+
:is_success? => true,
|
22
|
+
:is_not_modified? => false)
|
13
23
|
|
14
|
-
@request = mock('request', :response => @response, :should_be_redirected? => true)
|
24
|
+
@request = mock('request', :response => @response, :should_be_redirected? => true, :uri => @uri)
|
15
25
|
Resourceful::Request.stub!(:new).and_return(@request)
|
16
26
|
end
|
17
27
|
|
@@ -48,10 +58,15 @@ describe Resourceful::Resource do
|
|
48
58
|
end
|
49
59
|
|
50
60
|
it 'should make a new request object from the method' do
|
51
|
-
Resourceful::Request.should_receive(:new).with(:some_method, @resource).and_return(@request)
|
61
|
+
Resourceful::Request.should_receive(:new).with(:some_method, @resource, nil, {}).and_return(@request)
|
52
62
|
make_request
|
53
63
|
end
|
54
64
|
|
65
|
+
it 'should set the header of the request from the header arg' do
|
66
|
+
Resourceful::Request.should_receive(:new).with(:some_method, @resource, nil, :foo => :bar).and_return(@request)
|
67
|
+
@resource.do_read_request(:some_method, :foo => :bar)
|
68
|
+
end
|
69
|
+
|
55
70
|
describe 'non-success responses' do
|
56
71
|
before do
|
57
72
|
@uri = 'http://www.example.com/code/404'
|
@@ -63,6 +78,7 @@ describe Resourceful::Resource do
|
|
63
78
|
:is_redirect? => false,
|
64
79
|
:is_success? => false,
|
65
80
|
:is_not_authorized? => false,
|
81
|
+
:is_not_modified? => false,
|
66
82
|
:code => 404)
|
67
83
|
|
68
84
|
@request.stub!(:response).and_return(@redirect_response, @response)
|
@@ -92,7 +108,8 @@ describe Resourceful::Resource do
|
|
92
108
|
@redirect_response = mock('redirect_response',
|
93
109
|
:header => {'Location' => [@redirected_uri]},
|
94
110
|
:is_redirect? => true,
|
95
|
-
:is_permanent_redirect? => true
|
111
|
+
:is_permanent_redirect? => true,
|
112
|
+
:is_not_modified? => false)
|
96
113
|
|
97
114
|
@request.stub!(:response).and_return(@redirect_response, @response)
|
98
115
|
|
@@ -182,17 +199,54 @@ describe Resourceful::Resource do
|
|
182
199
|
end
|
183
200
|
|
184
201
|
it 'should re-make the request only once if it was not authorized the first time' do
|
185
|
-
Resourceful::Request.should_receive(:new).with(:some_method, @resource).twice.and_return(@request)
|
202
|
+
Resourceful::Request.should_receive(:new).with(:some_method, @resource, nil, {}).twice.and_return(@request)
|
186
203
|
@response.stub!(:is_not_authorized?).and_return(true)
|
187
204
|
make_request
|
188
205
|
end
|
189
206
|
|
190
207
|
end
|
191
208
|
|
209
|
+
describe 'with caching' do
|
210
|
+
before do
|
211
|
+
@cached_response = mock('cached response', :is_redirect? => false,
|
212
|
+
:is_not_authorized? => false,
|
213
|
+
:is_success? => true,
|
214
|
+
:stale? => false)
|
215
|
+
@cache_manager.stub!(:lookup).and_return(@cached_response)
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should lookup the request in the cache' do
|
219
|
+
@cache_manager.should_receive(:lookup).with(@request)
|
220
|
+
make_request
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should check if the cached response is stale' do
|
224
|
+
@cached_response.should_receive(:stale?).and_return(false)
|
225
|
+
make_request
|
226
|
+
end
|
227
|
+
|
228
|
+
describe 'in cache' do
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
describe 'in cache but stale' do
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
describe 'not in cache' do
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
192
242
|
end
|
193
243
|
|
194
244
|
describe '#do_write_request' do
|
195
245
|
|
246
|
+
def make_request
|
247
|
+
@resource.do_write_request(:some_method, "data", {})
|
248
|
+
end
|
249
|
+
|
196
250
|
it 'should make a new request object from the method' do
|
197
251
|
Resourceful::Request.should_receive(:new).with(:some_method, @resource, "data", anything).and_return(@request)
|
198
252
|
@resource.do_write_request(:some_method, "data")
|
@@ -244,10 +298,6 @@ describe Resourceful::Resource do
|
|
244
298
|
|
245
299
|
end
|
246
300
|
|
247
|
-
def make_request
|
248
|
-
@resource.do_write_request(:some_method, "data", {})
|
249
|
-
end
|
250
|
-
|
251
301
|
it 'should check if the response was a redirect' do
|
252
302
|
@redirect_response.should_receive(:is_redirect?).and_return(true)
|
253
303
|
make_request
|
@@ -311,15 +361,47 @@ describe Resourceful::Resource do
|
|
311
361
|
end
|
312
362
|
|
313
363
|
it 'should redirect to the new location with a GET request, regardless of the original method' do
|
314
|
-
@new_resource.should_receive(:do_read_request).with(:get).and_return(@response)
|
364
|
+
@new_resource.should_receive(:do_read_request).with(:get, {}).and_return(@response)
|
315
365
|
make_request
|
316
366
|
end
|
317
367
|
end
|
318
|
-
end
|
319
368
|
|
369
|
+
end
|
320
370
|
|
321
371
|
end # write with redirection
|
322
372
|
|
373
|
+
describe 'with authorization' do
|
374
|
+
before do
|
375
|
+
@authmgr = mock('auth_manager')
|
376
|
+
@authmgr.stub!(:add_credentials)
|
377
|
+
@authmgr.stub!(:associate_auth_info).and_return(true)
|
378
|
+
|
379
|
+
@accessor.stub!(:auth_manager).and_return(@authmgr)
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'should attempt to add credentials to the request' do
|
383
|
+
@authmgr.should_receive(:add_credentials).with(@request)
|
384
|
+
make_request
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'should check if the response was not authorized' do
|
388
|
+
@response.should_receive(:is_not_authorized?).and_return(false)
|
389
|
+
make_request
|
390
|
+
end
|
391
|
+
|
392
|
+
it 'should associate the auth info in the response if it was not authorized' do
|
393
|
+
@authmgr.should_receive(:associate_auth_info).with(@response).and_return(true)
|
394
|
+
@response.stub!(:is_not_authorized?).and_return(true)
|
395
|
+
make_request
|
396
|
+
end
|
397
|
+
|
398
|
+
it 'should re-make the request only once if it was not authorized the first time' do
|
399
|
+
Resourceful::Request.should_receive(:new).with(:some_method, @resource, "data", {}).twice.and_return(@request)
|
400
|
+
@response.stub!(:is_not_authorized?).and_return(true)
|
401
|
+
make_request
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
323
405
|
end
|
324
406
|
|
325
407
|
describe 'callback registration' do
|
@@ -352,7 +434,7 @@ describe Resourceful::Resource do
|
|
352
434
|
end
|
353
435
|
|
354
436
|
it 'should pass :get to the #do_read_request method' do
|
355
|
-
@resource.should_receive(:do_read_request).with(:get)
|
437
|
+
@resource.should_receive(:do_read_request).with(:get, {}).and_return(@response)
|
356
438
|
@resource.get
|
357
439
|
end
|
358
440
|
|
@@ -374,17 +456,10 @@ describe Resourceful::Resource do
|
|
374
456
|
|
375
457
|
end
|
376
458
|
|
377
|
-
end
|
378
|
-
|
379
|
-
describe Resourceful::Resource do
|
380
|
-
|
381
459
|
describe "#post(body_data, :content_type => content-type)" do
|
382
460
|
before do
|
383
|
-
@auth_manager = mock('auth_manager', :add_credentials => nil)
|
384
|
-
@cache_manager = mock('cache_manager', :lookup => nil, :store => nil)
|
385
|
-
@accessor = mock('accessor', :auth_manager => @auth_manager, :cache_manager => @cache_manager)
|
386
461
|
@resource = Resourceful::Resource.new(@accessor, 'http://foo.invalid/')
|
387
|
-
@response = mock('response', :is_redirect? => false, :is_success? => true)
|
462
|
+
@response = mock('response', :is_redirect? => false, :is_success? => true, :is_not_authorized? => false, :code => 200)
|
388
463
|
@request = mock('request', :response => @response)
|
389
464
|
Resourceful::Request.stub!(:new).and_return(@request)
|
390
465
|
end
|
@@ -397,7 +472,10 @@ describe Resourceful::Resource do
|
|
397
472
|
|
398
473
|
it 'should put the content type in the header' do
|
399
474
|
Resourceful::Request.should_receive(:new).
|
400
|
-
with(anything,
|
475
|
+
with(anything,
|
476
|
+
anything,
|
477
|
+
anything,
|
478
|
+
hash_including(:content_type =>'text/plain')).
|
401
479
|
and_return(@request)
|
402
480
|
|
403
481
|
@resource.post("a body", :content_type => 'text/plain')
|
@@ -430,11 +508,8 @@ describe Resourceful::Resource do
|
|
430
508
|
|
431
509
|
describe "#put(body_data, :content_type => content_type)" do
|
432
510
|
before do
|
433
|
-
@auth_manager = mock('auth_manager', :add_credentials => nil)
|
434
|
-
@cache_manager = mock('cache_manager', :lookup => nil, :store => nil)
|
435
|
-
@accessor = mock('accessor', :auth_manager => @auth_manager, :cache_manager => @cache_manager)
|
436
511
|
@resource = Resourceful::Resource.new(@accessor, 'http://foo.invalid/')
|
437
|
-
@response = mock('response', :is_redirect? => false, :is_success? => true)
|
512
|
+
@response = mock('response', :is_redirect? => false, :is_success? => true, :is_not_authorized? => false, :code => 200)
|
438
513
|
@request = mock('request', :response => @response)
|
439
514
|
Resourceful::Request.stub!(:new).and_return(@request)
|
440
515
|
end
|
@@ -447,7 +522,10 @@ describe Resourceful::Resource do
|
|
447
522
|
|
448
523
|
it 'should put the content type in the header' do
|
449
524
|
Resourceful::Request.should_receive(:new).
|
450
|
-
with(anything,
|
525
|
+
with(anything,
|
526
|
+
anything,
|
527
|
+
anything,
|
528
|
+
hash_including(:content_type =>'text/plain')).
|
451
529
|
and_return(@request)
|
452
530
|
|
453
531
|
@resource.put("a body", :content_type => 'text/plain')
|
@@ -58,6 +58,45 @@ describe Resourceful::Response do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
describe '#is_unsuccesful?' do
|
62
|
+
it 'should be true for a 4xx series response code' do
|
63
|
+
Resourceful::Response.new(@uri, 404, {}, "").is_unsuccesful?.should == true
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should be true for a 5xx series response code' do
|
67
|
+
Resourceful::Response.new(@uri, 500, {}, "").is_unsuccesful?.should == true
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should be not true for a 2xx series response code' do
|
71
|
+
Resourceful::Response.new(@uri, 200, {}, "").is_unsuccesful?.should == false
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should be true for a 3xx series response code' do
|
75
|
+
Resourceful::Response.new(@uri, 302, {}, "").is_unsuccesful?.should == true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#is_client_error?' do
|
80
|
+
it 'be true for a 4xx series response code' do
|
81
|
+
Resourceful::Response.new(@uri, 404, {}, "").is_client_error?.should == true
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'be false for anything else' do
|
85
|
+
Resourceful::Response.new(@uri, 200, {}, "").is_client_error?.should == false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#is_server_error?' do
|
90
|
+
it 'be true for a 5xx series response code' do
|
91
|
+
Resourceful::Response.new(@uri, 500, {}, "").is_server_error?.should == true
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'be false for anything else' do
|
95
|
+
Resourceful::Response.new(@uri, 200, {}, "").is_server_error?.should == false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
61
100
|
it 'should know if it is a redirect' do
|
62
101
|
Resourceful::Response.new(@uri, 301, {}, "").is_redirect?.should == true
|
63
102
|
Resourceful::Response.new(@uri, 302, {}, "").is_redirect?.should == true
|
@@ -35,7 +35,7 @@ CodeResponder = lambda do |env|
|
|
35
35
|
[ code, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
36
36
|
end unless defined? CodeResponder
|
37
37
|
|
38
|
-
# YAML-parses the
|
38
|
+
# YAML-parses the query string (expected hash) and sets the header to that
|
39
39
|
HeaderResponder = lambda do |env|
|
40
40
|
header = YAML.load(URI.unescape(env['QUERY_STRING']))
|
41
41
|
body = [header.inspect]
|
@@ -48,6 +48,18 @@ HeaderResponder = lambda do |env|
|
|
48
48
|
[ 200, header, body ]
|
49
49
|
end unless defined? HeaderResponder
|
50
50
|
|
51
|
+
# Echos the request header in the response body
|
52
|
+
EchoHeaderResponder = lambda do |env|
|
53
|
+
body = [env.inspect]
|
54
|
+
|
55
|
+
header = {
|
56
|
+
'Content-Type' => 'text/plain',
|
57
|
+
'Content-Length' => body.join.size.to_s
|
58
|
+
}
|
59
|
+
|
60
|
+
[ 200, header, body ]
|
61
|
+
end unless defined? EchoHeaderResponder
|
62
|
+
|
51
63
|
# redirect. /redirect/{301|302}?{url}
|
52
64
|
Redirector = lambda do |env|
|
53
65
|
code = env['PATH_INFO'] =~ /([\d]+)/ ? Integer($1) : 404
|
@@ -114,7 +126,6 @@ describe 'simple http server', :shared => true do
|
|
114
126
|
#setup a thin http server we can connect to
|
115
127
|
require 'thin'
|
116
128
|
require 'rack'
|
117
|
-
require 'rack/lobster'
|
118
129
|
|
119
130
|
app = Rack::Builder.new do |env|
|
120
131
|
use Rack::ShowExceptions
|
@@ -128,6 +139,7 @@ describe 'simple http server', :shared => true do
|
|
128
139
|
map( '/code' ){ run CodeResponder }
|
129
140
|
map( '/redirect' ){ run Redirector }
|
130
141
|
map( '/header' ){ run HeaderResponder }
|
142
|
+
map( '/echo_header' ){ run EchoHeaderResponder }
|
131
143
|
map( '/modified' ){ run ModifiedResponder }
|
132
144
|
map( '/auth' ){ run AuthorizationResponder }
|
133
145
|
end
|
@@ -65,6 +65,14 @@ describe 'http server' do
|
|
65
65
|
resp[1]['Expire'].first.should_not =~ /%/
|
66
66
|
end
|
67
67
|
|
68
|
+
it 'should echo the request header in the response body' do
|
69
|
+
uri = URI.escape("http://localhost:3000/echo_header")
|
70
|
+
|
71
|
+
resp = Resourceful::NetHttpAdapter.make_request(:get, uri)
|
72
|
+
|
73
|
+
resp[2].should =~ /HTTP_HOST/
|
74
|
+
end
|
75
|
+
|
68
76
|
describe '/modified' do
|
69
77
|
it 'should be 200 if no I-M-S header' do
|
70
78
|
uri = URI.escape("http://localhost:3000/modified?#{(Time.now + 3600).httpdate}")
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
CHANGED
@@ -4,8 +4,11 @@ require 'spec'
|
|
4
4
|
require 'pp'
|
5
5
|
require 'facets'
|
6
6
|
|
7
|
-
|
7
|
+
#$LOAD_PATH << Pathname(__FILE__).dirname + "../lib"
|
8
|
+
$LOAD_PATH << File.dirname(__FILE__) + "../lib"
|
8
9
|
require 'resourceful/util'
|
10
|
+
require 'resourceful'
|
11
|
+
require 'resourceful/http_accessor'
|
9
12
|
|
10
13
|
require Pathname(__FILE__).dirname + 'simple_http_server_shared_spec'
|
11
14
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resourceful
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Sadauskas & Peter Williams
|
@@ -9,11 +9,12 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-07-31 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: addressable
|
17
|
+
type: :runtime
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
@@ -23,6 +24,7 @@ dependencies:
|
|
23
24
|
version:
|
24
25
|
- !ruby/object:Gem::Dependency
|
25
26
|
name: httpauth
|
27
|
+
type: :runtime
|
26
28
|
version_requirement:
|
27
29
|
version_requirements: !ruby/object:Gem::Requirement
|
28
30
|
requirements:
|
@@ -32,15 +34,7 @@ dependencies:
|
|
32
34
|
version:
|
33
35
|
- !ruby/object:Gem::Dependency
|
34
36
|
name: rspec
|
35
|
-
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: "0"
|
41
|
-
version:
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: thin
|
37
|
+
type: :runtime
|
44
38
|
version_requirement:
|
45
39
|
version_requirements: !ruby/object:Gem::Requirement
|
46
40
|
requirements:
|
@@ -50,6 +44,7 @@ dependencies:
|
|
50
44
|
version:
|
51
45
|
- !ruby/object:Gem::Dependency
|
52
46
|
name: facets
|
47
|
+
type: :runtime
|
53
48
|
version_requirement:
|
54
49
|
version_requirements: !ruby/object:Gem::Requirement
|
55
50
|
requirements:
|
@@ -70,6 +65,7 @@ files:
|
|
70
65
|
- README.markdown
|
71
66
|
- Rakefile
|
72
67
|
- spec/acceptance_shared_specs.rb
|
68
|
+
- spec/spec.opts
|
73
69
|
- spec/acceptance_spec.rb
|
74
70
|
- spec/simple_http_server_shared_spec_spec.rb
|
75
71
|
- spec/spec_helper.rb
|
@@ -121,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
117
|
requirements: []
|
122
118
|
|
123
119
|
rubyforge_project: resourceful
|
124
|
-
rubygems_version: 1.
|
120
|
+
rubygems_version: 1.2.0
|
125
121
|
signing_key:
|
126
122
|
specification_version: 2
|
127
123
|
summary: Resourceful provides a convenient Ruby API for making HTTP requests.
|