rest-client 1.0.4 → 1.1.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.
- data/README.rdoc +26 -78
- data/Rakefile +1 -2
- data/VERSION +1 -1
- data/lib/restclient.rb +8 -0
- data/lib/restclient/mixin/response.rb +6 -1
- data/lib/restclient/net_http_ext.rb +21 -0
- data/lib/restclient/payload.rb +171 -0
- data/lib/restclient/request.rb +24 -10
- data/lib/restclient/resource.rb +12 -12
- data/spec/base.rb +6 -0
- data/spec/master_shake.jpg +0 -0
- data/spec/payload_spec.rb +131 -0
- data/spec/request_spec.rb +41 -20
- data/spec/resource_spec.rb +4 -4
- data/spec/response_spec.rb +6 -1
- metadata +21 -6
data/README.rdoc
CHANGED
@@ -14,14 +14,27 @@ of specifying actions: get, put, post, delete.
|
|
14
14
|
|
15
15
|
RestClient.delete 'http://example.com/resource'
|
16
16
|
|
17
|
-
|
17
|
+
== Multipart
|
18
|
+
|
19
|
+
Yeah, that's right! This does multipart sends for you!
|
20
|
+
|
21
|
+
RestClient.post '/data', :myfile => File.new("/path/to/image.jpg")
|
22
|
+
|
23
|
+
This does two things for you:
|
24
|
+
|
25
|
+
* Auto-detects that you have a File value sends it as multipart
|
26
|
+
* Auto-detects the mime of the file and sets it in the HEAD of the payload for each entry
|
27
|
+
|
28
|
+
If you are sending params that do not contain a File object but the payload needs to be multipart then:
|
29
|
+
|
30
|
+
RestClient.post '/data', :foo => 'bar', :multipart => true
|
18
31
|
|
19
32
|
== Usage: ActiveResource-Style
|
20
33
|
|
21
34
|
resource = RestClient::Resource.new 'http://example.com/resource'
|
22
35
|
resource.get
|
23
36
|
|
24
|
-
private_resource = RestClient::Resource.new 'https://example.com/private/resource',
|
37
|
+
private_resource = RestClient::Resource.new 'https://example.com/private/resource', 'user', 'pass'
|
25
38
|
private_resource.put File.read('pic.jpg'), :content_type => 'image/jpg'
|
26
39
|
|
27
40
|
See RestClient::Resource module docs for details.
|
@@ -33,6 +46,10 @@ See RestClient::Resource module docs for details.
|
|
33
46
|
|
34
47
|
See RestClient::Resource docs for details.
|
35
48
|
|
49
|
+
== Lower-level access
|
50
|
+
|
51
|
+
For cases not covered by the general API, you can use the RestClient::Resource class which provide a lower-level API, see the class' rdoc for more information.
|
52
|
+
|
36
53
|
== Shell
|
37
54
|
|
38
55
|
The restclient shell command gives an IRB session with RestClient already loaded:
|
@@ -64,88 +81,19 @@ Create ~/.restclient for named sessions:
|
|
64
81
|
Then invoke:
|
65
82
|
|
66
83
|
$ restclient private_site
|
67
|
-
|
68
|
-
Use as a one-off, curl-style:
|
69
|
-
|
70
|
-
$ restclient get http://example.com/resource > output_body
|
71
|
-
|
72
|
-
$ restclient put http://example.com/resource < input_body
|
73
|
-
|
74
|
-
== Logging
|
75
|
-
|
76
|
-
Write calls to a log filename (can also be "stdout" or "stderr"):
|
77
|
-
|
78
|
-
RestClient.log = '/tmp/restclient.log'
|
79
|
-
|
80
|
-
Or set an environment variable to avoid modifying the code:
|
81
|
-
|
82
|
-
$ RESTCLIENT_LOG=stdout path/to/my/program
|
83
|
-
|
84
|
-
Either produces logs like this:
|
85
|
-
|
86
|
-
RestClient.get "http://some/resource"
|
87
|
-
# => 200 OK | text/html 250 bytes
|
88
|
-
RestClient.put "http://some/resource", "payload"
|
89
|
-
# => 401 Unauthorized | application/xml 340 bytes
|
90
|
-
|
91
|
-
Note that these logs are valid Ruby, so you can paste them into the restclient
|
92
|
-
shell or a script to replay your sequence of rest calls.
|
93
|
-
|
94
|
-
== Proxy
|
95
|
-
|
96
|
-
All calls to RestClient, including Resources, will use the proxy specified by
|
97
|
-
RestClient.proxy:
|
98
|
-
|
99
|
-
RestClient.proxy = "http://proxy.example.com/"
|
100
|
-
RestClient.get "http://some/resource"
|
101
|
-
# => response from some/resource as proxied through proxy.example.com
|
102
|
-
|
103
|
-
Often the proxy url is set in an environment variable, so you can do this to
|
104
|
-
use whatever proxy the system is configured to use:
|
105
|
-
|
106
|
-
RestClient.proxy = ENV['http_proxy']
|
107
|
-
|
108
|
-
== Cookies
|
109
|
-
|
110
|
-
Request and Response objects know about HTTP cookies, and will automatically
|
111
|
-
extract and set headers for them as needed:
|
112
|
-
|
113
|
-
response = RestClient.get 'http://example.com/action_which_sets_session_id'
|
114
|
-
response.cookies
|
115
|
-
# => {"_applicatioN_session_id" => "1234"}
|
116
|
-
|
117
|
-
response2 = RestClient.post(
|
118
|
-
'http://localhost:3000/',
|
119
|
-
{:param1 => "foo"},
|
120
|
-
{:cookies => {:session_id => "1234"}}
|
121
|
-
)
|
122
|
-
# ...response body
|
123
|
-
|
124
|
-
== SSL Client Certificates
|
125
|
-
|
126
|
-
RestClient::Resource.new(
|
127
|
-
'https://example.com',
|
128
|
-
:ssl_client_cert => OpenSSL::X509::Certificate.new(File.read("cert.pem")),
|
129
|
-
:ssl_client_key => OpenSSL::PKey::RSA.new(File.read("key.pem"), "passphrase, if any"),
|
130
|
-
:ssl_ca_file => "ca_certificate.pem",
|
131
|
-
:verify_ssl => OpenSSL::SSL::VERIFY_PEER
|
132
|
-
).get
|
133
|
-
|
134
|
-
Self-signed certificates can be generated with the openssl command-line tool.
|
135
84
|
|
136
85
|
== Meta
|
137
86
|
|
138
|
-
Written by Adam Wiggins
|
87
|
+
Written by Adam Wiggins, major modifications by Blake Mizerany, maintained by Archiloque
|
139
88
|
|
140
|
-
Patches contributed by: Chris Anderson, Greg Borenstein, Ardekantur, Pedro
|
141
|
-
Belo, Rafael Souza, Rick Olson, Aman Gupta, Blake Mizerany, Brian Donovan, Ivan
|
142
|
-
Makfinsky, Marc-André Cournoyer, Coda Hale, Tetsuo Watanabe, Dusty Doris,
|
143
|
-
Lennon Day-Reynolds, James Edward Gray II, Cyril Rohr, Juan Alvarez, and Adam
|
144
|
-
Jacob, Paul Dlug, and Brad Ediger
|
89
|
+
Patches contributed by: Chris Anderson, Greg Borenstein, Ardekantur, Pedro Belo, Rafael Souza, Rick Olson, Aman Gupta, François Beausoleil and Nick Plante.
|
145
90
|
|
146
91
|
Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
|
147
92
|
|
148
|
-
http://rest-client
|
93
|
+
Main page: http://github.com/archiloque/rest-client
|
94
|
+
|
95
|
+
Rdoc: http://rdoc.info/projects/archiloque/rest-client
|
149
96
|
|
150
|
-
|
97
|
+
Mailing list: rest.client@librelist.com (send a mail to subscribe).
|
151
98
|
|
99
|
+
IRC: #rest-client at freenode
|
data/Rakefile
CHANGED
@@ -52,7 +52,6 @@ Rake::RDocTask.new do |t|
|
|
52
52
|
t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
53
53
|
t.options << '--charset' << 'utf-8'
|
54
54
|
t.rdoc_files.include('README.rdoc')
|
55
|
-
t.rdoc_files.include('lib
|
56
|
-
t.rdoc_files.include('lib/restclient/*.rb')
|
55
|
+
t.rdoc_files.include('lib/*.rb')
|
57
56
|
end
|
58
57
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0
|
1
|
+
1.1.0
|
data/lib/restclient.rb
CHANGED
@@ -15,6 +15,8 @@ require File.dirname(__FILE__) + '/restclient/response'
|
|
15
15
|
require File.dirname(__FILE__) + '/restclient/raw_response'
|
16
16
|
require File.dirname(__FILE__) + '/restclient/resource'
|
17
17
|
require File.dirname(__FILE__) + '/restclient/exceptions'
|
18
|
+
require File.dirname(__FILE__) + '/restclient/payload'
|
19
|
+
require File.dirname(__FILE__) + '/restclient/net_http_ext'
|
18
20
|
|
19
21
|
# This module's static methods are the entry point for using the REST client.
|
20
22
|
#
|
@@ -96,4 +98,10 @@ module RestClient
|
|
96
98
|
return @@log if defined? @@log
|
97
99
|
nil
|
98
100
|
end
|
101
|
+
|
102
|
+
def self.version
|
103
|
+
version_path = File.dirname(__FILE__) + "/../VERSION"
|
104
|
+
return File.read(version_path).chomp if File.file?(version_path)
|
105
|
+
"0.0.0"
|
106
|
+
end
|
99
107
|
end
|
@@ -15,6 +15,11 @@ module RestClient
|
|
15
15
|
@headers ||= self.class.beautify_headers(@net_http_res.to_hash)
|
16
16
|
end
|
17
17
|
|
18
|
+
# The raw headers.
|
19
|
+
def raw_headers
|
20
|
+
@raw_headers ||= @net_http_res.to_hash
|
21
|
+
end
|
22
|
+
|
18
23
|
# Hash of cookies extracted from response headers
|
19
24
|
def cookies
|
20
25
|
@cookies ||= (self.headers[:set_cookie] || "").split('; ').inject({}) do |out, raw_c|
|
@@ -33,7 +38,7 @@ module RestClient
|
|
33
38
|
module ClassMethods
|
34
39
|
def beautify_headers(headers)
|
35
40
|
headers.inject({}) do |out, (key, value)|
|
36
|
-
out[key.gsub(/-/, '_').to_sym] = value.first
|
41
|
+
out[key.gsub(/-/, '_').downcase.to_sym] = value.first
|
37
42
|
out
|
38
43
|
end
|
39
44
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# Replace the request method in Net::HTTP to sniff the body type
|
3
|
+
# and set the stream if appropriate
|
4
|
+
#
|
5
|
+
# Taken from:
|
6
|
+
# http://www.missiondata.com/blog/ruby/29/streaming-data-to-s3-with-ruby/
|
7
|
+
|
8
|
+
module Net
|
9
|
+
class HTTP
|
10
|
+
alias __request__ request
|
11
|
+
|
12
|
+
def request(req, body=nil, &block)
|
13
|
+
if body != nil && body.respond_to?(:read)
|
14
|
+
req.body_stream = body
|
15
|
+
return __request__(req, nil, &block)
|
16
|
+
else
|
17
|
+
return __request__(req, body, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require "tempfile"
|
2
|
+
require "stringio"
|
3
|
+
require "mime/types"
|
4
|
+
|
5
|
+
module RestClient
|
6
|
+
module Payload
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def generate(params)
|
10
|
+
if params.is_a?(String)
|
11
|
+
Base.new(params)
|
12
|
+
elsif params
|
13
|
+
if params.delete(:multipart) == true || has_file?(params)
|
14
|
+
Multipart.new(params)
|
15
|
+
else
|
16
|
+
UrlEncoded.new(params)
|
17
|
+
end
|
18
|
+
else
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_file?(params)
|
24
|
+
params.any? do |_, v|
|
25
|
+
case v
|
26
|
+
when Hash
|
27
|
+
has_file?(v)
|
28
|
+
else
|
29
|
+
v.respond_to?(:path) && v.respond_to?(:read)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Base
|
35
|
+
def initialize(params)
|
36
|
+
build_stream(params)
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_stream(params)
|
40
|
+
@stream = StringIO.new(params)
|
41
|
+
@stream.seek(0)
|
42
|
+
end
|
43
|
+
|
44
|
+
def read(bytes=nil)
|
45
|
+
@stream.read(bytes)
|
46
|
+
end
|
47
|
+
alias :to_s :read
|
48
|
+
|
49
|
+
def escape(v)
|
50
|
+
URI.escape(v.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Flatten parameters by converting hashes of hashes to flat hashes
|
54
|
+
# {keys1 => {keys2 => value}} will be transformed into {keys1[key2] => value}
|
55
|
+
def flatten_params(params, parent_key = nil)
|
56
|
+
result = {}
|
57
|
+
params.keys.map do |key|
|
58
|
+
calculated_key = parent_key ? "#{parent_key}[#{escape key}]" : escape(key)
|
59
|
+
value = params[key]
|
60
|
+
if value.is_a? Hash
|
61
|
+
result.merge!(flatten_params(value, calculated_key))
|
62
|
+
else
|
63
|
+
result[calculated_key] = value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
def headers
|
70
|
+
{ 'Content-Length' => size.to_s }
|
71
|
+
end
|
72
|
+
|
73
|
+
def size
|
74
|
+
@stream.size
|
75
|
+
end
|
76
|
+
alias :length :size
|
77
|
+
|
78
|
+
def close
|
79
|
+
@stream.close
|
80
|
+
end
|
81
|
+
|
82
|
+
def inspect
|
83
|
+
result = to_s.inspect
|
84
|
+
@stream.seek(0)
|
85
|
+
result
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class UrlEncoded < Base
|
90
|
+
def build_stream(params = nil)
|
91
|
+
@stream = StringIO.new(flatten_params(params).map do |k,v|
|
92
|
+
"#{k}=#{escape(v)}"
|
93
|
+
end.join("&"))
|
94
|
+
@stream.seek(0)
|
95
|
+
end
|
96
|
+
|
97
|
+
def headers
|
98
|
+
super.merge({ 'Content-Type' => 'application/x-www-form-urlencoded' })
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Multipart < Base
|
103
|
+
EOL = "\r\n"
|
104
|
+
|
105
|
+
def build_stream(params)
|
106
|
+
b = "--#{boundary}"
|
107
|
+
|
108
|
+
@stream = Tempfile.new("RESTClient.Stream.#{rand(1000)}")
|
109
|
+
@stream.write(b + EOL)
|
110
|
+
|
111
|
+
if params.is_a? Hash
|
112
|
+
x = flatten_params(params).to_a
|
113
|
+
else
|
114
|
+
x = params.to_a
|
115
|
+
end
|
116
|
+
|
117
|
+
last_index = x.length - 1
|
118
|
+
x.each_with_index do |a, index|
|
119
|
+
k, v = *a
|
120
|
+
if v.respond_to?(:read) && v.respond_to?(:path)
|
121
|
+
create_file_field(@stream, k,v)
|
122
|
+
else
|
123
|
+
create_regular_field(@stream, k,v)
|
124
|
+
end
|
125
|
+
@stream.write(EOL + b)
|
126
|
+
@stream.write(EOL) unless last_index == index
|
127
|
+
end
|
128
|
+
@stream.write('--')
|
129
|
+
@stream.write(EOL)
|
130
|
+
@stream.seek(0)
|
131
|
+
end
|
132
|
+
|
133
|
+
def create_regular_field(s, k, v)
|
134
|
+
s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"")
|
135
|
+
s.write(EOL)
|
136
|
+
s.write(EOL)
|
137
|
+
s.write(v)
|
138
|
+
end
|
139
|
+
|
140
|
+
def create_file_field(s, k, v)
|
141
|
+
begin
|
142
|
+
s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"; filename=\"#{v.respond_to?(:original_filename) ? v.original_filename : File.basename(v.path)}\"#{EOL}")
|
143
|
+
s.write("Content-Type: #{v.respond_to?(:content_type) ? v.content_type : mime_for(v.path)}#{EOL}")
|
144
|
+
s.write(EOL)
|
145
|
+
while data = v.read(8124)
|
146
|
+
s.write(data)
|
147
|
+
end
|
148
|
+
ensure
|
149
|
+
v.close
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def mime_for(path)
|
154
|
+
mime = MIME::Types.type_for path
|
155
|
+
mime.empty? ? 'text/plain' : mime[0].content_type
|
156
|
+
end
|
157
|
+
|
158
|
+
def boundary
|
159
|
+
@boundary ||= rand(1_000_000).to_s
|
160
|
+
end
|
161
|
+
|
162
|
+
def headers
|
163
|
+
super.merge({'Content-Type' => %Q{multipart/form-data; boundary="#{boundary}"}})
|
164
|
+
end
|
165
|
+
|
166
|
+
def close
|
167
|
+
@stream.close
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
data/lib/restclient/request.rb
CHANGED
@@ -2,11 +2,22 @@ require 'tempfile'
|
|
2
2
|
|
3
3
|
module RestClient
|
4
4
|
# This class is used internally by RestClient to send the request, but you can also
|
5
|
-
#
|
5
|
+
# call it directly if you'd like to use a method not supported by the
|
6
6
|
# main API. For example:
|
7
7
|
#
|
8
8
|
# RestClient::Request.execute(:method => :head, :url => 'http://example.com')
|
9
9
|
#
|
10
|
+
# Mandatory parameters:
|
11
|
+
# * :method
|
12
|
+
# * :url
|
13
|
+
# Optional parameters (have a look at ssl and/or uri for some explanations):
|
14
|
+
# * :headers a hash containing the request headers
|
15
|
+
# * :cookies will replace possible cookies in the :headers
|
16
|
+
# * :user and :password for basic auth, will be replaced by a user/password available in the :url
|
17
|
+
# * :raw_response return a low-level RawResponse instead of a Response
|
18
|
+
# * :verify_ssl enable ssl verification, possible values are constants from OpenSSL::SSL
|
19
|
+
# * :timeout and :open_timeout
|
20
|
+
# * :ssl_client_cert, :ssl_client_key, :ssl_ca_file
|
10
21
|
class Request
|
11
22
|
attr_reader :method, :url, :payload, :headers,
|
12
23
|
:cookies, :user, :password, :timeout, :open_timeout,
|
@@ -22,7 +33,7 @@ module RestClient
|
|
22
33
|
@url = args[:url] or raise ArgumentError, "must pass :url"
|
23
34
|
@headers = args[:headers] || {}
|
24
35
|
@cookies = @headers.delete(:cookies) || args[:cookies] || {}
|
25
|
-
@payload =
|
36
|
+
@payload = Payload.generate(args[:payload])
|
26
37
|
@user = args[:user]
|
27
38
|
@password = args[:password]
|
28
39
|
@timeout = args[:timeout]
|
@@ -30,7 +41,7 @@ module RestClient
|
|
30
41
|
@raw_response = args[:raw_response] || false
|
31
42
|
@verify_ssl = args[:verify_ssl] || false
|
32
43
|
@ssl_client_cert = args[:ssl_client_cert] || nil
|
33
|
-
@ssl_client_key
|
44
|
+
@ssl_client_key = args[:ssl_client_key] || nil
|
34
45
|
@ssl_ca_file = args[:ssl_ca_file] || nil
|
35
46
|
@tf = nil # If you are a raw request, this is your tempfile
|
36
47
|
end
|
@@ -54,10 +65,13 @@ module RestClient
|
|
54
65
|
user_headers[:cookie] = @cookies.map {|key, val| "#{key.to_s}=#{val}" }.join('; ')
|
55
66
|
end
|
56
67
|
|
57
|
-
default_headers.merge(user_headers).inject({}) do |final, (key, value)|
|
68
|
+
headers = default_headers.merge(user_headers).inject({}) do |final, (key, value)|
|
58
69
|
final[key.to_s.gsub(/_/, '-').capitalize] = value.to_s
|
59
|
-
|
70
|
+
final
|
60
71
|
end
|
72
|
+
|
73
|
+
headers.merge!(@payload.headers) if @payload
|
74
|
+
headers
|
61
75
|
end
|
62
76
|
|
63
77
|
def net_http_class
|
@@ -108,10 +122,10 @@ module RestClient
|
|
108
122
|
net = net_http_class.new(uri.host, uri.port)
|
109
123
|
net.use_ssl = uri.is_a?(URI::HTTPS)
|
110
124
|
if @verify_ssl == false
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
125
|
+
net.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
126
|
+
elsif @verify_ssl.is_a? Integer
|
127
|
+
net.verify_mode = @verify_ssl
|
128
|
+
end
|
115
129
|
net.cert = @ssl_client_cert if @ssl_client_cert
|
116
130
|
net.key = @ssl_client_key if @ssl_client_key
|
117
131
|
net.ca_file = @ssl_ca_file if @ssl_ca_file
|
@@ -130,7 +144,7 @@ module RestClient
|
|
130
144
|
elsif @raw_response
|
131
145
|
RawResponse.new(@tf, res)
|
132
146
|
else
|
133
|
-
nil
|
147
|
+
Response.new(nil, res)
|
134
148
|
end
|
135
149
|
end
|
136
150
|
rescue EOFError
|
data/lib/restclient/resource.rb
CHANGED
@@ -45,38 +45,38 @@ module RestClient
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
def get(additional_headers={})
|
48
|
+
def get(additional_headers={}, &b)
|
49
|
+
headers = (options[:headers] || {}).merge(additional_headers)
|
49
50
|
Request.execute(options.merge(
|
50
51
|
:method => :get,
|
51
52
|
:url => url,
|
52
|
-
:headers => headers
|
53
|
-
))
|
53
|
+
:headers => headers), &b)
|
54
54
|
end
|
55
55
|
|
56
|
-
def post(payload, additional_headers={})
|
56
|
+
def post(payload, additional_headers={}, &b)
|
57
|
+
headers = (options[:headers] || {}).merge(additional_headers)
|
57
58
|
Request.execute(options.merge(
|
58
59
|
:method => :post,
|
59
60
|
:url => url,
|
60
61
|
:payload => payload,
|
61
|
-
:headers => headers
|
62
|
-
))
|
62
|
+
:headers => headers), &b)
|
63
63
|
end
|
64
64
|
|
65
|
-
def put(payload, additional_headers={})
|
65
|
+
def put(payload, additional_headers={}, &b)
|
66
|
+
headers = (options[:headers] || {}).merge(additional_headers)
|
66
67
|
Request.execute(options.merge(
|
67
68
|
:method => :put,
|
68
69
|
:url => url,
|
69
70
|
:payload => payload,
|
70
|
-
:headers => headers
|
71
|
-
))
|
71
|
+
:headers => headers), &b)
|
72
72
|
end
|
73
73
|
|
74
|
-
def delete(additional_headers={})
|
74
|
+
def delete(additional_headers={}, &b)
|
75
|
+
headers = (options[:headers] || {}).merge(additional_headers)
|
75
76
|
Request.execute(options.merge(
|
76
77
|
:method => :delete,
|
77
78
|
:url => url,
|
78
|
-
:headers => headers
|
79
|
-
))
|
79
|
+
:headers => headers), &b)
|
80
80
|
end
|
81
81
|
|
82
82
|
def to_s
|
data/spec/base.rb
CHANGED
Binary file
|
@@ -0,0 +1,131 @@
|
|
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
|
+
end
|
14
|
+
|
15
|
+
it "should properly handle hashes as parameter" do
|
16
|
+
RestClient::Payload::UrlEncoded.new({:foo => {:bar => 'baz' }}).to_s.
|
17
|
+
should == "foo[bar]=baz"
|
18
|
+
RestClient::Payload::UrlEncoded.new({:foo => {:bar => {:baz => 'qux' }}}).to_s.
|
19
|
+
should == "foo[bar][baz]=qux"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should form properly use symbols as parameters" do
|
23
|
+
RestClient::Payload::UrlEncoded.new({:foo => :bar}).to_s.
|
24
|
+
should == "foo=bar"
|
25
|
+
RestClient::Payload::UrlEncoded.new({:foo => {:bar => :baz }}).to_s.
|
26
|
+
should == "foo[bar]=baz"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
context "A multipart Payload" do
|
32
|
+
it "should use standard enctype as default content-type" do
|
33
|
+
m = RestClient::Payload::Multipart.new({})
|
34
|
+
m.stub!(:boundary).and_return(123)
|
35
|
+
m.headers['Content-Type'].should == 'multipart/form-data; boundary="123"'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should form properly seperated multipart data" do
|
39
|
+
m = RestClient::Payload::Multipart.new([[:bar , "baz"], [:foo, "bar"]])
|
40
|
+
m.to_s.should == <<-EOS
|
41
|
+
--#{m.boundary}\r
|
42
|
+
Content-Disposition: multipart/form-data; name="bar"\r
|
43
|
+
\r
|
44
|
+
baz\r
|
45
|
+
--#{m.boundary}\r
|
46
|
+
Content-Disposition: multipart/form-data; name="foo"\r
|
47
|
+
\r
|
48
|
+
bar\r
|
49
|
+
--#{m.boundary}--\r
|
50
|
+
EOS
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should form properly seperated multipart data" do
|
54
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
55
|
+
m = RestClient::Payload::Multipart.new({:foo => f})
|
56
|
+
m.to_s.should == <<-EOS
|
57
|
+
--#{m.boundary}\r
|
58
|
+
Content-Disposition: multipart/form-data; name="foo"; filename="master_shake.jpg"\r
|
59
|
+
Content-Type: image/jpeg\r
|
60
|
+
\r
|
61
|
+
#{IO.read(f.path)}\r
|
62
|
+
--#{m.boundary}--\r
|
63
|
+
EOS
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should detect optional (original) content type and filename" do
|
67
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
68
|
+
f.instance_eval "def content_type; 'text/plain'; end"
|
69
|
+
f.instance_eval "def original_filename; 'foo.txt'; end"
|
70
|
+
m = RestClient::Payload::Multipart.new({:foo => f})
|
71
|
+
m.to_s.should == <<-EOS
|
72
|
+
--#{m.boundary}\r
|
73
|
+
Content-Disposition: multipart/form-data; name="foo"; filename="foo.txt"\r
|
74
|
+
Content-Type: text/plain\r
|
75
|
+
\r
|
76
|
+
#{IO.read(f.path)}\r
|
77
|
+
--#{m.boundary}--\r
|
78
|
+
EOS
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should handle hash in hash parameters" do
|
82
|
+
m = RestClient::Payload::Multipart.new({:bar => {:baz => "foo"}})
|
83
|
+
m.to_s.should == <<-EOS
|
84
|
+
--#{m.boundary}\r
|
85
|
+
Content-Disposition: multipart/form-data; name="bar[baz]"\r
|
86
|
+
\r
|
87
|
+
foo\r
|
88
|
+
--#{m.boundary}--\r
|
89
|
+
EOS
|
90
|
+
|
91
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
92
|
+
f.instance_eval "def content_type; 'text/plain'; end"
|
93
|
+
f.instance_eval "def original_filename; 'foo.txt'; end"
|
94
|
+
m = RestClient::Payload::Multipart.new({:foo => {:bar => f}})
|
95
|
+
m.to_s.should == <<-EOS
|
96
|
+
--#{m.boundary}\r
|
97
|
+
Content-Disposition: multipart/form-data; name="foo[bar]"; filename="foo.txt"\r
|
98
|
+
Content-Type: text/plain\r
|
99
|
+
\r
|
100
|
+
#{IO.read(f.path)}\r
|
101
|
+
--#{m.boundary}--\r
|
102
|
+
EOS
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
context "Payload generation" do
|
108
|
+
it "should recognize standard urlencoded params" do
|
109
|
+
RestClient::Payload.generate({"foo" => 'bar'}).should be_kind_of(RestClient::Payload::UrlEncoded)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should recognize multipart params" do
|
113
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
114
|
+
RestClient::Payload.generate({"foo" => f}).should be_kind_of(RestClient::Payload::Multipart)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should be multipart if forced" do
|
118
|
+
RestClient::Payload.generate({"foo" => "bar", :multipart => true}).should be_kind_of(RestClient::Payload::Multipart)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should return data if no of the above" do
|
122
|
+
RestClient::Payload.generate("data").should be_kind_of(RestClient::Payload::Base)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should recognize nested multipart payloads" do
|
126
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
127
|
+
RestClient::Payload.generate({"foo" => {"file" => f}}).should be_kind_of(RestClient::Payload::Multipart)
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
data/spec/request_spec.rb
CHANGED
@@ -93,17 +93,25 @@ describe RestClient::Request do
|
|
93
93
|
|
94
94
|
it "merges user headers with the default headers" do
|
95
95
|
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
96
|
-
@request.make_headers('3' => '4')
|
96
|
+
headers = @request.make_headers('3' => '4')
|
97
|
+
headers.should have_key('1')
|
98
|
+
headers['1'].should == '2'
|
99
|
+
headers.should have_key('3')
|
100
|
+
headers['3'].should == '4'
|
97
101
|
end
|
98
102
|
|
99
103
|
it "prefers the user header when the same header exists in the defaults" do
|
100
104
|
@request.should_receive(:default_headers).and_return({ '1' => '2' })
|
101
|
-
@request.make_headers('1' => '3')
|
105
|
+
headers = @request.make_headers('1' => '3')
|
106
|
+
headers.should have_key('1')
|
107
|
+
headers['1'].should == '3'
|
102
108
|
end
|
103
109
|
|
104
110
|
it "converts header symbols from :content_type to 'Content-type'" do
|
105
111
|
@request.should_receive(:default_headers).and_return({})
|
106
|
-
@request.make_headers(:content_type => 'abc')
|
112
|
+
headers = @request.make_headers(:content_type => 'abc')
|
113
|
+
headers.should have_key('Content-type')
|
114
|
+
headers['Content-type'].should == 'abc'
|
107
115
|
end
|
108
116
|
|
109
117
|
it "converts header values to strings" do
|
@@ -115,7 +123,7 @@ describe RestClient::Request do
|
|
115
123
|
klass = mock("net:http class")
|
116
124
|
@request.should_receive(:net_http_request_class).with(:put).and_return(klass)
|
117
125
|
klass.should_receive(:new).and_return('result')
|
118
|
-
@request.should_receive(:transmit).with(@uri, 'result',
|
126
|
+
@request.should_receive(:transmit).with(@uri, 'result', kind_of(RestClient::Payload::Base))
|
119
127
|
@request.execute_inner
|
120
128
|
end
|
121
129
|
|
@@ -350,13 +358,13 @@ describe RestClient::Request do
|
|
350
358
|
@request.verify_ssl.should == false
|
351
359
|
end
|
352
360
|
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
361
|
+
it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do
|
362
|
+
@net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
363
|
+
@http.stub!(:request)
|
364
|
+
@request.stub!(:process_result)
|
365
|
+
@request.stub!(:response_log)
|
366
|
+
@request.transmit(@uri, 'req', 'payload')
|
367
|
+
end
|
360
368
|
|
361
369
|
it "should not set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is true" do
|
362
370
|
@request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true)
|
@@ -367,19 +375,18 @@ describe RestClient::Request do
|
|
367
375
|
@request.transmit(@uri, 'req', 'payload')
|
368
376
|
end
|
369
377
|
|
370
|
-
|
371
|
-
|
372
|
-
OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
378
|
+
it "should set net.verify_mode to the passed value if verify_ssl is an OpenSSL constant" do
|
379
|
+
mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
373
380
|
@request = RestClient::Request.new( :method => :put,
|
374
381
|
:url => 'https://some/resource',
|
375
382
|
:payload => 'payload',
|
376
383
|
:verify_ssl => mode )
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
384
|
+
@net.should_receive(:verify_mode=).with(mode)
|
385
|
+
@http.stub!(:request)
|
386
|
+
@request.stub!(:process_result)
|
387
|
+
@request.stub!(:response_log)
|
388
|
+
@request.transmit(@uri, 'req', 'payload')
|
389
|
+
end
|
383
390
|
|
384
391
|
it "should default to not having an ssl_client_cert" do
|
385
392
|
@request.ssl_client_cert.should be(nil)
|
@@ -473,4 +480,18 @@ describe RestClient::Request do
|
|
473
480
|
@request.stub!(:response_log)
|
474
481
|
@request.transmit(@uri, 'req', 'payload')
|
475
482
|
end
|
483
|
+
|
484
|
+
it "should still return a response object for 204 No Content responses" do
|
485
|
+
@request = RestClient::Request.new(
|
486
|
+
:method => :put,
|
487
|
+
:url => 'https://some/resource',
|
488
|
+
:payload => 'payload'
|
489
|
+
)
|
490
|
+
net_http_res = Net::HTTPNoContent.new("", "204", "No Content")
|
491
|
+
net_http_res.stub(:read_body).and_return(nil)
|
492
|
+
@http.should_receive(:request).and_return(@request.fetch_body(net_http_res))
|
493
|
+
response = @request.transmit(@uri, 'req', 'payload')
|
494
|
+
response.should_not be_nil
|
495
|
+
response.code.should equal(204)
|
496
|
+
end
|
476
497
|
end
|
data/spec/resource_spec.rb
CHANGED
@@ -42,19 +42,19 @@ describe RestClient::Resource do
|
|
42
42
|
@resource.password.should == 'pass'
|
43
43
|
end
|
44
44
|
|
45
|
-
it "
|
45
|
+
it "concatenates urls, inserting a slash when it needs one" do
|
46
46
|
@resource.concat_urls('http://example.com', 'resource').should == 'http://example.com/resource'
|
47
47
|
end
|
48
48
|
|
49
|
-
it "
|
49
|
+
it "concatenates urls, using no slash if the first url ends with a slash" do
|
50
50
|
@resource.concat_urls('http://example.com/', 'resource').should == 'http://example.com/resource'
|
51
51
|
end
|
52
52
|
|
53
|
-
it "
|
53
|
+
it "concatenates urls, using no slash if the second url starts with a slash" do
|
54
54
|
@resource.concat_urls('http://example.com', '/resource').should == 'http://example.com/resource'
|
55
55
|
end
|
56
56
|
|
57
|
-
it "
|
57
|
+
it "concatenates even non-string urls, :posts + 1 => 'posts/1'" do
|
58
58
|
@resource.concat_urls(:posts, 1).should == 'posts/1'
|
59
59
|
end
|
60
60
|
|
data/spec/response_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/base'
|
|
2
2
|
|
3
3
|
describe RestClient::Response do
|
4
4
|
before do
|
5
|
-
@net_http_res = mock('net http response')
|
5
|
+
@net_http_res = mock('net http response', :to_hash => {"Status" => ["200 OK"]})
|
6
6
|
@response = RestClient::Response.new('abc', @net_http_res)
|
7
7
|
end
|
8
8
|
|
@@ -13,4 +13,9 @@ describe RestClient::Response do
|
|
13
13
|
it "accepts nil strings and sets it to empty for the case of HEAD" do
|
14
14
|
RestClient::Response.new(nil, @net_http_res).should == ""
|
15
15
|
end
|
16
|
+
|
17
|
+
it "test headers and raw headers" do
|
18
|
+
@response.raw_headers["Status"][0].should == "200 OK"
|
19
|
+
@response.headers[:status].should == "200 OK"
|
20
|
+
end
|
16
21
|
end
|
metadata
CHANGED
@@ -1,20 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Wiggins
|
8
|
+
- Archiloque
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date: 2009-12-
|
13
|
+
date: 2009-12-28 00:00:00 +01:00
|
13
14
|
default_executable: restclient
|
14
|
-
dependencies:
|
15
|
-
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: mime-types
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "1.16"
|
25
|
+
version:
|
16
26
|
description: "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
|
17
|
-
email:
|
27
|
+
email: rest.client@librelist.com
|
18
28
|
executables:
|
19
29
|
- restclient
|
20
30
|
extensions: []
|
@@ -30,20 +40,24 @@ files:
|
|
30
40
|
- lib/restclient.rb
|
31
41
|
- lib/restclient/exceptions.rb
|
32
42
|
- lib/restclient/mixin/response.rb
|
43
|
+
- lib/restclient/net_http_ext.rb
|
44
|
+
- lib/restclient/payload.rb
|
33
45
|
- lib/restclient/raw_response.rb
|
34
46
|
- lib/restclient/request.rb
|
35
47
|
- lib/restclient/resource.rb
|
36
48
|
- lib/restclient/response.rb
|
37
49
|
- spec/base.rb
|
38
50
|
- spec/exceptions_spec.rb
|
51
|
+
- spec/master_shake.jpg
|
39
52
|
- spec/mixin/response_spec.rb
|
53
|
+
- spec/payload_spec.rb
|
40
54
|
- spec/raw_response_spec.rb
|
41
55
|
- spec/request_spec.rb
|
42
56
|
- spec/resource_spec.rb
|
43
57
|
- spec/response_spec.rb
|
44
58
|
- spec/restclient_spec.rb
|
45
59
|
has_rdoc: true
|
46
|
-
homepage: http://rest-client
|
60
|
+
homepage: http://github.com/archiloque/rest-client
|
47
61
|
licenses: []
|
48
62
|
|
49
63
|
post_install_message:
|
@@ -74,6 +88,7 @@ test_files:
|
|
74
88
|
- spec/base.rb
|
75
89
|
- spec/exceptions_spec.rb
|
76
90
|
- spec/mixin/response_spec.rb
|
91
|
+
- spec/payload_spec.rb
|
77
92
|
- spec/raw_response_spec.rb
|
78
93
|
- spec/request_spec.rb
|
79
94
|
- spec/resource_spec.rb
|