rest-client 1.6.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.

Potentially problematic release.


This version of rest-client might be problematic. Click here for more details.

@@ -0,0 +1,147 @@
1
+ module RestClient
2
+ # A class that can be instantiated for access to a RESTful resource,
3
+ # including authentication.
4
+ #
5
+ # Example:
6
+ #
7
+ # resource = RestClient::Resource.new('http://some/resource')
8
+ # jpg = resource.get(:accept => 'image/jpg')
9
+ #
10
+ # With HTTP basic authentication:
11
+ #
12
+ # resource = RestClient::Resource.new('http://protected/resource', :user => 'user', :password => 'password')
13
+ # resource.delete
14
+ #
15
+ # With a timeout (seconds):
16
+ #
17
+ # RestClient::Resource.new('http://slow', :timeout => 10)
18
+ #
19
+ # With an open timeout (seconds):
20
+ #
21
+ # RestClient::Resource.new('http://behindfirewall', :open_timeout => 10)
22
+ #
23
+ # You can also use resources to share common headers. For headers keys,
24
+ # symbols are converted to strings. Example:
25
+ #
26
+ # resource = RestClient::Resource.new('http://some/resource', :headers => { :client_version => 1 })
27
+ #
28
+ # This header will be transported as X-Client-Version (notice the X prefix,
29
+ # capitalization and hyphens)
30
+ #
31
+ # Use the [] syntax to allocate subresources:
32
+ #
33
+ # site = RestClient::Resource.new('http://example.com', :user => 'adam', :password => 'mypasswd')
34
+ # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
35
+ #
36
+ class Resource
37
+ attr_reader :url, :options, :block
38
+
39
+ def initialize(url, options={}, backwards_compatibility=nil, &block)
40
+ @url = url
41
+ @block = block
42
+ if options.class == Hash
43
+ @options = options
44
+ else # compatibility with previous versions
45
+ @options = { :user => options, :password => backwards_compatibility }
46
+ end
47
+ end
48
+
49
+ def get(additional_headers={}, &block)
50
+ headers = (options[:headers] || {}).merge(additional_headers)
51
+ Request.execute(options.merge(
52
+ :method => :get,
53
+ :url => url,
54
+ :headers => headers), &(block || @block))
55
+ end
56
+
57
+ def post(payload, additional_headers={}, &block)
58
+ headers = (options[:headers] || {}).merge(additional_headers)
59
+ Request.execute(options.merge(
60
+ :method => :post,
61
+ :url => url,
62
+ :payload => payload,
63
+ :headers => headers), &(block || @block))
64
+ end
65
+
66
+ def put(payload, additional_headers={}, &block)
67
+ headers = (options[:headers] || {}).merge(additional_headers)
68
+ Request.execute(options.merge(
69
+ :method => :put,
70
+ :url => url,
71
+ :payload => payload,
72
+ :headers => headers), &(block || @block))
73
+ end
74
+
75
+ def delete(additional_headers={}, &block)
76
+ headers = (options[:headers] || {}).merge(additional_headers)
77
+ Request.execute(options.merge(
78
+ :method => :delete,
79
+ :url => url,
80
+ :headers => headers), &(block || @block))
81
+ end
82
+
83
+ def to_s
84
+ url
85
+ end
86
+
87
+ def user
88
+ options[:user]
89
+ end
90
+
91
+ def password
92
+ options[:password]
93
+ end
94
+
95
+ def headers
96
+ options[:headers] || {}
97
+ end
98
+
99
+ def timeout
100
+ options[:timeout]
101
+ end
102
+
103
+ def open_timeout
104
+ options[:open_timeout]
105
+ end
106
+
107
+ # Construct a subresource, preserving authentication.
108
+ #
109
+ # Example:
110
+ #
111
+ # site = RestClient::Resource.new('http://example.com', 'adam', 'mypasswd')
112
+ # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
113
+ #
114
+ # This is especially useful if you wish to define your site in one place and
115
+ # call it in multiple locations:
116
+ #
117
+ # def orders
118
+ # RestClient::Resource.new('http://example.com/orders', 'admin', 'mypasswd')
119
+ # end
120
+ #
121
+ # orders.get # GET http://example.com/orders
122
+ # orders['1'].get # GET http://example.com/orders/1
123
+ # orders['1/items'].delete # DELETE http://example.com/orders/1/items
124
+ #
125
+ # Nest resources as far as you want:
126
+ #
127
+ # site = RestClient::Resource.new('http://example.com')
128
+ # posts = site['posts']
129
+ # first_post = posts['1']
130
+ # comments = first_post['comments']
131
+ # comments.post 'Hello', :content_type => 'text/plain'
132
+ #
133
+ def [](suburl)
134
+ self.class.new(concat_urls(url, suburl), options)
135
+ end
136
+
137
+ def concat_urls(url, suburl) # :nodoc:
138
+ url = url.to_s
139
+ suburl = suburl.to_s
140
+ if url.slice(-1, 1) == '/' or suburl.slice(0, 1) == '/'
141
+ url + suburl
142
+ else
143
+ "#{url}/#{suburl}"
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,24 @@
1
+ module RestClient
2
+
3
+ # A Response from RestClient, you can access the response body, the code or the headers.
4
+ #
5
+ module Response
6
+
7
+ include AbstractResponse
8
+
9
+ attr_accessor :args, :body, :net_http_res
10
+
11
+ def body
12
+ self
13
+ end
14
+
15
+ def Response.create body, net_http_res, args
16
+ result = body || ''
17
+ result.extend Response
18
+ result.net_http_res = net_http_res
19
+ result.args = args
20
+ result
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,67 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ describe RestClient::AbstractResponse do
4
+
5
+ class MyAbstractResponse
6
+
7
+ include RestClient::AbstractResponse
8
+
9
+ attr_accessor :size
10
+
11
+ def initialize net_http_res, args
12
+ @net_http_res = net_http_res
13
+ @args = args
14
+ end
15
+
16
+ end
17
+
18
+ before do
19
+ @net_http_res = mock('net http response')
20
+ @response = MyAbstractResponse.new(@net_http_res, {})
21
+ end
22
+
23
+ it "fetches the numeric response code" do
24
+ @net_http_res.should_receive(:code).and_return('200')
25
+ @response.code.should == 200
26
+ end
27
+
28
+ it "has a nice description" do
29
+ @net_http_res.should_receive(:to_hash).and_return({'Content-Type' => ['application/pdf']})
30
+ @net_http_res.should_receive(:code).and_return('200')
31
+ @response.description == '200 OK | application/pdf bytes\n'
32
+ end
33
+
34
+ it "beautifies the headers by turning the keys to symbols" do
35
+ h = RestClient::AbstractResponse.beautify_headers('content-type' => [ 'x' ])
36
+ h.keys.first.should == :content_type
37
+ end
38
+
39
+ it "beautifies the headers by turning the values to strings instead of one-element arrays" do
40
+ h = RestClient::AbstractResponse.beautify_headers('x' => [ 'text/html' ] )
41
+ h.values.first.should == 'text/html'
42
+ end
43
+
44
+ it "fetches the headers" do
45
+ @net_http_res.should_receive(:to_hash).and_return('content-type' => [ 'text/html' ])
46
+ @response.headers.should == { :content_type => 'text/html' }
47
+ end
48
+
49
+ it "extracts cookies from response headers" do
50
+ @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
51
+ @response.cookies.should == { 'session_id' => '1' }
52
+ end
53
+
54
+ it "extract strange cookies" do
55
+ @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/'])
56
+ @response.cookies.should == { 'session_id' => 'ZJ%2FHQVH6YE+rVkTpn0zvTQ%3D%3D' }
57
+ end
58
+
59
+ it "doesn't escape cookies" do
60
+ @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca; path=/'])
61
+ @response.cookies.should == { 'session_id' => 'BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca' }
62
+ end
63
+
64
+ it "can access the net http result directly" do
65
+ @response.net_http_res.should == @net_http_res
66
+ end
67
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ begin
5
+ require "ruby-debug"
6
+ rescue LoadError
7
+ # NOP, ignore
8
+ end
9
+
10
+ require File.dirname(__FILE__) + '/../lib/restclient'
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ require 'webmock/rspec'
4
+ include WebMock
5
+
6
+ describe RestClient::Exception do
7
+ it "sets the exception message to ErrorMessage" do
8
+ RestClient::ResourceNotFound.new.message.should == 'Resource Not Found'
9
+ end
10
+
11
+ it "contains exceptions in RestClient" do
12
+ RestClient::Unauthorized.new.should be_a_kind_of(RestClient::Exception)
13
+ RestClient::ServerBrokeConnection.new.should be_a_kind_of(RestClient::Exception)
14
+ end
15
+ end
16
+
17
+ describe RestClient::RequestFailed do
18
+ before do
19
+ @response = mock('HTTP Response', :code => '502')
20
+ end
21
+
22
+ it "stores the http response on the exception" do
23
+ response = "response"
24
+ begin
25
+ raise RestClient::RequestFailed, response
26
+ rescue RestClient::RequestFailed => e
27
+ e.response.should == response
28
+ end
29
+ end
30
+
31
+ it "http_code convenience method for fetching the code as an integer" do
32
+ RestClient::RequestFailed.new(@response).http_code.should == 502
33
+ end
34
+
35
+ it "http_body convenience method for fetching the body (decoding when necessary)" do
36
+ RestClient::RequestFailed.new(@response).http_code.should == 502
37
+ RestClient::RequestFailed.new(@response).message.should == 'HTTP status code 502'
38
+ end
39
+
40
+ it "shows the status code in the message" do
41
+ RestClient::RequestFailed.new(@response).to_s.should match(/502/)
42
+ end
43
+ end
44
+
45
+ describe RestClient::ResourceNotFound do
46
+ it "also has the http response attached" do
47
+ response = "response"
48
+ begin
49
+ raise RestClient::ResourceNotFound, response
50
+ rescue RestClient::ResourceNotFound => e
51
+ e.response.should == response
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "backwards compatibility" do
57
+ it "alias RestClient::Request::Redirect to RestClient::Redirect" do
58
+ RestClient::Request::Redirect.should == RestClient::Redirect
59
+ end
60
+
61
+ it "alias RestClient::Request::Unauthorized to RestClient::Unauthorized" do
62
+ RestClient::Request::Unauthorized.should == RestClient::Unauthorized
63
+ end
64
+
65
+ it "alias RestClient::Request::RequestFailed to RestClient::RequestFailed" do
66
+ RestClient::Request::RequestFailed.should == RestClient::RequestFailed
67
+ end
68
+
69
+ it "make the exception's response act like an Net::HTTPResponse" do
70
+ body = "body"
71
+ stub_request(:get, "www.example.com").to_return(:body => body, :status => 404)
72
+ begin
73
+ RestClient.get "www.example.com"
74
+ raise
75
+ rescue RestClient::ResourceNotFound => e
76
+ e.response.body.should == body
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,19 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
3
+ UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
4
+ dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
5
+ MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
6
+ dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
7
+ AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
8
+ BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
9
+ cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
10
+ AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
11
+ MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
12
+ aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
13
+ ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
14
+ IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
15
+ MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
16
+ A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
17
+ 7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
18
+ 1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
19
+ -----END CERTIFICATE-----
@@ -0,0 +1,14 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
3
+ A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
4
+ cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
5
+ MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
6
+ BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
7
+ YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
8
+ ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
9
+ BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
10
+ I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
11
+ CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
12
+ lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
13
+ AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
14
+ -----END CERTIFICATE-----
@@ -0,0 +1,25 @@
1
+ require File.dirname(__FILE__) + '/../base'
2
+
3
+ describe RestClient::Request do
4
+ describe "ssl verification" do
5
+ it "is successful with the correct ca_file" do
6
+ request = RestClient::Request.new(
7
+ :method => :get,
8
+ :url => 'https://www.google.com',
9
+ :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
10
+ :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "verisign.crt")
11
+ )
12
+ expect { request.execute }.to_not raise_error
13
+ end
14
+
15
+ it "is unsuccessful with an incorrect ca_file" do
16
+ request = RestClient::Request.new(
17
+ :method => :get,
18
+ :url => 'https://www.google.com',
19
+ :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
20
+ :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "equifax.crt")
21
+ )
22
+ expect { request.execute }.to raise_error(RestClient::SSLCertificateNotVerified)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + '/base'
2
+
3
+ require 'webmock/rspec'
4
+ include WebMock
5
+
6
+ describe RestClient do
7
+
8
+ it "a simple request" do
9
+ body = 'abc'
10
+ stub_request(:get, "www.example.com").to_return(:body => body, :status => 200)
11
+ response = RestClient.get "www.example.com"
12
+ response.code.should == 200
13
+ response.body.should == body
14
+ end
15
+
16
+ it "a simple request with gzipped content" do
17
+ stub_request(:get, "www.example.com").with(:headers => { 'Accept-Encoding' => 'gzip, deflate' }).to_return(:body => "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000", :status => 200, :headers => { 'Content-Encoding' => 'gzip' } )
18
+ response = RestClient.get "www.example.com"
19
+ response.code.should == 200
20
+ response.body.should == "i'm gziped\n"
21
+ end
22
+
23
+ it "a 404" do
24
+ body = "Ho hai ! I'm not here !"
25
+ stub_request(:get, "www.example.com").to_return(:body => body, :status => 404)
26
+ begin
27
+ RestClient.get "www.example.com"
28
+ raise
29
+ rescue RestClient::ResourceNotFound => e
30
+ e.http_code.should == 404
31
+ e.response.code.should == 404
32
+ e.response.body.should == body
33
+ e.http_body.should == body
34
+ end
35
+ end
36
+
37
+
38
+ end
Binary file
@@ -0,0 +1,219 @@
1
+ require File.dirname(__FILE__) + "/base"
2
+
3
+ describe RestClient::Payload do
4
+ context "A regular Payload" do
5
+ it "should use standard enctype as default content-type" 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
+ ["foo=bar&baz=qux", "baz=qux&foo=bar"].should include(
14
+ RestClient::Payload::UrlEncoded.new({:foo => 'bar', :baz => 'qux'}).to_s)
15
+ end
16
+
17
+ it "should escape parameters" do
18
+ RestClient::Payload::UrlEncoded.new({'foo ' => 'bar'}).to_s.
19
+ should == "foo%20=bar"
20
+ end
21
+
22
+ it "should properly handle hashes as parameter" do
23
+ RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz' }}).to_s.
24
+ should == "foo[bar]=baz"
25
+ RestClient::Payload::UrlEncoded.new({:foo => {:bar => {:baz => 'qux' }}}).to_s.
26
+ should == "foo[bar][baz]=qux"
27
+ end
28
+
29
+ it "should handle many attributes inside a hash" do
30
+ parameters = RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz', :baz => 'qux'}}).to_s
31
+ parameters.should include("foo[bar]=baz", "foo[baz]=qux")
32
+ end
33
+
34
+ it "should handle attributes inside a an array inside an hash" do
35
+ parameters = RestClient::Payload::UrlEncoded.new({"foo" => [{"bar" => 'baz'}, {"bar" => 'qux'}]}).to_s
36
+ parameters.should include("foo[bar]=baz", "foo[bar]=qux")
37
+ end
38
+
39
+ it "should handle attributes inside a an array inside an array inside an hash" do
40
+ parameters = RestClient::Payload::UrlEncoded.new({"foo" => [ [{"bar" => 'baz'}, {"bar" => 'qux'}]]}).to_s
41
+ parameters.should include("foo[bar]=baz", "foo[bar]=qux")
42
+ end
43
+
44
+ it "should form properly use symbols as parameters" do
45
+ RestClient::Payload::UrlEncoded.new({:foo => :bar}).to_s.
46
+ should == "foo=bar"
47
+ RestClient::Payload::UrlEncoded.new({:foo => {:bar => :baz }}).to_s.
48
+ should == "foo[bar]=baz"
49
+ end
50
+
51
+ it "should properly handle arrays as repeated parameters" do
52
+ RestClient::Payload::UrlEncoded.new({:foo => ['bar']}).to_s.
53
+ should == "foo[]=bar"
54
+ RestClient::Payload::UrlEncoded.new({:foo => ['bar', 'baz']}).to_s.
55
+ should == "foo[]=bar&foo[]=baz"
56
+ end
57
+
58
+ end
59
+
60
+ context "A multipart Payload" do
61
+ it "should use standard enctype as default content-type" do
62
+ m = RestClient::Payload::Multipart.new({})
63
+ m.stub!(:boundary).and_return(123)
64
+ m.headers['Content-Type'].should == 'multipart/form-data; boundary=123'
65
+ end
66
+
67
+ it "should form properly separated multipart data" do
68
+ m = RestClient::Payload::Multipart.new([[:bar, "baz"], [:foo, "bar"]])
69
+ m.to_s.should == <<-EOS
70
+ --#{m.boundary}\r
71
+ Content-Disposition: form-data; name="bar"\r
72
+ \r
73
+ baz\r
74
+ --#{m.boundary}\r
75
+ Content-Disposition: form-data; name="foo"\r
76
+ \r
77
+ bar\r
78
+ --#{m.boundary}--\r
79
+ EOS
80
+ end
81
+
82
+ it "should not escape parameters names" do
83
+ m = RestClient::Payload::Multipart.new([["bar ", "baz"]])
84
+ m.to_s.should == <<-EOS
85
+ --#{m.boundary}\r
86
+ Content-Disposition: form-data; name="bar "\r
87
+ \r
88
+ baz\r
89
+ --#{m.boundary}--\r
90
+ EOS
91
+ end
92
+
93
+ it "should form properly separated multipart data" do
94
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
95
+ m = RestClient::Payload::Multipart.new({:foo => f})
96
+ m.to_s.should == <<-EOS
97
+ --#{m.boundary}\r
98
+ Content-Disposition: form-data; name="foo"; filename="master_shake.jpg"\r
99
+ Content-Type: image/jpeg\r
100
+ \r
101
+ #{IO.read(f.path)}\r
102
+ --#{m.boundary}--\r
103
+ EOS
104
+ end
105
+
106
+ it "should ignore the name attribute when it's not set" do
107
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
108
+ m = RestClient::Payload::Multipart.new({nil => f})
109
+ m.to_s.should == <<-EOS
110
+ --#{m.boundary}\r
111
+ Content-Disposition: form-data; filename="master_shake.jpg"\r
112
+ Content-Type: image/jpeg\r
113
+ \r
114
+ #{IO.read(f.path)}\r
115
+ --#{m.boundary}--\r
116
+ EOS
117
+ end
118
+
119
+ it "should detect optional (original) content type and filename" do
120
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
121
+ f.instance_eval "def content_type; 'text/plain'; end"
122
+ f.instance_eval "def original_filename; 'foo.txt'; end"
123
+ m = RestClient::Payload::Multipart.new({:foo => f})
124
+ m.to_s.should == <<-EOS
125
+ --#{m.boundary}\r
126
+ Content-Disposition: form-data; name="foo"; filename="foo.txt"\r
127
+ Content-Type: text/plain\r
128
+ \r
129
+ #{IO.read(f.path)}\r
130
+ --#{m.boundary}--\r
131
+ EOS
132
+ end
133
+
134
+ it "should handle hash in hash parameters" do
135
+ m = RestClient::Payload::Multipart.new({:bar => {:baz => "foo"}})
136
+ m.to_s.should == <<-EOS
137
+ --#{m.boundary}\r
138
+ Content-Disposition: form-data; name="bar[baz]"\r
139
+ \r
140
+ foo\r
141
+ --#{m.boundary}--\r
142
+ EOS
143
+
144
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
145
+ f.instance_eval "def content_type; 'text/plain'; end"
146
+ f.instance_eval "def original_filename; 'foo.txt'; end"
147
+ m = RestClient::Payload::Multipart.new({:foo => {:bar => f}})
148
+ m.to_s.should == <<-EOS
149
+ --#{m.boundary}\r
150
+ Content-Disposition: form-data; name="foo[bar]"; filename="foo.txt"\r
151
+ Content-Type: text/plain\r
152
+ \r
153
+ #{IO.read(f.path)}\r
154
+ --#{m.boundary}--\r
155
+ EOS
156
+ end
157
+
158
+ end
159
+
160
+ context "streamed payloads" do
161
+ it "should properly determine the size of file payloads" do
162
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
163
+ payload = RestClient::Payload.generate(f)
164
+ payload.size.should == 22_545
165
+ payload.length.should == 22_545
166
+ end
167
+
168
+ it "should properly determine the size of other kinds of streaming payloads" do
169
+ s = StringIO.new 'foo'
170
+ payload = RestClient::Payload.generate(s)
171
+ payload.size.should == 3
172
+ payload.length.should == 3
173
+
174
+ begin
175
+ f = Tempfile.new "rest-client"
176
+ f.write 'foo bar'
177
+
178
+ payload = RestClient::Payload.generate(f)
179
+ payload.size.should == 7
180
+ payload.length.should == 7
181
+ ensure
182
+ f.close
183
+ end
184
+ end
185
+ end
186
+
187
+ context "Payload generation" do
188
+ it "should recognize standard urlencoded params" do
189
+ RestClient::Payload.generate({"foo" => 'bar'}).should be_kind_of(RestClient::Payload::UrlEncoded)
190
+ end
191
+
192
+ it "should recognize multipart params" do
193
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
194
+ RestClient::Payload.generate({"foo" => f}).should be_kind_of(RestClient::Payload::Multipart)
195
+ end
196
+
197
+ it "should be multipart if forced" do
198
+ RestClient::Payload.generate({"foo" => "bar", :multipart => true}).should be_kind_of(RestClient::Payload::Multipart)
199
+ end
200
+
201
+ it "should return data if no of the above" do
202
+ RestClient::Payload.generate("data").should be_kind_of(RestClient::Payload::Base)
203
+ end
204
+
205
+ it "should recognize nested multipart payloads" do
206
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
207
+ RestClient::Payload.generate({"foo" => {"file" => f}}).should be_kind_of(RestClient::Payload::Multipart)
208
+ end
209
+
210
+ it "should recognize file payloads that can be streamed" do
211
+ f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
212
+ RestClient::Payload.generate(f).should be_kind_of(RestClient::Payload::Streamed)
213
+ end
214
+
215
+ it "should recognize other payloads that can be streamed" do
216
+ RestClient::Payload.generate(StringIO.new('foo')).should be_kind_of(RestClient::Payload::Streamed)
217
+ end
218
+ end
219
+ end