rest-client 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

@@ -1,6 +1,6 @@
1
- = REST Client -- simple DSL for accessing REST resources
1
+ = REST Client -- simple DSL for accessing HTTP and REST resources
2
2
 
3
- A simple REST client for Ruby, inspired by the Sinatra's microframework style
3
+ A simple HTTP and REST client for Ruby, inspired by the Sinatra's microframework style
4
4
  of specifying actions: get, put, post, delete.
5
5
 
6
6
  == Usage: Raw URL
@@ -49,6 +49,51 @@ See RestClient::Resource module docs for details.
49
49
 
50
50
  See RestClient::Resource docs for details.
51
51
 
52
+ == Exceptions
53
+
54
+ * for results code between 200 and 206 a RestClient::Response will be returned
55
+ * for results code between 301 and 303 the redirection will be automatically followed
56
+ * for other result codes a RestClient::Exception holding the Response will be raised, a specific exception class will be thrown for know error codes
57
+
58
+ RestClient.get 'http://example.com/resource'
59
+ ➔ RestClient::ResourceNotFound: RestClient::ResourceNotFound
60
+
61
+ begin
62
+ RestClient.get 'http://example.com/resource'
63
+ rescue => e
64
+ e.response
65
+ end
66
+ ➔ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>404 Not Found</title>..."
67
+
68
+ == Result handling
69
+
70
+ A block can be passed to the RestClient method, this block will then be called with the Response.
71
+ Response.return! can be called to invoke the default response's behavior (return the Response for 200..206, raise an exception in other cases).
72
+
73
+ # Don't raise exceptions but return the response
74
+ RestClient.get('http://example.com/resource'){|response| response}
75
+ ➔ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>404 Not Found</title>..."
76
+
77
+ # Manage a specific error code
78
+ RestClient.get('http://my-rest-service.com/resource'){ |response|
79
+ case response.code
80
+ when 200
81
+ p "It worked !"
82
+ response
83
+ when 423
84
+ raise SomeCustomExceptionIfYouWant
85
+ else
86
+ response.return!
87
+ end
88
+ }
89
+
90
+ == Non-normalized URIs.
91
+
92
+ If you want to use non-normalized URIs, you can normalize them with the addressable gem (http://addressable.rubyforge.org/api/).
93
+
94
+ require 'addressable/uri'
95
+ RestClient.get(Addressable::URI.parse("http://www.詹姆斯.com/").normalize.to_str)
96
+
52
97
  == Lower-level access
53
98
 
54
99
  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.
@@ -58,17 +103,17 @@ For cases not covered by the general API, you can use the RestClient::Resource c
58
103
  The restclient shell command gives an IRB session with RestClient already loaded:
59
104
 
60
105
  $ restclient
61
- >> RestClient.get 'http://example.com'
106
+ RestClient.get 'http://example.com'
62
107
 
63
108
  Specify a URL argument for get/post/put/delete on that resource:
64
109
 
65
110
  $ restclient http://example.com
66
- >> put '/resource', 'data'
111
+ put '/resource', 'data'
67
112
 
68
113
  Add a user and password for authenticated resources:
69
114
 
70
115
  $ restclient https://example.com user pass
71
- >> delete '/private/resource'
116
+ delete '/private/resource'
72
117
 
73
118
  Create ~/.restclient for named sessions:
74
119
 
@@ -85,11 +130,78 @@ Then invoke:
85
130
 
86
131
  $ restclient private_site
87
132
 
133
+ Use as a one-off, curl-style:
134
+
135
+ $ restclient get http://example.com/resource > output_body
136
+
137
+ $ restclient put http://example.com/resource < input_body
138
+
139
+ == Logging
140
+
141
+ To enable logging you can
142
+
143
+ * set RestClient.log with a ruby Logger
144
+ * or set an environment variable to avoid modifying the code (in this case you can use a file name, "stdout" or "stderr"):
145
+
146
+ $ RESTCLIENT_LOG=stdout path/to/my/program
147
+
148
+ Either produces logs like this:
149
+
150
+ RestClient.get "http://some/resource"
151
+ # => 200 OK | text/html 250 bytes
152
+ RestClient.put "http://some/resource", "payload"
153
+ # => 401 Unauthorized | application/xml 340 bytes
154
+
155
+ Note that these logs are valid Ruby, so you can paste them into the restclient
156
+ shell or a script to replay your sequence of rest calls.
157
+
158
+ == Proxy
159
+
160
+ All calls to RestClient, including Resources, will use the proxy specified by
161
+ RestClient.proxy:
162
+
163
+ RestClient.proxy = "http://proxy.example.com/"
164
+ RestClient.get "http://some/resource"
165
+ # => response from some/resource as proxied through proxy.example.com
166
+
167
+ Often the proxy url is set in an environment variable, so you can do this to
168
+ use whatever proxy the system is configured to use:
169
+
170
+ RestClient.proxy = ENV['http_proxy']
171
+
172
+ == Cookies
173
+
174
+ Request and Response objects know about HTTP cookies, and will automatically
175
+ extract and set headers for them as needed:
176
+
177
+ response = RestClient.get 'http://example.com/action_which_sets_session_id'
178
+ response.cookies
179
+ # => {"_applicatioN_session_id" => "1234"}
180
+
181
+ response2 = RestClient.post(
182
+ 'http://localhost:3000/',
183
+ {:param1 => "foo"},
184
+ {:cookies => {:session_id => "1234"}}
185
+ )
186
+ # ...response body
187
+
188
+ == SSL Client Certificates
189
+
190
+ RestClient::Resource.new(
191
+ 'https://example.com',
192
+ :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read("cert.pem")),
193
+ :ssl_client_key => OpenSSL::PKey::RSA.new(File.read("key.pem"), "passphrase, if any"),
194
+ :ssl_ca_file => "ca_certificate.pem",
195
+ :verify_ssl => OpenSSL::SSL::VERIFY_PEER
196
+ ).get
197
+
198
+ Self-signed certificates can be generated with the openssl command-line tool.
199
+
88
200
  == Meta
