rest-client-next 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -8,10 +8,13 @@ of specifying actions: get, put, post, delete.
8
8
  require 'rest_client'
9
9
 
10
10
  RestClient.get 'http://example.com/resource'
11
+
11
12
  RestClient.get 'https://user:password@example.com/private/resource'
12
13
 
13
14
  RestClient.post 'http://example.com/resource', :param1 => 'one', :nested => { :param2 => 'two' }
14
15
 
16
+ RestClient.post "http://example.com/resource", { 'x' => 1 }.to_json, :content_type => :json, :accept => :json
17
+
15
18
  RestClient.delete 'http://example.com/resource'
16
19
 
17
20
  == Multipart
@@ -46,6 +49,51 @@ See RestClient::Resource module docs for details.
46
49
 
47
50
  See RestClient::Resource docs for details.
48
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
+
49
97
  == Lower-level access
50
98
 
51
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.
@@ -55,17 +103,17 @@ For cases not covered by the general API, you can use the RestClient::Resource c
55
103
  The restclient shell command gives an IRB session with RestClient already loaded:
56
104
 
57
105
  $ restclient
58
- >> RestClient.get 'http://example.com'
106
+ RestClient.get 'http://example.com'
59
107
 
60
108
  Specify a URL argument for get/post/put/delete on that resource:
61
109
 
62
110
  $ restclient http://example.com
63
- >> put '/resource', 'data'
111
+ put '/resource', 'data'
64
112
 
65
113
  Add a user and password for authenticated resources:
66
114
 
67
115
  $ restclient https://example.com user pass
68
- >> delete '/private/resource'
116
+ delete '/private/resource'
69
117
 
70
118
  Create ~/.restclient for named sessions:
71
119
 
@@ -82,11 +130,78 @@ Then invoke:
82
130
 
83
131
  $ restclient private_site
84
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
+
85
200
  == Meta
86
201
 
87
- 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
88
203
 
89
- 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.
90
205
 
91
206
  Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
92
207
 
data/Rakefile CHANGED
@@ -4,11 +4,11 @@ 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
- s.email = "adam@heroku.com"
11
- s.homepage = "http://rest-client.heroku.com/"
10
+ s.email = "rest.client@librelist.com"
11
+ s.homepage = "http://github.com/archiloque/rest-client"
12
12
  s.rubyforge_project = "rest-client"
13
13
  s.has_rdoc = true
14
14
  s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.6
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.
@@ -3,18 +3,18 @@ require 'zlib'
3
3
  require 'stringio'
4
4
 
5
5
  begin
6
- require 'net/https'
6
+ require 'net/https'
7
7
  rescue LoadError => e
8
- raise e unless RUBY_PLATFORM =~ /linux/
9
- raise LoadError, "no such file to load -- net/https. Try running apt-get install libopenssl-ruby"
8
+ raise e unless RUBY_PLATFORM =~ /linux/
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
 
@@ -63,45 +63,89 @@ require File.dirname(__FILE__) + '/restclient/net_http_ext'
63
63
  # => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
64
64
  #
65
65
  module RestClient
66
- def self.get(url, headers={})
67
- Request.execute(:method => :get, :url => url, :headers => headers)
68
- end
69
-
70
- def self.post(url, payload, headers={})
71
- Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers)
72
- end
73
-
74
- def self.put(url, payload, headers={})
75
- Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers)
76
- end
77
-
78
- def self.delete(url, headers={})
79
- Request.execute(:method => :delete, :url => url, :headers => headers)
80
- end
81
-
82
- def self.head(url, headers={})
83
- Request.execute(:method => :head, :url => url, :headers => headers)
84
- end
85
-
86
- class << self
87
- attr_accessor :proxy
88
- end
89
-
90
- # Print log of RestClient calls. Value can be stdout, stderr, or a filename.
91
- # You can also configure logging by the environment variable RESTCLIENT_LOG.
92
- def self.log=(log)
93
- @@log = log
94
- end
95
-
96
- def self.log # :nodoc:
97
- return ENV['RESTCLIENT_LOG'] if ENV['RESTCLIENT_LOG']
98
- return @@log if defined? @@log
99
- nil
100
- end
66
+
67
+ def self.get(url, headers={}, &block)
68
+ Request.execute(:method => :get, :url => url, :headers => headers, &block)
69
+ end
70
+
71
+ def self.post(url, payload, headers={}, &block)
72
+ Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers, &block)
73
+ end
74
+
75
+ def self.put(url, payload, headers={}, &block)
76
+ Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers, &block)
77
+ end
78
+
79
+ def self.delete(url, headers={}, &block)
80
+ Request.execute(:method => :delete, :url => url, :headers => headers, &block)
81
+ end
82
+
83
+ def self.head(url, headers={}, &block)
84
+ Request.execute(:method => :head, :url => url, :headers => headers, &block)
85
+ end
86
+
87
+ class << self
88
+ attr_accessor :proxy
89
+ end
90
+
91
+ # Setup the log for RestClient calls.
92
+ # Value should be a logger but can can be stdout, stderr, or a filename.
93
+ # You can also configure logging by the environment variable RESTCLIENT_LOG.
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
99
+ end
101
100
 
