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.
Files changed (57) hide show
  1. data/.gitignore +26 -0
  2. data/LICENSE +20 -0
  3. data/README +47 -0
  4. data/Rakefile +145 -0
  5. data/VERSION +1 -0
  6. data/astrotrain.gemspec +96 -0
  7. data/config/sample.rb +12 -0
  8. data/lib/astrotrain/api.rb +53 -0
  9. data/lib/astrotrain/logged_mail.rb +41 -0
  10. data/lib/astrotrain/mapping/http_post.rb +18 -0
  11. data/lib/astrotrain/mapping/jabber.rb +23 -0
  12. data/lib/astrotrain/mapping/transport.rb +55 -0
  13. data/lib/astrotrain/mapping.rb +157 -0
  14. data/lib/astrotrain/message.rb +313 -0
  15. data/lib/astrotrain/tmail.rb +48 -0
  16. data/lib/astrotrain.rb +55 -0
  17. data/lib/vendor/rest-client/README.rdoc +104 -0
  18. data/lib/vendor/rest-client/Rakefile +84 -0
  19. data/lib/vendor/rest-client/bin/restclient +65 -0
  20. data/lib/vendor/rest-client/foo.diff +66 -0
  21. data/lib/vendor/rest-client/lib/rest_client/net_http_ext.rb +21 -0
  22. data/lib/vendor/rest-client/lib/rest_client/payload.rb +185 -0
  23. data/lib/vendor/rest-client/lib/rest_client/request_errors.rb +75 -0
  24. data/lib/vendor/rest-client/lib/rest_client/resource.rb +103 -0
  25. data/lib/vendor/rest-client/lib/rest_client.rb +189 -0
  26. data/lib/vendor/rest-client/rest-client.gemspec +18 -0
  27. data/lib/vendor/rest-client/spec/base.rb +5 -0
  28. data/lib/vendor/rest-client/spec/master_shake.jpg +0 -0
  29. data/lib/vendor/rest-client/spec/payload_spec.rb +71 -0
  30. data/lib/vendor/rest-client/spec/request_errors_spec.rb +44 -0
  31. data/lib/vendor/rest-client/spec/resource_spec.rb +52 -0
  32. data/lib/vendor/rest-client/spec/rest_client_spec.rb +219 -0
  33. data/tasks/doc.thor +149 -0
  34. data/tasks/merb.thor +2020 -0
  35. data/test/api_test.rb +28 -0
  36. data/test/fixtures/apple_multipart.txt +100 -0
  37. data/test/fixtures/basic.txt +14 -0
  38. data/test/fixtures/custom.txt +15 -0
  39. data/test/fixtures/fwd.txt +0 -0
  40. data/test/fixtures/gb2312_encoding.txt +16 -0
  41. data/test/fixtures/gb2312_encoding_invalid.txt +15 -0
  42. data/test/fixtures/html.txt +16 -0
  43. data/test/fixtures/iso-8859-1.txt +13 -0
  44. data/test/fixtures/mapped.txt +13 -0
  45. data/test/fixtures/multipart.txt +213 -0
  46. data/test/fixtures/multipart2.txt +213 -0
  47. data/test/fixtures/multiple.txt +13 -0
  48. data/test/fixtures/multiple_delivered_to.txt +14 -0
  49. data/test/fixtures/multiple_with_body_recipients.txt +15 -0
  50. data/test/fixtures/reply.txt +16 -0
  51. data/test/fixtures/utf-8.txt +13 -0
  52. data/test/logged_mail_test.rb +63 -0
  53. data/test/mapping_test.rb +129 -0
  54. data/test/message_test.rb +424 -0
  55. data/test/test_helper.rb +54 -0
  56. data/test/transport_test.rb +111 -0
  57. 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
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require File.dirname(__FILE__) + '/../lib/rest_client'
5
+
@@ -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