89
201
 
90
- Written by Adam Wiggins, major modifications by Blake Mizerany, maintained by Archiloque
202
+ Written by Adam Wiggins, major modifications by Blake Mizerany, maintained by Julien Kirch
91
203
 
92
- Patches contributed by: Chris Anderson, Greg Borenstein, Ardekantur, Pedro Belo, Rafael Souza, Rick Olson, Aman Gupta, François Beausoleil and Nick Plante.
204
+ Patches contributed by many, including Chris Anderson, Greg Borenstein, Ardekantur, Pedro Belo, Rafael Souza, Rick Olson, Aman Gupta, François Beausoleil and Nick Plante.
93
205
 
94
206
  Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
95
207
 
data/Rakefile CHANGED
@@ -4,8 +4,8 @@ require 'jeweler'
4
4
 
5
5
  Jeweler::Tasks.new do |s|
6
6
  s.name = "rest-client"
7
- s.description = "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
8
- s.summary = "Simple REST client for Ruby, inspired by microframework syntax for specifying actions."
7
+ s.description = "A simple HTTP and REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
8
+ s.summary = "Simple HTTP and REST client for Ruby, inspired by microframework syntax for specifying actions."
9
9
  s.author = "Adam Wiggins"
10
10
  s.email = "rest.client@librelist.com"
11
11
  s.homepage = "http://github.com/archiloque/rest-client"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0