102
101
  def self.version
103
102
  version_path = File.dirname(__FILE__) + "/../VERSION"
104
103
  return File.read(version_path).chomp if File.file?(version_path)
105
104
  "0.0.0"
106
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
+
107
151
  end
@@ -1,88 +1,120 @@
1
1
  module RestClient
2
- # This is the base RestClient exception class. Rescue it if you want to
3
- # catch any exception that your request might raise
4
- 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
13
-
14
- def initialize(response=nil)
15
- @response = response
16
- end
17
-
18
- def http_code
19
- @response.code.to_i if @response
20
- end
21
-
22
- def http_body
23
- RestClient::Request.decode(@response['content-encoding'], @response.body) if @response
24
- end
25
- end
26
-
27
- # A redirect was encountered; caught by execute to retry with the new url.
28
- class Redirect < Exception
29
- ErrorMessage = "Redirect"
30
-
31
- attr_accessor :url
32
- def initialize(url)
33
- @url = url
34
- end
35
- end
36
-
37
- class NotModified < ExceptionWithResponse
38
- ErrorMessage = 'NotModified'
39
- end
40
-
41
- # Authorization is required to access the resource specified.
42
- class Unauthorized < ExceptionWithResponse
43
- ErrorMessage = 'Unauthorized'
44
- end
45
-
46
- # No resource was found at the given URL.
47
- class ResourceNotFound < ExceptionWithResponse
48
- ErrorMessage = 'Resource not found'
49
- end
50
-
51
- # The server broke the connection prior to the request completing. Usually
52
- # this means it crashed, or sometimes that your network connection was
53
- # severed before it could complete.
54
- class ServerBrokeConnection < Exception
55
- ErrorMessage = 'Server broke connection'
56
- end
57
-
58
- # The server took too long to respond.
59
- class RequestTimeout < Exception
60
- ErrorMessage = 'Request timed out'
61
- end
62
-
63
- # The request failed, meaning the remote HTTP server returned a code other
64
- # than success, unauthorized, or redirect.
65
- #
66
- # The exception message attempts to extract the error from the XML, using
67
- # format returned by Rails: <errors><error>some message</error></errors>
68
- #
69
- # You can get the status code by e.http_code, or see anything about the
70
- # response via e.response. For example, the entire result body (which is
71
- # probably an HTML error page) is e.response.body.
72
- class RequestFailed < ExceptionWithResponse
73
- def message
74
- "HTTP status code #{http_code}"
75
- end
76
-
77
- def to_s
78
- message
79
- end
80
- end
2
+
3
+ # This is the base RestClient exception class. Rescue it if you want to
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.
9
+ class Exception < RuntimeError
10
+ attr_accessor :message, :response
11
+
12
+ def initialize response = nil
13
+ @response = response
14
+ end
15
+
16
+ def http_code
17
+ # return integer for compatibility
18
+ @response.code.to_i if @response
19
+ end
20
+
21
+ def http_body
22
+ @response
23
+ end
24
+
25
+ def inspect
26
+ "#{self.class} : #{http_code} #{message}"
27
+ end
28
+
29
+ end
30
+
31
+ # Compatibility
32
+ class ExceptionWithResponse < Exception
33
+ end
34
+
35
+ # The request failed with an error code not managed by the code
36
+ class RequestFailed < ExceptionWithResponse
37
+
38
+ def message
39
+ "HTTP status code #{http_code}"
40
+ end
41
+
42
+ def to_s
43
+ message
44
+ end
45
+ end
46
+
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 = {}
51
+ end
52
+
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
90
+ end
91
+
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
102
+ end
103
+
104
+ # The server broke the connection prior to the request completing. Usually
105
+ # this means it crashed, or sometimes that your network connection was
106
+ # severed before it could complete.
107
+ class ServerBrokeConnection < Exception
108
+ message = 'Server broke connection'
109
+ end
110
+
111
+
112
+
81
113
  end
82
114
 
83
115
  # backwards compatibility
84
116
  class RestClient::Request
85
- Redirect = RestClient::Redirect
86
- Unauthorized = RestClient::Unauthorized
87
- RequestFailed = RestClient::RequestFailed
117
+ Redirect = RestClient::Redirect
118
+ Unauthorized = RestClient::Unauthorized
119
+ RequestFailed = RestClient::RequestFailed
88
120
  end