rest-client-next-dshelf 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
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 head(additional_headers={}, &block)
58
+ headers = (options[:headers] || {}).merge(additional_headers)
59
+ Request.execute(options.merge(
60
+ :method => :head,
61
+ :url => url,
62
+ :headers => headers), &(block || @block))
63
+ end
64
+
65
+ def post(payload, additional_headers={}, &block)
66
+ headers = (options[:headers] || {}).merge(additional_headers)
67
+ Request.execute(options.merge(
68
+ :method => :post,
69
+ :url => url,
70
+ :payload => payload,
71
+ :headers => headers), &(block || @block))
72
+ end
73
+
74
+ def put(payload, additional_headers={}, &block)
75
+ headers = (options[:headers] || {}).merge(additional_headers)
76
+ Request.execute(options.merge(
77
+ :method => :put,
78
+ :url => url,
79
+ :payload => payload,
80
+ :headers => headers), &(block || @block))
81
+ end
82
+
83
+ def delete(additional_headers={}, &block)
84
+ headers = (options[:headers] || {}).merge(additional_headers)
85
+ Request.execute(options.merge(
86
+ :method => :delete,
87
+ :url => url,
88
+ :headers => headers), &(block || @block))
89
+ end
90
+
91
+ def to_s
92
+ url
93
+ end
94
+
95
+ def user
96
+ options[:user]
97
+ end
98
+
99
+ def password
100
+ options[:password]
101
+ end
102
+
103
+ def headers
104
+ options[:headers] || {}
105
+ end
106
+
107
+ def timeout
108
+ options[:timeout]
109
+ end
110
+
111
+ def open_timeout
112
+ options[:open_timeout]
113
+ end
114
+
115
+ # Construct a subresource, preserving authentication.
116
+ #
117
+ # Example:
118
+ #
119
+ # site = RestClient::Resource.new('http://example.com', 'adam', 'mypasswd')
120
+ # site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
121
+ #
122
+ # This is especially useful if you wish to define your site in one place and
123
+ # call it in multiple locations:
124
+ #
125
+ # def orders
126
+ # RestClient::Resource.new('http://example.com/orders', 'admin', 'mypasswd')
127
+ # end
128
+ #
129
+ # orders.get # GET http://example.com/orders
130
+ # orders['1'].get # GET http://example.com/orders/1
131
+ # orders['1/items'].delete # DELETE http://example.com/orders/1/items
132
+ #
133
+ # Nest resources as far as you want:
134
+ #
135
+ # site = RestClient::Resource.new('http://example.com')
136
+ # posts = site['posts']
137
+ # first_post = posts['1']
138
+ # comments = first_post['comments']
139
+ # comments.post 'Hello', :content_type => 'text/plain'
140
+ #
141
+ def [](suburl, &new_block)
142
+ case
143
+ when block_given? then self.class.new(concat_urls(url, suburl), options, &new_block)
144
+ when block then self.class.new(concat_urls(url, suburl), options, &block)
145
+ else
146
+ self.class.new(concat_urls(url, suburl), options)
147
+ end
148
+ end
149
+
150
+ def concat_urls(url, suburl) # :nodoc:
151
+ url = url.to_s
152
+ suburl = suburl.to_s
153
+ if url.slice(-1, 1) == '/' or suburl.slice(0, 1) == '/'
154
+ url + suburl
155
+ else
156
+ "#{url}/#{suburl}"
157
+ end
158
+ end
159
+ end
160
+ 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,85 @@
1
+ require File.join( File.dirname(File.expand_path(__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
+
68
+ describe "#return!" do
69
+ it "should return the response itself on 200-codes" do
70
+ @net_http_res.should_receive(:code).and_return('200')
71
+ @response.return!.should be_equal(@response)
72
+ end
73
+
74
+ it "should raise RequestFailed on unknown codes" do
75
+ @net_http_res.should_receive(:code).and_return('1000')
76
+ lambda { @response.return! }.should raise_error RestClient::RequestFailed
77
+ end
78
+
79
+ it "should raise an error on a redirection after non-GET/HEAD requests" do
80
+ @net_http_res.should_receive(:code).and_return('301')
81
+ @response.args.merge(:method => :put)
82
+ lambda { @response.return! }.should raise_error RestClient::RequestFailed
83
+ end
84
+ end
85
+ end
data/spec/base.rb ADDED
@@ -0,0 +1,16 @@
1
+ def is_ruby_19?
2
+ RUBY_VERSION == '1.9.1' or RUBY_VERSION == '1.9.2'
3
+ end
4
+
5
+ Encoding.default_internal = Encoding.default_external = "ASCII-8BIT" if is_ruby_19?
6
+
7
+ require 'rubygems'
8
+ require 'spec'
9
+
10
+ begin
11
+ require "ruby-debug"
12
+ rescue LoadError
13
+ # NOP, ignore
14
+ end
15
+
16
+ require File.dirname(__FILE__) + '/../lib/restclient'
@@ -0,0 +1,98 @@
1
+ require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
+
3
+ require 'webmock/rspec'
4
+ include WebMock
5
+
6
+ describe RestClient::Exception do
7
+ it "returns a 'message' equal to the class name if the message is not set, because 'message' should not be nil" do
8
+ e = RestClient::Exception.new
9
+ e.message.should == "RestClient::Exception"
10
+ end
11
+
12
+ it "returns the 'message' that was set" do
13
+ e = RestClient::Exception.new
14
+ message = "An explicitly set message"
15
+ e.message = message
16
+ e.message.should == message
17
+ end
18
+
19
+ it "sets the exception message to ErrorMessage" do
20
+ RestClient::ResourceNotFound.new.message.should == 'Resource Not Found'
21
+ end
22
+
23
+ it "contains exceptions in RestClient" do
24
+ RestClient::Unauthorized.new.should be_a_kind_of(RestClient::Exception)
25
+ RestClient::ServerBrokeConnection.new.should be_a_kind_of(RestClient::Exception)
26
+ end
27
+ end
28
+
29
+ describe RestClient::ServerBrokeConnection do
30
+ it "should have a default message of 'Server broke connection'" do
31
+ e = RestClient::ServerBrokeConnection.new
32
+ e.message.should == 'Server broke connection'
33
+ end
34
+ end
35
+
36
+ describe RestClient::RequestFailed do
37
+ before do
38
+ @response = mock('HTTP Response', :code => '502')
39
+ end
40
+
41
+ it "stores the http response on the exception" do
42
+ response = "response"
43
+ begin
44
+ raise RestClient::RequestFailed, response
45
+ rescue RestClient::RequestFailed => e
46
+ e.response.should == response
47
+ end
48
+ end
49
+
50
+ it "http_code convenience method for fetching the code as an integer" do
51
+ RestClient::RequestFailed.new(@response).http_code.should == 502
52
+ end
53
+
54
+ it "http_body convenience method for fetching the body (decoding when necessary)" do
55
+ RestClient::RequestFailed.new(@response).http_code.should == 502
56
+ RestClient::RequestFailed.new(@response).message.should == 'HTTP status code 502'
57
+ end
58
+
59
+ it "shows the status code in the message" do
60
+ RestClient::RequestFailed.new(@response).to_s.should match(/502/)
61
+ end
62
+ end
63
+
64
+ describe RestClient::ResourceNotFound do
65
+ it "also has the http response attached" do
66
+ response = "response"
67
+ begin
68
+ raise RestClient::ResourceNotFound, response
69
+ rescue RestClient::ResourceNotFound => e
70
+ e.response.should == response
71
+ end
72
+ end
73
+ end
74
+
75
+ describe "backwards compatibility" do
76
+ it "alias RestClient::Request::Redirect to RestClient::Redirect" do
77
+ RestClient::Request::Redirect.should == RestClient::Redirect
78
+ end
79
+
80
+ it "alias RestClient::Request::Unauthorized to RestClient::Unauthorized" do
81
+ RestClient::Request::Unauthorized.should == RestClient::Unauthorized
82
+ end
83
+
84
+ it "alias RestClient::Request::RequestFailed to RestClient::RequestFailed" do
85
+ RestClient::Request::RequestFailed.should == RestClient::RequestFailed
86
+ end
87
+
88
+ it "make the exception's response act like an Net::HTTPResponse" do
89
+ body = "body"
90
+ stub_request(:get, "www.example.com").to_return(:body => body, :status => 404)
91
+ begin
92
+ RestClient.get "www.example.com"
93
+ raise
94
+ rescue RestClient::ResourceNotFound => e
95
+ e.response.body.should == body
96
+ end
97
+ end
98
+ 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.join( File.dirname(File.expand_path(__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