1
+ 1.3.0
@@ -0,0 +1,28 @@
1
+ # 1.3.0
2
+
3
+ - a block can be used to process a request's result, this enable to handle custom error codes or paththrought (design by Cyril Rohr)
4
+ - cleaner log API, add a warning for some cases but should be compatible
5
+ - accept multiple "Set-Cookie" headers, see http://www.ietf.org/rfc/rfc2109.txt (patch provided by Cyril Rohr)
6
+ - remove "Content-Length" and "Content-Type" headers when following a redirection (patch provided by haarts)
7
+ - all http error codes have now a corresponding exception class and all of them contain the Reponse -> this means that the raised exception can be different
8
+ - changed "Content-Disposition: multipart/form-data" to "Content-Disposition: form-data" per RFC 2388 (patch provided by Kyle Crawford)
9
+
10
+ # 1.2.0
11
+
12
+ - formatting changed from tabs to spaces
13
+ - logged requests now include generated headers
14
+ - accept and content-type headers can now be specified using extentions: RestClient.post "http://example.com/resource", { 'x' => 1 }.to_json, :content_type => :json, :accept => :json
15
+ - should be 1.1.1 but renamed to 1.2.0 because 1.1.X versions has already been packaged on Debian
16
+
17
+ # 1.1.0
18
+
19
+ - new maintainer: Archiloque, the working repo is now at http://github.com/archiloque/rest-client
20
+ - a mailing list has been created at rest.client@librelist.com and an freenode irc channel #rest-client
21
+ - François Beausoleil' multipart code from http://github.com/francois/rest-client has been merged
22
+ - ability to use hash in hash as payload
23
+ - the mime-type code now rely on the mime-types gem http://mime-types.rubyforge.org/ instead of an internal partial list
24
+ - 204 response returns a Response instead of nil (patch provided by Elliott Draper)
25
+
26
+ All changes exept the last one should be fully compatible with the previous version.
27
+
28
+ NOTE: due to a dependency problem and to the last change, heroku users should update their heroku gem to >= 1.5.3 to be able to use this version.
@@ -9,12 +9,12 @@ rescue LoadError => e
9
9
  raise LoadError, "no such file to load -- net/https. Try running apt-get install libopenssl-ruby"
10
10
  end
11
11
 
12
+ require File.dirname(__FILE__) + '/restclient/exceptions'
12
13
  require File.dirname(__FILE__) + '/restclient/request'
13
14
  require File.dirname(__FILE__) + '/restclient/mixin/response'
14
15
  require File.dirname(__FILE__) + '/restclient/response'
15
16
  require File.dirname(__FILE__) + '/restclient/raw_response'
16
17
  require File.dirname(__FILE__) + '/restclient/resource'
17
- require File.dirname(__FILE__) + '/restclient/exceptions'
18
18
  require File.dirname(__FILE__) + '/restclient/payload'
19
19
  require File.dirname(__FILE__) + '/restclient/net_http_ext'
20
20
 
@@ -64,40 +64,38 @@ require File.dirname(__FILE__) + '/restclient/net_http_ext'
64
64
  #
65
65
  module RestClient
66
66
 
67
- def self.get(url, headers={})
68
- Request.execute(:method => :get, :url => url, :headers => headers)
67
+ def self.get(url, headers={}, &block)
68
+ Request.execute(:method => :get, :url => url, :headers => headers, &block)
69
69
  end
70
70
 
71
- def self.post(url, payload, headers={})
72
- Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers)
71
+ def self.post(url, payload, headers={}, &block)
72
+ Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers, &block)
73
73
  end
74
74
 
75
- def self.put(url, payload, headers={})
76
- Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers)
75
+ def self.put(url, payload, headers={}, &block)
76
+ Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers, &block)
77
77
  end
78
78
 
79
- def self.delete(url, headers={})
80
- Request.execute(:method => :delete, :url => url, :headers => headers)
79
+ def self.delete(url, headers={}, &block)
80
+ Request.execute(:method => :delete, :url => url, :headers => headers, &block)
81
81
  end
82
82
 
83
- def self.head(url, headers={})
84
- Request.execute(:method => :head, :url => url, :headers => headers)
83
+ def self.head(url, headers={}, &block)
84
+ Request.execute(:method => :head, :url => url, :headers => headers, &block)
85
85
  end
86
86
 
87
87
  class << self
88
88
  attr_accessor :proxy
89
89
  end
90
90
 
