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.

@@ -14,14 +14,27 @@ of specifying actions: get, put, post, delete.
14
14
 
15
15
  RestClient.delete 'http://example.com/resource'
16
16
 
17
- See RestClient module docs for details.
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', :user => 'adam', :password => 'secret', :timeout => 20, :open_timeout => 5
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 (adam at heroku dot com)
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.heroku.com
93
+ Main page: http://github.com/archiloque/rest-client
94
+
95
+ Rdoc: http://rdoc.info/projects/archiloque/rest-client
149
96
 
150
- http://github.com/adamwiggins/rest-client
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/restclient.rb')
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.4
1
+ 1.1.0
@@ -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
@@ -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
- # access it internally if you'd like to use a method not directly supported by the
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 = process_payload(args[: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 = args[:ssl_client_key] || nil
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
- final
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
- net.verify_mode = OpenSSL::SSL::VERIFY_NONE
112
- elsif @verify_ssl.is_a? Integer
113
- net.verify_mode = @verify_ssl
114
- end
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
@@ -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.merge(additional_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.merge(additional_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.merge(additional_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.merge(additional_headers)
79
- ))
79
+ :headers => headers), &b)
80
80
  end
81
81
 
82
82
  def to_s
@@ -1,4 +1,10 @@
1
1
  require 'rubygems'
2
2
  require 'spec'
3
3
 
4
+ begin
5
+ require "ruby-debug"
6
+ rescue LoadError
7
+ # NOP, ignore
8
+ end
9
+
4
10
  require File.dirname(__FILE__) + '/../lib/restclient'
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
@@ -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').should == { '1' => '2', '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').should == { '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').should == { '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', 'payload')
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
- it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do
354
- @net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
355
- @http.stub!(:request)
356
- @request.stub!(:process_result)
357
- @request.stub!(:response_log)
358
- @request.transmit(@uri, 'req', 'payload')
359
- end
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
- it "should set net.verify_mode to the passed value if verify_ssl is an OpenSSL constant" do
371
- mode = OpenSSL::SSL::VERIFY_PEER |
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
- @net.should_receive(:verify_mode=).with(mode)
378
- @http.stub!(:request)
379
- @request.stub!(:process_result)
380
- @request.stub!(:response_log)
381
- @request.transmit(@uri, 'req', 'payload')
382
- end
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
@@ -42,19 +42,19 @@ describe RestClient::Resource do
42
42
  @resource.password.should == 'pass'
43
43
  end
44
44
 
45
- it "concatinates urls, inserting a slash when it needs one" do
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 "concatinates urls, using no slash if the first url ends with a slash" do
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 "concatinates urls, using no slash if the second url starts with a slash" do
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 "concatinates even non-string urls, :posts + 1 => 'posts/1'" do
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
 
@@ -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
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-12 00:00:00 -08:00
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: adam@heroku.com
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.heroku.com/
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