resourceful 0.2 → 0.2.1
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.
- 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.
|