91
- # Print log of RestClient calls. Value can be stdout, stderr, or a filename.
91
+ # Setup the log for RestClient calls.
92
+ # Value should be a logger but can can be stdout, stderr, or a filename.
92
93
  # You can also configure logging by the environment variable RESTCLIENT_LOG.
93
- def self.log=(log)
94
- @@log = log
95
- end
96
-
97
- def self.log # :nodoc:
98
- return ENV['RESTCLIENT_LOG'] if ENV['RESTCLIENT_LOG']
99
- return @@log if defined? @@log
100
- nil
94
+ def self.log= log
95
+ if log.is_a? String
96
+ warn "[warning] You should set the log with a logger"
97
+ end
98
+ @@log = create_log log
101
99
  end
102
100
 
103
101
  def self.version
@@ -105,4 +103,49 @@ module RestClient
105
103
  return File.read(version_path).chomp if File.file?(version_path)
106
104
  "0.0.0"
107
105
  end
106
+
107
+ # Create a log that respond to << like a logger
108
+ # param can be 'stdout', 'stderr', a string (then we will log to that file) or a logger (then we return it)
109
+ def self.create_log param
110
+ if param
111
+ if param.is_a? String
112
+ if param == 'stdout'
113
+ stdout_logger = Class.new do
114
+ def << obj
115
+ STDOUT.puts obj
116
+ end
117
+ end
118
+ stdout_logger.new
119
+ elsif param == 'stderr'
120
+ stderr_logger = Class.new do
121
+ def << obj
122
+ STDERR.puts obj
123
+ end
124
+ end
125
+ stderr_logger.new
126
+ else
127
+ file_logger = Class.new do
128
+ attr_writer :target_file
129
+ def << obj
130
+ File.open(@target_file, 'a') { |f| f.puts obj }
131
+ end
132
+ end
133
+ logger = file_logger.new
134
+ logger.target_file = param
135
+ logger
136
+ end
137
+ else
138
+ param
139
+ end
140
+ end
141
+ end
142
+
143
+ @@env_log = create_log ENV['RESTCLIENT_LOG']
144
+
145
+ @@log = nil
146
+
147
+ def self.log # :nodoc:
148
+ @@env_log || @@log
149
+ end
150
+
108
151
  end
@@ -1,84 +1,115 @@
1
1
  module RestClient
2
+
2
3
  # This is the base RestClient exception class. Rescue it if you want to
3
4
  # catch any exception that your request might raise
5
+ # You can get the status code by e.http_code, or see anything about the
6
+ # response via e.response.
7
+ # For example, the entire result body (which is
8
+ # probably an HTML error page) is e.response.
4
9
  class Exception < RuntimeError
5
- def message(default=nil)
6
- self.class::ErrorMessage
7
- end
8
- end
9
-
10
- # Base RestClient exception when there's a response available
11
- class ExceptionWithResponse < Exception
12
- attr_accessor :response
10
+ attr_accessor :message, :response
13
11
 
14
- def initialize(response=nil)
12
+ def initialize response = nil
15
13
  @response = response
16
14
  end
17
15
 
18
16
  def http_code
17
+ # return integer for compatibility
19
18
  @response.code.to_i if @response
20
19
  end
21
20
 
22
21
  def http_body
23
- RestClient::Request.decode(@response['content-encoding'], @response.body) if @response
22
+ @response
24
23
  end
24
+
25
+ def inspect
26
+ "#{self.class} : #{http_code} #{message}"
27
+ end
28
+
25
29
  end
26
30
 
27
- # A redirect was encountered; caught by execute to retry with the new url.
28
- class Redirect < Exception
29
- ErrorMessage = "Redirect"
31
+ # Compatibility
32
+ class ExceptionWithResponse < Exception
33
+ end
30
34
 
31
- attr_accessor :url
35
+ # The request failed with an error code not managed by the code
36
+ class RequestFailed < ExceptionWithResponse
32
37
 
33
- def initialize(url)
34
- @url = url
38
+ def message
39
+ "HTTP status code #{http_code}"
40
+ end
41
+
42
+ def to_s
43
+ message
35
44
  end
36
45
  end
