resourceful 0.3.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +24 -28
- data/Rakefile +44 -14
- data/lib/resourceful.rb +11 -21
- data/lib/resourceful/authentication_manager.rb +3 -2
- data/lib/resourceful/cache_manager.rb +58 -1
- data/lib/resourceful/exceptions.rb +34 -0
- data/lib/resourceful/header.rb +95 -0
- data/lib/resourceful/http_accessor.rb +0 -2
- data/lib/resourceful/memcache_cache_manager.rb +3 -13
- data/lib/resourceful/net_http_adapter.rb +15 -5
- data/lib/resourceful/request.rb +180 -18
- data/lib/resourceful/resource.rb +38 -141
- data/lib/resourceful/response.rb +142 -95
- data/resourceful.gemspec +9 -7
- data/spec/acceptance/authorization_spec.rb +16 -0
- data/spec/acceptance/caching_spec.rb +192 -0
- data/spec/acceptance/header_spec.rb +24 -0
- data/spec/acceptance/redirecting_spec.rb +12 -0
- data/spec/acceptance/resource_spec.rb +84 -0
- data/spec/acceptance_shared_specs.rb +12 -17
- data/spec/{acceptance_spec.rb → old_acceptance_specs.rb} +27 -57
- data/spec/simple_sinatra_server.rb +74 -0
- data/spec/simple_sinatra_server_spec.rb +98 -0
- data/spec/spec_helper.rb +21 -7
- metadata +50 -42
- data/spec/resourceful/authentication_manager_spec.rb +0 -249
- data/spec/resourceful/cache_manager_spec.rb +0 -223
- data/spec/resourceful/header_spec.rb +0 -38
- data/spec/resourceful/http_accessor_spec.rb +0 -164
- data/spec/resourceful/memcache_cache_manager_spec.rb +0 -111
- data/spec/resourceful/net_http_adapter_spec.rb +0 -96
- data/spec/resourceful/options_interpreter_spec.rb +0 -102
- data/spec/resourceful/request_spec.rb +0 -186
- data/spec/resourceful/resource_spec.rb +0 -600
- data/spec/resourceful/response_spec.rb +0 -238
- data/spec/resourceful/stubbed_resource_proxy_spec.rb +0 -58
- data/spec/simple_http_server_shared_spec.rb +0 -162
- data/spec/simple_http_server_shared_spec_spec.rb +0 -212
@@ -1,238 +0,0 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
require Pathname(__FILE__).dirname + '../spec_helper'
|
3
|
-
|
4
|
-
require 'resourceful/response'
|
5
|
-
|
6
|
-
describe Resourceful::Response do
|
7
|
-
before do
|
8
|
-
@net_http = mock('net_http')
|
9
|
-
Net::HTTP::Get.stub!(:new).and_return(@net_http)
|
10
|
-
@uri = 'http://www.example.com'
|
11
|
-
|
12
|
-
@response = Resourceful::Response.new(@uri, 0, {}, "")
|
13
|
-
end
|
14
|
-
|
15
|
-
describe 'init' do
|
16
|
-
|
17
|
-
it 'should be instantiatable' do
|
18
|
-
@response.should be_instance_of(Resourceful::Response)
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'should take a [uri, code, header, body] array' do
|
22
|
-
r = Resourceful::Response.new(@uri, 200, {}, "")
|
23
|
-
r.code.should == 200
|
24
|
-
r.header.should == {}
|
25
|
-
r.body.should == ""
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'should have a code' do
|
31
|
-
@response.should respond_to(:code)
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'should have a header' do
|
35
|
-
@response.should respond_to(:header)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should have header aliased as headers' do
|
39
|
-
@response.should respond_to(:headers)
|
40
|
-
@response.headers.should == @response.header
|
41
|
-
end
|
42
|
-
|
43
|
-
describe "#is_success?" do
|
44
|
-
it 'should be true for 200' do
|
45
|
-
Resourceful::Response.new(@uri, 200, {}, "").is_success?.should == true
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'should be true for any 2xx' do
|
49
|
-
Resourceful::Response.new(@uri, 299, {}, "").is_success?.should == true
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'should not be true for 300' do
|
53
|
-
Resourceful::Response.new(@uri, 300, {}, "").is_success?.should == false
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'should not be true for 199' do
|
57
|
-
Resourceful::Response.new(@uri, 199, {}, "").is_success?.should == false
|
58
|
-
end
|
59
|
-
end
|
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
|
-
|
100
|
-
it 'should know if it is a redirect' do
|
101
|
-
Resourceful::Response.new(@uri, 301, {}, "").is_redirect?.should == true
|
102
|
-
Resourceful::Response.new(@uri, 302, {}, "").is_redirect?.should == true
|
103
|
-
Resourceful::Response.new(@uri, 303, {}, "").is_redirect?.should == true
|
104
|
-
Resourceful::Response.new(@uri, 307, {}, "").is_redirect?.should == true
|
105
|
-
|
106
|
-
#aliased as was_redirect?
|
107
|
-
Resourceful::Response.new(@uri, 301, {}, "").was_redirect?.should == true
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'should know if it is a permanent redirect' do
|
111
|
-
Resourceful::Response.new(@uri, 301, {}, "").is_permanent_redirect?.should == true
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'should know if it is a temporary redirect' do
|
115
|
-
Resourceful::Response.new(@uri, 303, {}, "").is_temporary_redirect?.should == true
|
116
|
-
end
|
117
|
-
|
118
|
-
it 'should know if its authoritative' do
|
119
|
-
@response.should respond_to(:authoritative?)
|
120
|
-
end
|
121
|
-
|
122
|
-
it 'should allow authoritative to be set' do
|
123
|
-
@response.authoritative = true
|
124
|
-
@response.authoritative?.should be_true
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'should know if it is authorized' do
|
128
|
-
@response.should respond_to(:is_not_authorized?)
|
129
|
-
Resourceful::Response.new(@uri, 200, {}, "").is_not_authorized?.should == false
|
130
|
-
Resourceful::Response.new(@uri, 401, {}, "").is_not_authorized?.should == true
|
131
|
-
end
|
132
|
-
|
133
|
-
describe 'caching and expiration' do
|
134
|
-
before do
|
135
|
-
Time.stub!(:now).and_return(Time.utc(2008,5,15,18,0,1), Time.utc(2008,5,15,20,0,0))
|
136
|
-
|
137
|
-
@response = Resourceful::Response.new(@uri, 0, {'Date' => ['Thu, 15 May 2008 18:00:00 GMT']}, "")
|
138
|
-
@response.request_time = Time.utc(2008,5,15,17,59,59)
|
139
|
-
end
|
140
|
-
|
141
|
-
it 'should know if its #stale?' do
|
142
|
-
@response.should respond_to(:stale?)
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'should be stale if it is expired' do
|
146
|
-
@response.should_receive(:expired?).and_return(true)
|
147
|
-
@response.should be_stale
|
148
|
-
end
|
149
|
-
|
150
|
-
it 'should know if its #expired?' do
|
151
|
-
@response.should respond_to(:expired?)
|
152
|
-
end
|
153
|
-
|
154
|
-
it 'should be expired if Now is after the "Expire" header' do
|
155
|
-
Time.stub!(:now).and_return(Time.utc(2008,5,23,18,0))
|
156
|
-
@response.header['Expire'] = [(Time.now - 60*60).httpdate]
|
157
|
-
|
158
|
-
@response.should be_expired
|
159
|
-
end
|
160
|
-
|
161
|
-
it 'should have a #current_age' do
|
162
|
-
@response.should respond_to(:current_age)
|
163
|
-
end
|
164
|
-
|
165
|
-
it 'should calculate the #current_age' do
|
166
|
-
@response.current_age.should == (2 * 60 * 60 + 2)
|
167
|
-
end
|
168
|
-
|
169
|
-
it 'should know if its #cachable?' do
|
170
|
-
@response.should respond_to(:cachable?)
|
171
|
-
end
|
172
|
-
|
173
|
-
it 'should normally be cachable' do
|
174
|
-
@response.cachable?.should be_true
|
175
|
-
end
|
176
|
-
|
177
|
-
def response_with_header(header = {})
|
178
|
-
Resourceful::Response.new(@uri, 200, header, "")
|
179
|
-
end
|
180
|
-
|
181
|
-
it 'should not be cachable if the vary header has "*"' do
|
182
|
-
r = response_with_header('Vary' => ['*'])
|
183
|
-
r.cachable?.should be_false
|
184
|
-
end
|
185
|
-
|
186
|
-
it 'should not be cachable if the Cache-Control header is set to no-store' do
|
187
|
-
r = response_with_header('Cache-Control' => ['no-store'])
|
188
|
-
r.cachable?.should be_false
|
189
|
-
end
|
190
|
-
|
191
|
-
it 'should be stale if the Cache-Control header is set to must-revalidate' do
|
192
|
-
r = response_with_header('Cache-Control' => ['must-revalidate'])
|
193
|
-
r.should be_stale
|
194
|
-
end
|
195
|
-
|
196
|
-
it 'should be stale if the Cache-Control header is set to no-cache' do
|
197
|
-
r = response_with_header('Cache-Control' => ['no-cache'])
|
198
|
-
r.should be_stale
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
|
203
|
-
describe '#body' do
|
204
|
-
it 'should have a body method' do
|
205
|
-
@response.should respond_to(:body)
|
206
|
-
end
|
207
|
-
|
208
|
-
require 'zlib'
|
209
|
-
['gzip', ' gzip', ' gzip ', 'GZIP', 'gzIP'].each do |gzip|
|
210
|
-
it "ungzip the body if content-encoding header field is #{gzip}" do
|
211
|
-
compressed_date = StringIO.new.tap do |out|
|
212
|
-
Zlib::GzipWriter.new(out).tap do |zout|
|
213
|
-
zout << "This is a test"
|
214
|
-
zout.close
|
215
|
-
end
|
216
|
-
end.string
|
217
|
-
|
218
|
-
@response = Resourceful::Response.new(@uri, 0, {'Content-Encoding' => [gzip]}, compressed_date)
|
219
|
-
|
220
|
-
@response.body.should == "This is a test"
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
it 'should leave body unmolested if Content-Encoding missing' do
|
225
|
-
@response = Resourceful::Response.new(@uri, 0, {}, "This is a test")
|
226
|
-
@response.body.should == "This is a test"
|
227
|
-
end
|
228
|
-
|
229
|
-
it 'should raise error if Content-Encoding is not supported' do
|
230
|
-
@response = Resourceful::Response.new(@uri, 0, {'Content-Encoding' => ['broken-identity']}, "This is a test")
|
231
|
-
lambda {
|
232
|
-
@response.body
|
233
|
-
}.should raise_error(Resourceful::UnsupportedContentCoding)
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
end
|
238
|
-
|
@@ -1,58 +0,0 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
require Pathname(__FILE__).dirname + '../spec_helper'
|
3
|
-
|
4
|
-
require 'resourceful/stubbed_resource_proxy'
|
5
|
-
|
6
|
-
describe Resourceful::StubbedResourceProxy, "init" do
|
7
|
-
it 'should require real resource' do
|
8
|
-
lambda{
|
9
|
-
Resourceful::StubbedResourceProxy.new
|
10
|
-
}.should raise_error(ArgumentError)
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'should require canned responses hash' do
|
14
|
-
lambda{
|
15
|
-
Resourceful::StubbedResourceProxy.new(stub('resource'))
|
16
|
-
}.should raise_error(ArgumentError)
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'should be creatable with a Resource and canned responses' do
|
20
|
-
Resourceful::StubbedResourceProxy.new(stub('resource'), {})
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe Resourceful::StubbedResourceProxy do
|
25
|
-
before do
|
26
|
-
@resource = stub('resource', :effective_uri => "http://test.invalid/foo")
|
27
|
-
@stubbed_resource = Resourceful::StubbedResourceProxy.
|
28
|
-
new(@resource, [{:mime_type => 'application/xml',
|
29
|
-
:body => '<thing>1</thing>'}])
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'should return canned response body for matching requests' do
|
33
|
-
resp = @stubbed_resource.get
|
34
|
-
resp.body.should == '<thing>1</thing>'
|
35
|
-
resp['content-type'].should == 'application/xml'
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should pass #get() through to base resource if no matching canned response is defined' do
|
39
|
-
@resource.should_receive(:get)
|
40
|
-
@stubbed_resource.get(:accept => 'application/unknown')
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'should pass #post() through to base resource if no canned response is defined' do
|
44
|
-
@resource.should_receive(:post)
|
45
|
-
@stubbed_resource.post
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'should pass #put() through to base resource if no canned response is defined' do
|
49
|
-
@resource.should_receive(:put)
|
50
|
-
@stubbed_resource.put
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'should pass #effective_uri() through to base resource if no canned response is defined' do
|
54
|
-
@resource.should_receive(:effective_uri)
|
55
|
-
@stubbed_resource.effective_uri
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
@@ -1,162 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
|
-
# this sets up a very simple http server using thin to be used in specs.
|
4
|
-
SimpleGet = lambda do |env|
|
5
|
-
body = ["Hello, world!"]
|
6
|
-
[ 200, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
7
|
-
end unless defined? SimpleGet
|
8
|
-
|
9
|
-
SimplePost = lambda do |env|
|
10
|
-
body = [env['rack.input'].string]
|
11
|
-
[ 201, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
12
|
-
end unless defined? SimplePost
|
13
|
-
|
14
|
-
SimplePut = lambda do |env|
|
15
|
-
body = [env['rack.input'].string]
|
16
|
-
[ 200, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
17
|
-
end unless defined? SimplePut
|
18
|
-
|
19
|
-
SimpleDel = lambda do |env|
|
20
|
-
body = ["KABOOM!"]
|
21
|
-
[ 200, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
22
|
-
end unless defined? SimpleDel
|
23
|
-
|
24
|
-
# has the method used in the body of the response
|
25
|
-
MethodResponder = lambda do |env|
|
26
|
-
body = [env['REQUEST_METHOD']]
|
27
|
-
[ 200, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
28
|
-
end unless defined? MethodResponder
|
29
|
-
|
30
|
-
# has a response code of whatever it was given in the url /code/{123}
|
31
|
-
CodeResponder = lambda do |env|
|
32
|
-
code = env['PATH_INFO'] =~ /([\d]+)/ ? Integer($1) : 404
|
33
|
-
body = [code.to_s]
|
34
|
-
|
35
|
-
[ code, {'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}, body ]
|
36
|
-
end unless defined? CodeResponder
|
37
|
-
|
38
|
-
# YAML-parses the query string (expected hash) and sets the header to that
|
39
|
-
HeaderResponder = lambda do |env|
|
40
|
-
header = YAML.load(URI.unescape(env['QUERY_STRING']))
|
41
|
-
body = [header.inspect]
|
42
|
-
|
43
|
-
header.merge!({
|
44
|
-
'Content-Type' => 'text/plain',
|
45
|
-
'Content-Length' => body.join.size.to_s
|
46
|
-
})
|
47
|
-
|
48
|
-
[ 200, header, body ]
|
49
|
-
end unless defined? HeaderResponder
|
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
|
-
|
63
|
-
# redirect. /redirect/{301|302}?{url}
|
64
|
-
Redirector = lambda do |env|
|
65
|
-
code = env['PATH_INFO'] =~ /([\d]+)/ ? Integer($1) : 404
|
66
|
-
location = env['QUERY_STRING']
|
67
|
-
body = [location]
|
68
|
-
|
69
|
-
[ code, {'Content-Type' => 'text/plain', 'Location' => location, 'Content-Length' => body.join.size.to_s}, body ]
|
70
|
-
end unless defined? Redirector
|
71
|
-
|
72
|
-
# Returns 304 if 'If-Modified-Since' is after given mod time
|
73
|
-
ModifiedResponder = lambda do |env|
|
74
|
-
modtime = Time.httpdate(URI.unescape(env['QUERY_STRING']))
|
75
|
-
|
76
|
-
code = 200
|
77
|
-
if env['HTTP_IF_MODIFIED_SINCE']
|
78
|
-
code = 304
|
79
|
-
end
|
80
|
-
body = [modtime.to_s]
|
81
|
-
|
82
|
-
header = {'Content-Type' => 'text/plain',
|
83
|
-
'Content-Length' => body.join.size.to_s,
|
84
|
-
'Last-Modified' => modtime.httpdate,
|
85
|
-
'Cache-Control' => 'must-revalidate'}
|
86
|
-
|
87
|
-
[ code, header, body ]
|
88
|
-
end unless defined? ModifiedResponder
|
89
|
-
|
90
|
-
require 'rubygems'
|
91
|
-
require 'httpauth'
|
92
|
-
AuthorizationResponder = lambda do |env|
|
93
|
-
authtype = env['QUERY_STRING']
|
94
|
-
header = {}
|
95
|
-
if auth_string = env['HTTP_AUTHORIZATION']
|
96
|
-
if authtype == "basic" &&
|
97
|
-
['admin', 'secret'] == HTTPAuth::Basic.unpack_authorization(auth_string)
|
98
|
-
code = 200
|
99
|
-
body = ["Authorized"]
|
100
|
-
elsif authtype == "digest" #&&
|
101
|
-
credentials = HTTPAuth::Digest::Credentials.from_header(auth_string) &&
|
102
|
-
credentials &&
|
103
|
-
credentials.validate(:password => 'secret', :method => 'GET')
|
104
|
-
code = 200
|
105
|
-
body = ["Authorized"]
|
106
|
-
else
|
107
|
-
code = 401
|
108
|
-
body = ["Not Authorized"]
|
109
|
-
end
|
110
|
-
else
|
111
|
-
code = 401
|
112
|
-
body = ["Not Authorized"]
|
113
|
-
if authtype == "basic"
|
114
|
-
header = {'WWW-Authenticate' => HTTPAuth::Basic.pack_challenge('Test Auth')}
|
115
|
-
elsif authtype == "digest"
|
116
|
-
chal = HTTPAuth::Digest::Credentials.new(:realm => 'Test Auth', :qop => 'auth')
|
117
|
-
header = {'WWW-Authenticate' => chal.to_header}
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
[ code, header.merge({'Content-Type' => 'text/plain', 'Content-Length' => body.join.size.to_s}), body ]
|
122
|
-
end unless defined? AuthorizationResponder
|
123
|
-
|
124
|
-
describe 'simple http server', :shared => true do
|
125
|
-
before(:all) do
|
126
|
-
require 'rack'
|
127
|
-
require 'thin'
|
128
|
-
|
129
|
-
app = Rack::Builder.new do |env|
|
130
|
-
use Rack::ShowExceptions
|
131
|
-
|
132
|
-
map( '/get' ){ run SimpleGet }
|
133
|
-
map( '/post' ){ run SimplePost }
|
134
|
-
map( '/put' ){ run SimplePut }
|
135
|
-
map( '/delete' ){ run SimpleDel }
|
136
|
-
|
137
|
-
map( '/method' ){ run MethodResponder }
|
138
|
-
map( '/code' ){ run CodeResponder }
|
139
|
-
map( '/redirect' ){ run Redirector }
|
140
|
-
map( '/header' ){ run HeaderResponder }
|
141
|
-
map( '/echo_header' ){ run EchoHeaderResponder }
|
142
|
-
map( '/modified' ){ run ModifiedResponder }
|
143
|
-
map( '/auth' ){ run AuthorizationResponder }
|
144
|
-
end
|
145
|
-
|
146
|
-
#spawn the server in a separate thread
|
147
|
-
@httpd = Thread.new do
|
148
|
-
Thin::Logging.silent = true
|
149
|
-
# Thin::Logging.debug = true
|
150
|
-
Thin::Server.start(app)
|
151
|
-
end
|
152
|
-
#give the server a chance to initialize
|
153
|
-
sleep 0.05
|
154
|
-
end
|
155
|
-
|
156
|
-
after(:all) do
|
157
|
-
# kill the server thread
|
158
|
-
@httpd.exit
|
159
|
-
end
|
160
|
-
|
161
|
-
|
162
|
-
end
|