entp-astrotrain 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +26 -0
- data/LICENSE +20 -0
- data/README +47 -0
- data/Rakefile +145 -0
- data/VERSION +1 -0
- data/astrotrain.gemspec +96 -0
- data/config/sample.rb +12 -0
- data/lib/astrotrain/api.rb +53 -0
- data/lib/astrotrain/logged_mail.rb +41 -0
- data/lib/astrotrain/mapping/http_post.rb +18 -0
- data/lib/astrotrain/mapping/jabber.rb +23 -0
- data/lib/astrotrain/mapping/transport.rb +55 -0
- data/lib/astrotrain/mapping.rb +157 -0
- data/lib/astrotrain/message.rb +313 -0
- data/lib/astrotrain/tmail.rb +48 -0
- data/lib/astrotrain.rb +55 -0
- data/lib/vendor/rest-client/README.rdoc +104 -0
- data/lib/vendor/rest-client/Rakefile +84 -0
- data/lib/vendor/rest-client/bin/restclient +65 -0
- data/lib/vendor/rest-client/foo.diff +66 -0
- data/lib/vendor/rest-client/lib/rest_client/net_http_ext.rb +21 -0
- data/lib/vendor/rest-client/lib/rest_client/payload.rb +185 -0
- data/lib/vendor/rest-client/lib/rest_client/request_errors.rb +75 -0
- data/lib/vendor/rest-client/lib/rest_client/resource.rb +103 -0
- data/lib/vendor/rest-client/lib/rest_client.rb +189 -0
- data/lib/vendor/rest-client/rest-client.gemspec +18 -0
- data/lib/vendor/rest-client/spec/base.rb +5 -0
- data/lib/vendor/rest-client/spec/master_shake.jpg +0 -0
- data/lib/vendor/rest-client/spec/payload_spec.rb +71 -0
- data/lib/vendor/rest-client/spec/request_errors_spec.rb +44 -0
- data/lib/vendor/rest-client/spec/resource_spec.rb +52 -0
- data/lib/vendor/rest-client/spec/rest_client_spec.rb +219 -0
- data/tasks/doc.thor +149 -0
- data/tasks/merb.thor +2020 -0
- data/test/api_test.rb +28 -0
- data/test/fixtures/apple_multipart.txt +100 -0
- data/test/fixtures/basic.txt +14 -0
- data/test/fixtures/custom.txt +15 -0
- data/test/fixtures/fwd.txt +0 -0
- data/test/fixtures/gb2312_encoding.txt +16 -0
- data/test/fixtures/gb2312_encoding_invalid.txt +15 -0
- data/test/fixtures/html.txt +16 -0
- data/test/fixtures/iso-8859-1.txt +13 -0
- data/test/fixtures/mapped.txt +13 -0
- data/test/fixtures/multipart.txt +213 -0
- data/test/fixtures/multipart2.txt +213 -0
- data/test/fixtures/multiple.txt +13 -0
- data/test/fixtures/multiple_delivered_to.txt +14 -0
- data/test/fixtures/multiple_with_body_recipients.txt +15 -0
- data/test/fixtures/reply.txt +16 -0
- data/test/fixtures/utf-8.txt +13 -0
- data/test/logged_mail_test.rb +63 -0
- data/test/mapping_test.rb +129 -0
- data/test/message_test.rb +424 -0
- data/test/test_helper.rb +54 -0
- data/test/transport_test.rb +111 -0
- metadata +115 -0
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + '/rest_client/resource'
|
5
|
+
require File.dirname(__FILE__) + '/rest_client/request_errors'
|
6
|
+
require File.dirname(__FILE__) + '/rest_client/payload'
|
7
|
+
require File.dirname(__FILE__) + '/rest_client/net_http_ext'
|
8
|
+
|
9
|
+
# This module's static methods are the entry point for using the REST client.
|
10
|
+
#
|
11
|
+
# # GET
|
12
|
+
# xml = RestClient.get 'http://example.com/resource'
|
13
|
+
# jpg = RestClient.get 'http://example.com/resource', :accept => 'image/jpg'
|
14
|
+
#
|
15
|
+
# # authentication and SSL
|
16
|
+
# RestClient.get 'https://user:password@example.com/private/resource'
|
17
|
+
#
|
18
|
+
# # POST or PUT with a hash sends parameters as a urlencoded form body
|
19
|
+
# RestClient.post 'http://example.com/resource', :param1 => 'one'
|
20
|
+
#
|
21
|
+
# # nest hash parameters
|
22
|
+
# RestClient.post 'http://example.com/resource', :nested => { :param1 => 'one' }
|
23
|
+
#
|
24
|
+
# # POST and PUT with raw payloads
|
25
|
+
# RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain'
|
26
|
+
# RestClient.post 'http://example.com/resource.xml', xml_doc
|
27
|
+
# RestClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf'
|
28
|
+
#
|
29
|
+
# # DELETE
|
30
|
+
# RestClient.delete 'http://example.com/resource'
|
31
|
+
#
|
32
|
+
# For live tests of RestClient, try using http://rest-test.heroku.com, which echoes back information about the rest call:
|
33
|
+
#
|
34
|
+
# >> RestClient.put 'http://rest-test.heroku.com/resource', :foo => 'baz'
|
35
|
+
# => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
|
36
|
+
#
|
37
|
+
module RestClient
|
38
|
+
def self.get(url, headers={}, &b)
|
39
|
+
Request.execute(:method => :get,
|
40
|
+
:url => url,
|
41
|
+
:headers => headers, &b)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.post(url, payload, headers={}, &b)
|
45
|
+
Request.execute(:method => :post,
|
46
|
+
:url => url,
|
47
|
+
:payload => payload,
|
48
|
+
:headers => headers, &b)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.put(url, payload, headers={}, &b)
|
52
|
+
Request.execute(:method => :put,
|
53
|
+
:url => url,
|
54
|
+
:payload => payload,
|
55
|
+
:headers => headers, &b)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.delete(url, headers={}, &b)
|
59
|
+
Request.execute(:method => :delete,
|
60
|
+
:url => url,
|
61
|
+
:headers => headers, &b)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Internal class used to build and execute the request.
|
65
|
+
class Request
|
66
|
+
attr_reader :method, :url, :headers, :user, :password
|
67
|
+
|
68
|
+
def self.execute(args, &b)
|
69
|
+
new(args).execute(&b)
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(args)
|
73
|
+
@method = args[:method] or raise ArgumentError, "must pass :method"
|
74
|
+
@url = args[:url] or raise ArgumentError, "must pass :url"
|
75
|
+
@payload = Payload.generate(args[:payload] || '')
|
76
|
+
@headers = args[:headers] || {}
|
77
|
+
@user = args[:user]
|
78
|
+
@password = args[:password]
|
79
|
+
end
|
80
|
+
|
81
|
+
def execute(&b)
|
82
|
+
execute_inner(&b)
|
83
|
+
rescue Redirect => e
|
84
|
+
@url = e.url
|
85
|
+
execute(&b)
|
86
|
+
end
|
87
|
+
|
88
|
+
def execute_inner(&b)
|
89
|
+
uri = parse_url_with_auth(url)
|
90
|
+
transmit(uri, net_http_class(method).new(uri.request_uri, make_headers(headers)), payload, &b)
|
91
|
+
end
|
92
|
+
|
93
|
+
def make_headers(user_headers)
|
94
|
+
final = {}
|
95
|
+
merged = default_headers.merge(user_headers)
|
96
|
+
merged.keys.each do |key|
|
97
|
+
final[key.to_s.gsub(/_/, '-').capitalize] = merged[key]
|
98
|
+
end
|
99
|
+
final
|
100
|
+
end
|
101
|
+
|
102
|
+
def net_http_class(method)
|
103
|
+
Net::HTTP.const_get(method.to_s.capitalize)
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_url(url)
|
107
|
+
url = "http://#{url}" unless url.match(/^http/)
|
108
|
+
URI.parse(url)
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse_url_with_auth(url)
|
112
|
+
uri = parse_url(url)
|
113
|
+
@user = uri.user if uri.user
|
114
|
+
@password = uri.password if uri.password
|
115
|
+
uri
|
116
|
+
end
|
117
|
+
|
118
|
+
def process_payload(p=nil, parent_key=nil)
|
119
|
+
unless p.is_a?(Hash)
|
120
|
+
p
|
121
|
+
else
|
122
|
+
@headers[:content_type] ||= 'application/x-www-form-urlencoded'
|
123
|
+
p.keys.map do |k|
|
124
|
+
key = parent_key ? "#{parent_key}[#{k}]" : k
|
125
|
+
if p[k].is_a? Hash
|
126
|
+
process_payload(p[k], key)
|
127
|
+
else
|
128
|
+
value = URI.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
129
|
+
"#{key}=#{value}"
|
130
|
+
end
|
131
|
+
end.join("&")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def transmit(uri, req, payload, &b)
|
136
|
+
setup_credentials(req)
|
137
|
+
|
138
|
+
net = Net::HTTP.new(uri.host, uri.port)
|
139
|
+
net.use_ssl = uri.is_a?(URI::HTTPS)
|
140
|
+
|
141
|
+
net.start do |http|
|
142
|
+
## Ok. I know this is weird but it's a hack for now
|
143
|
+
## this lets process_result determine if it should read the body
|
144
|
+
## into memory or not
|
145
|
+
process_result(http.request(req, payload || "", &b), &b)
|
146
|
+
end
|
147
|
+
rescue EOFError
|
148
|
+
raise RestClient::ServerBrokeConnection
|
149
|
+
rescue Timeout::Error
|
150
|
+
raise RestClient::RequestTimeout
|
151
|
+
ensure
|
152
|
+
payload.close
|
153
|
+
end
|
154
|
+
|
155
|
+
def setup_credentials(req)
|
156
|
+
req.basic_auth(user, password) if user
|
157
|
+
end
|
158
|
+
|
159
|
+
def process_result(res, &b)
|
160
|
+
if %w(200 201 202).include? res.code
|
161
|
+
return res.body unless b
|
162
|
+
elsif %w(301 302 303).include? res.code
|
163
|
+
url = res.header['Location']
|
164
|
+
|
165
|
+
if url !~ /^http/
|
166
|
+
uri = URI.parse(@url)
|
167
|
+
uri.path = "/#{url}".squeeze('/')
|
168
|
+
url = uri.to_s
|
169
|
+
end
|
170
|
+
|
171
|
+
raise Redirect, url
|
172
|
+
elsif res.code == "401"
|
173
|
+
raise Unauthorized
|
174
|
+
elsif res.code == "404"
|
175
|
+
raise ResourceNotFound
|
176
|
+
else
|
177
|
+
raise RequestFailed, res
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def payload
|
182
|
+
@payload
|
183
|
+
end
|
184
|
+
|
185
|
+
def default_headers
|
186
|
+
@payload.headers.merge({ :accept => 'application/xml' })
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "rest-client"
|
3
|
+
s.version = "0.6.2"
|
4
|
+
s.summary = "Simple REST client for Ruby, inspired by microframework syntax for specifying actions."
|
5
|
+
s.description = "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
|
6
|
+
s.author = "Adam Wiggins"
|
7
|
+
s.email = "adam@heroku.com"
|
8
|
+
s.rubyforge_project = "rest-client"
|
9
|
+
s.homepage = "http://rest-client.heroku.com/"
|
10
|
+
s.has_rdoc = true
|
11
|
+
s.platform = Gem::Platform::RUBY
|
12
|
+
s.files = %w(Rakefile README.rdoc rest-client.gemspec
|
13
|
+
lib/request_errors.rb lib/resource.rb lib/rest_client.rb
|
14
|
+
spec/base.rb spec/request_errors_spec.rb spec/resource_spec.rb spec/rest_client_spec.rb
|
15
|
+
bin/restclient)
|
16
|
+
s.executables = ['restclient']
|
17
|
+
s.require_path = "lib"
|
18
|
+
end
|
Binary file
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/base"
|
2
|
+
|
3
|
+
describe RestClient::Payload do
|
4
|
+
context "A regular Payload" do
|
5
|
+
it "should should default content-type to standard enctype" do
|
6
|
+
RestClient::Payload::UrlEncoded.new({}).headers['Content-Type'].
|
7
|
+
should == 'application/x-www-form-urlencoded'
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should form properly encoded params" do
|
11
|
+
RestClient::Payload::UrlEncoded.new({:foo => 'bar'}).to_s.
|
12
|
+
should == "foo=bar"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "A multipart Payload" do
|
17
|
+
it "should should default content-type to standard enctype" do
|
18
|
+
m = RestClient::Payload::Multipart.new({})
|
19
|
+
m.stub!(:boundary).and_return(123)
|
20
|
+
m.headers['Content-Type'].should == 'multipart/form-data; boundary="123"'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should form properly seperated multipart data" do
|
24
|
+
m = RestClient::Payload::Multipart.new({:foo => "bar", :bar => 'foo'})
|
25
|
+
m.to_s.should == <<-EOS
|
26
|
+
--#{m.boundary}\r
|
27
|
+
Content-Disposition: multipart/form-data; name="bar"\r
|
28
|
+
\r
|
29
|
+
foo\r
|
30
|
+
--#{m.boundary}\r
|
31
|
+
Content-Disposition: multipart/form-data; name="foo"\r
|
32
|
+
\r
|
33
|
+
bar\r
|
34
|
+
--#{m.boundary}--\r
|
35
|
+
EOS
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should form properly seperated multipart data" do
|
39
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
40
|
+
m = RestClient::Payload::Multipart.new({:foo => f})
|
41
|
+
m.to_s.should == <<-EOS
|
42
|
+
--#{m.boundary}\r
|
43
|
+
Content-Disposition: multipart/form-data; name="foo"; filename="./spec/master_shake.jpg"\r
|
44
|
+
Content-Type: image/jpeg\r
|
45
|
+
\r
|
46
|
+
#{IO.read(f.path)}\r
|
47
|
+
--#{m.boundary}--\r
|
48
|
+
EOS
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "Payload generation" do
|
53
|
+
it "should recognize standard urlencoded params" do
|
54
|
+
RestClient::Payload.generate({"foo" => 'bar'}).should be_kind_of(RestClient::Payload::UrlEncoded)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should recognize multipart params" do
|
58
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
59
|
+
|
60
|
+
RestClient::Payload.generate({"foo" => f}).should be_kind_of(RestClient::Payload::Multipart)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should be multipart if forced" do
|
64
|
+
RestClient::Payload.generate({"foo" => "bar", :multipart => true}).should be_kind_of(RestClient::Payload::Multipart)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should return data if no of the above" do
|
68
|
+
RestClient::Payload.generate("data").should be_kind_of(RestClient::Payload::Base)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe RestClient::Exception do
|
4
|
+
it "sets the exception message to ErrorMessage" do
|
5
|
+
RestClient::ResourceNotFound.new.message.should == 'Resource not found'
|
6
|
+
end
|
7
|
+
|
8
|
+
it "contains exceptions in RestClient" do
|
9
|
+
RestClient::Unauthorized.new.should be_a_kind_of(RestClient::Exception)
|
10
|
+
RestClient::ServerBrokeConnection.new.should be_a_kind_of(RestClient::Exception)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe RestClient::RequestFailed do
|
15
|
+
it "stores the http response on the exception" do
|
16
|
+
begin
|
17
|
+
raise RestClient::RequestFailed, :response
|
18
|
+
rescue RestClient::RequestFailed => e
|
19
|
+
e.response.should == :response
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "http_code convenience method for fetching the code as an integer" do
|
24
|
+
RestClient::RequestFailed.new(mock('res', :code => '502')).http_code.should == 502
|
25
|
+
end
|
26
|
+
|
27
|
+
it "shows the status code in the message" do
|
28
|
+
RestClient::RequestFailed.new(mock('res', :code => '502')).to_s.should match(/502/)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "backwards compatibility" do
|
33
|
+
it "alias RestClient::Request::Redirect to RestClient::Redirect" do
|
34
|
+
RestClient::Request::Redirect.should == RestClient::Redirect
|
35
|
+
end
|
36
|
+
|
37
|
+
it "alias RestClient::Request::Unauthorized to RestClient::Unauthorized" do
|
38
|
+
RestClient::Request::Unauthorized.should == RestClient::Unauthorized
|
39
|
+
end
|
40
|
+
|
41
|
+
it "alias RestClient::Request::RequestFailed to RestClient::RequestFailed" do
|
42
|
+
RestClient::Request::RequestFailed.should == RestClient::RequestFailed
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe RestClient::Resource do
|
4
|
+
before do
|
5
|
+
@resource = RestClient::Resource.new('http://some/resource', 'jane', 'mypass')
|
6
|
+
end
|
7
|
+
|
8
|
+
it "GET" do
|
9
|
+
RestClient::Request.should_receive(:execute).with(:method => :get, :url => 'http://some/resource', :headers => {}, :user => 'jane', :password => 'mypass')
|
10
|
+
@resource.get
|
11
|
+
end
|
12
|
+
|
13
|
+
it "POST" do
|
14
|
+
RestClient::Request.should_receive(:execute).with(:method => :post, :url => 'http://some/resource', :payload => 'abc', :headers => { :content_type => 'image/jpg' }, :user => 'jane', :password => 'mypass')
|
15
|
+
@resource.post 'abc', :content_type => 'image/jpg'
|
16
|
+
end
|
17
|
+
|
18
|
+
it "PUT" do
|
19
|
+
RestClient::Request.should_receive(:execute).with(:method => :put, :url => 'http://some/resource', :payload => 'abc', :headers => { :content_type => 'image/jpg' }, :user => 'jane', :password => 'mypass')
|
20
|
+
@resource.put 'abc', :content_type => 'image/jpg'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "DELETE" do
|
24
|
+
RestClient::Request.should_receive(:execute).with(:method => :delete, :url => 'http://some/resource', :headers => {}, :user => 'jane', :password => 'mypass')
|
25
|
+
@resource.delete
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can instantiate with no user/password" do
|
29
|
+
@resource = RestClient::Resource.new('http://some/resource')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "concatinates urls, inserting a slash when it needs one" do
|
33
|
+
@resource.concat_urls('http://example.com', 'resource').should == 'http://example.com/resource'
|
34
|
+
end
|
35
|
+
|
36
|
+
it "concatinates urls, using no slash if the first url ends with a slash" do
|
37
|
+
@resource.concat_urls('http://example.com/', 'resource').should == 'http://example.com/resource'
|
38
|
+
end
|
39
|
+
|
40
|
+
it "concatinates urls, using no slash if the second url starts with a slash" do
|
41
|
+
@resource.concat_urls('http://example.com', '/resource').should == 'http://example.com/resource'
|
42
|
+
end
|
43
|
+
|
44
|
+
it "concatinates even non-string urls, :posts + 1 => 'posts/1'" do
|
45
|
+
@resource.concat_urls(:posts, 1).should == 'posts/1'
|
46
|
+
end
|
47
|
+
|
48
|
+
it "offers subresources via []" do
|
49
|
+
parent = RestClient::Resource.new('http://example.com')
|
50
|
+
parent['posts'].url.should == 'http://example.com/posts'
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
def generate_payload(v)
|
4
|
+
RestClient::Payload::Base.new(v)
|
5
|
+
end
|
6
|
+
|
7
|
+
describe RestClient do
|
8
|
+
context "public API" do
|
9
|
+
it "GET" do
|
10
|
+
RestClient::Request.should_receive(:execute).with(:method => :get, :url => 'http://some/resource', :headers => {})
|
11
|
+
RestClient.get('http://some/resource')
|
12
|
+
end
|
13
|
+
|
14
|
+
it "POST" do
|
15
|
+
RestClient::Request.should_receive(:execute).with(:method => :post, :url => 'http://some/resource', :payload => 'payload', :headers => {})
|
16
|
+
RestClient.post('http://some/resource', 'payload')
|
17
|
+
end
|
18
|
+
|
19
|
+
it "PUT" do
|
20
|
+
RestClient::Request.should_receive(:execute).with(:method => :put, :url => 'http://some/resource', :payload => 'payload', :headers => {})
|
21
|
+
RestClient.put('http://some/resource', 'payload')
|
22
|
+
end
|
23
|
+
|
24
|
+
it "DELETE" do
|
25
|
+
RestClient::Request.should_receive(:execute).with(:method => :delete, :url => 'http://some/resource', :headers => {})
|
26
|
+
RestClient.delete('http://some/resource')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context RestClient::Request do
|
31
|
+
before do
|
32
|
+
@request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
|
33
|
+
|
34
|
+
@uri = mock("uri")
|
35
|
+
@uri.stub!(:request_uri).and_return('/resource')
|
36
|
+
@uri.stub!(:host).and_return('some')
|
37
|
+
@uri.stub!(:port).and_return(80)
|
38
|
+
|
39
|
+
@net = mock("net::http base")
|
40
|
+
@http = mock("net::http connection")
|
41
|
+
Net::HTTP.stub!(:new).and_return(@net)
|
42
|
+
@net.stub!(:start).and_yield(@http)
|
43
|
+
@net.stub!(:use_ssl=)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "requests xml mimetype" do
|
47
|
+
@request.default_headers[:accept].should == 'application/xml'
|
48
|
+
end
|
49
|
+
|
50
|
+
it "processes a successful result" do
|
51
|
+
res = mock("result")
|
52
|
+
res.stub!(:code).and_return("200")
|
53
|
+
res.stub!(:body).and_return('body')
|
54
|
+
@request.process_result(res).should == 'body'
|
55
|
+
end
|
56
|
+
|
57
|
+
it "parses a url into a URI object" do
|
58
|
+
URI.should_receive(:parse).with('http://example.com/resource')
|
59
|
+
@request.parse_url('http://example.com/resource')
|
60
|
+
end
|
61
|
+
|
62
|
+
it "adds http:// to the front of resources specified in the syntax example.com/resource" do
|
63
|
+
URI.should_receive(:parse).with('http://example.com/resource')
|
64
|
+
@request.parse_url('example.com/resource')
|
65
|
+
end
|
66
|
+
|
67
|
+
it "extracts the username and password when parsing http://user:password@example.com/" do
|
68
|
+
URI.stub!(:parse).and_return(mock('uri', :user => 'joe', :password => 'pass1'))
|
69
|
+
@request.parse_url_with_auth('http://joe:pass1@example.com/resource')
|
70
|
+
@request.user.should == 'joe'
|
71
|
+
@request.password.should == 'pass1'
|
72
|
+
end
|
73
|
+
|
74
|
+
it "doesn't overwrite user and password (which may have already been set by the Resource constructor) if there is no user/password in the url" do
|
75
|
+
URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
|
76
|
+
@request = RestClient::Request.new(:method => 'get', :url => 'example.com', :user => 'beth', :password => 'pass2')
|
77
|
+
@request.parse_url_with_auth('http://example.com/resource')
|
78
|
+
@request.user.should == 'beth'
|
79
|
+
@request.password.should == 'pass2'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "determines the Net::HTTP class to instantiate by the method name" do
|
83
|
+
@request.net_http_class(:put).should == Net::HTTP::Put
|
84
|
+
end
|
85
|
+
|
86
|
+
it "merges user headers with the default headers" do
|
87
|
+
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
88
|
+
@request.make_headers('3' => '4').should == { '1' => '2', '3' => '4' }
|
89
|
+
end
|
90
|
+
|
91
|
+
it "prefers the user header when the same header exists in the defaults" do
|
92
|
+
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
93
|
+
@request.make_headers('1' => '3').should == { '1' => '3' }
|
94
|
+
end
|
95
|
+
|
96
|
+
it "converts header symbols from :content_type to 'Content-type'" do
|
97
|
+
@request.should_receive(:default_headers).and_return({})
|
98
|
+
@request.make_headers(:content_type => 'abc').should == { 'Content-type' => 'abc' }
|
99
|
+
end
|
100
|
+
|
101
|
+
it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
|
102
|
+
@request.should_receive(:parse_url_with_auth).with('http://some/resource').and_return(@uri)
|
103
|
+
klass = mock("net:http class")
|
104
|
+
@request.should_receive(:net_http_class).with(:put).and_return(klass)
|
105
|
+
klass.should_receive(:new).and_return('result')
|
106
|
+
@request.should_receive(:transmit).with(@uri, 'result', be_kind_of(RestClient::Payload::Base))
|
107
|
+
@request.execute_inner
|
108
|
+
end
|
109
|
+
|
110
|
+
it "transmits the request with Net::HTTP" do
|
111
|
+
@http.should_receive(:request).with('req', be_kind_of(RestClient::Payload::Base))
|
112
|
+
@request.should_receive(:process_result)
|
113
|
+
@request.transmit(@uri, 'req', generate_payload('payload'))
|
114
|
+
end
|
115
|
+
|
116
|
+
it "uses SSL when the URI refers to a https address" do
|
117
|
+
@uri.stub!(:is_a?).with(URI::HTTPS).and_return(true)
|
118
|
+
@net.should_receive(:use_ssl=).with(true)
|
119
|
+
@http.stub!(:request)
|
120
|
+
@request.stub!(:process_result)
|
121
|
+
@request.transmit(@uri, 'req', generate_payload('payload'))
|
122
|
+
end
|
123
|
+
|
124
|
+
it "passes non-hash payloads straight through" do
|
125
|
+
@request.process_payload("x").should == "x"
|
126
|
+
end
|
127
|
+
|
128
|
+
it "converts a hash payload to urlencoded data" do
|
129
|
+
@request.process_payload(:a => 'b c+d').should == "a=b%20c%2Bd"
|
130
|
+
end
|
131
|
+
|
132
|
+
it "accepts nested hashes in payload" do
|
133
|
+
payload = @request.process_payload(:user => { :name => 'joe', :location => { :country => 'USA', :state => 'CA' }})
|
134
|
+
payload.should include('user[name]=joe')
|
135
|
+
payload.should include('user[location][country]=USA')
|
136
|
+
payload.should include('user[location][state]=CA')
|
137
|
+
end
|
138
|
+
|
139
|
+
it "set urlencoded content_type header on hash payloads" do
|
140
|
+
@request.process_payload(:a => 1)
|
141
|
+
@request.headers[:content_type].should == 'application/x-www-form-urlencoded'
|
142
|
+
end
|
143
|
+
|
144
|
+
it "sets up the credentials prior to the request" do
|
145
|
+
@http.stub!(:request)
|
146
|
+
@request.stub!(:process_result)
|
147
|
+
|
148
|
+
@request.stub!(:user).and_return('joe')
|
149
|
+
@request.stub!(:password).and_return('mypass')
|
150
|
+
@request.should_receive(:setup_credentials).with('req')
|
151
|
+
|
152
|
+
@request.transmit(@uri, 'req', generate_payload(''))
|
153
|
+
end
|
154
|
+
|
155
|
+
it "does not attempt to send any credentials if user is nil" do
|
156
|
+
@request.stub!(:user).and_return(nil)
|
157
|
+
req = mock("request")
|
158
|
+
req.should_not_receive(:basic_auth)
|
159
|
+
@request.setup_credentials(req)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "setup credentials when there's a user" do
|
163
|
+
@request.stub!(:user).and_return('joe')
|
164
|
+
@request.stub!(:password).and_return('mypass')
|
165
|
+
req = mock("request")
|
166
|
+
req.should_receive(:basic_auth).with('joe', 'mypass')
|
167
|
+
@request.setup_credentials(req)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "catches EOFError and shows the more informative ServerBrokeConnection" do
|
171
|
+
@http.stub!(:request).and_raise(EOFError)
|
172
|
+
lambda { @request.transmit(@uri, 'req', generate_payload('')) }.
|
173
|
+
should raise_error(RestClient::ServerBrokeConnection)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "execute calls execute_inner" do
|
177
|
+
@request.should_receive(:execute_inner)
|
178
|
+
@request.execute
|
179
|
+
end
|
180
|
+
|
181
|
+
it "class method execute wraps constructor" do
|
182
|
+
req = mock("rest request")
|
183
|
+
RestClient::Request.should_receive(:new).with(1 => 2).and_return(req)
|
184
|
+
req.should_receive(:execute)
|
185
|
+
RestClient::Request.execute(1 => 2)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "raises a Redirect with the new location when the response is in the 30x range" do
|
189
|
+
res = mock('response', :code => '301', :header => { 'Location' => 'http://new/resource' })
|
190
|
+
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://new/resource'}
|
191
|
+
end
|
192
|
+
|
193
|
+
it "handles redirects with relative paths" do
|
194
|
+
res = mock('response', :code => '301', :header => { 'Location' => 'index' })
|
195
|
+
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
|
196
|
+
end
|
197
|
+
|
198
|
+
it "handles redirects with absolute paths" do
|
199
|
+
@request.instance_variable_set('@url', 'http://some/place/else')
|
200
|
+
res = mock('response', :code => '301', :header => { 'Location' => '/index' })
|
201
|
+
lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
|
202
|
+
end
|
203
|
+
|
204
|
+
it "raises Unauthorized when the response is 401" do
|
205
|
+
res = mock('response', :code => '401')
|
206
|
+
lambda { @request.process_result(res) }.should raise_error(RestClient::Unauthorized)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "raises ResourceNotFound when the response is 404" do
|
210
|
+
res = mock('response', :code => '404')
|
211
|
+
lambda { @request.process_result(res) }.should raise_error(RestClient::ResourceNotFound)
|
212
|
+
end
|
213
|
+
|
214
|
+
it "raises RequestFailed otherwise" do
|
215
|
+
res = mock('response', :code => '500')
|
216
|
+
lambda { @request.process_result(res) }.should raise_error(RestClient::RequestFailed)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|