37
46
 
38
- class NotModified < ExceptionWithResponse
39
- ErrorMessage = 'NotModified'
47
+ # We will a create an exception for each status code, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
48
+ module Exceptions
49
+ # Map http status codes to the corresponding exception class
50
+ EXCEPTIONS_MAP = {}
40
51
  end
41
52
 
42
- # Authorization is required to access the resource specified.
43
- class Unauthorized < ExceptionWithResponse
44
- ErrorMessage = 'Unauthorized'
53
+ {300 => 'Multiple Choices',
54
+ 301 => 'Moved Permanently',
55
+ 302 => 'Found',
56
+ 303 => 'See Other',
57
+ 304 => 'Not Modified',
58
+ 305 => 'Use Proxy',
59
+ 400 => 'Bad Request',
60
+ 401 => 'Unauthorized',
61
+ 403 => 'Forbidden',
62
+ 404 => 'Resource Not Found',
63
+ 405 => 'Method Not Allowed',
64
+ 406 => 'Not Acceptable',
65
+ 407 => 'Proxy Authentication Required',
66
+ 408 => 'Request Timeout',
67
+ 409 => 'Conflict',
68
+ 410 => 'Gone',
69
+ 411 => 'Length Required',
70
+ 412 => 'Precondition Failed',
71
+ 413 => 'Request Entity Too Large',
72
+ 414 => 'Request-URI Too Long',
73
+ 415 => 'Unsupported Media Type',
74
+ 416 => 'Requested Range Not Satisfiable',
75
+ 417 => 'Expectation Failed',
76
+ 500 => 'Internal Server Error',
77
+ 501 => 'Not Implemented',
78
+ 502 => 'Bad Gateway',
79
+ 503 => 'Service Unavailable',
80
+ 504 => 'Gateway Timeout',
81
+ 505 => 'HTTP Version Not Supported'}.each_pair do |code, message|
82
+
83
+ # Compatibility
84
+ superclass = ([304, 401, 404].include? code) ? ExceptionWithResponse : RequestFailed
85
+ klass = Class.new(superclass) do
86
+ send(:define_method, :message) {message}
87
+ end
88
+ klass_constant = const_set message.gsub(/ /, '').gsub(/-/, ''), klass
89
+ Exceptions::EXCEPTIONS_MAP[code] = klass_constant
45
90
  end
46
91
 
47
- # No resource was found at the given URL.
48
- class ResourceNotFound < ExceptionWithResponse
49
- ErrorMessage = 'Resource not found'
92
+ # A redirect was encountered; caught by execute to retry with the new url.
93
+ class Redirect < Exception
94
+
95
+ message = 'Redirect'
96
+
97
+ attr_accessor :url
98
+
99
+ def initialize(url)
100
+ @url = url
101
+ end
50
102
  end
51
103
 
52
104
  # The server broke the connection prior to the request completing. Usually
53
105
  # this means it crashed, or sometimes that your network connection was
54
106
  # severed before it could complete.
55
107
  class ServerBrokeConnection < Exception
56
- ErrorMessage = 'Server broke connection'
108
+ message = 'Server broke connection'
57
109
  end
58
110
 
59
- # The server took too long to respond.
60
- class RequestTimeout < Exception
61
- ErrorMessage = 'Request timed out'
62
- end
63
111
 
64
- # The request failed, meaning the remote HTTP server returned a code other
65
- # than success, unauthorized, or redirect.
66
- #
67
- # The exception message attempts to extract the error from the XML, using
68
- # format returned by Rails: <errors><error>some message</error></errors>
69
- #
70
- # You can get the status code by e.http_code, or see anything about the
71
- # response via e.response. For example, the entire result body (which is
72
- # probably an HTML error page) is e.response.body.
73
- class RequestFailed < ExceptionWithResponse
74
- def message
75
- "HTTP status code #{http_code}"
76
- end
77
112
 
78
- def to_s
79
- message
80
- end
81
- end
82
113
  end
83
114
 
84
115
  # backwards compatibility