bartzon-httparty 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/.gitignore +8 -0
  2. data/History +216 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest +47 -0
  5. data/README.rdoc +54 -0
  6. data/Rakefile +89 -0
  7. data/VERSION +1 -0
  8. data/bartzon-httparty.gemspec +147 -0
  9. data/bin/httparty +108 -0
  10. data/cucumber.yml +1 -0
  11. data/examples/aaws.rb +32 -0
  12. data/examples/basic.rb +11 -0
  13. data/examples/custom_parsers.rb +67 -0
  14. data/examples/delicious.rb +37 -0
  15. data/examples/google.rb +16 -0
  16. data/examples/rubyurl.rb +14 -0
  17. data/examples/twitter.rb +31 -0
  18. data/examples/whoismyrep.rb +10 -0
  19. data/features/basic_authentication.feature +20 -0
  20. data/features/command_line.feature +7 -0
  21. data/features/deals_with_http_error_codes.feature +26 -0
  22. data/features/digest_authentication.feature +20 -0
  23. data/features/handles_compressed_responses.feature +19 -0
  24. data/features/handles_multiple_formats.feature +34 -0
  25. data/features/steps/env.rb +23 -0
  26. data/features/steps/httparty_response_steps.rb +26 -0
  27. data/features/steps/httparty_steps.rb +27 -0
  28. data/features/steps/mongrel_helper.rb +94 -0
  29. data/features/steps/remote_service_steps.rb +69 -0
  30. data/features/supports_redirection.feature +22 -0
  31. data/features/supports_timeout_option.feature +13 -0
  32. data/httparty.gemspec +146 -0
  33. data/lib/httparty.rb +383 -0
  34. data/lib/httparty/cookie_hash.rb +22 -0
  35. data/lib/httparty/core_extensions.rb +31 -0
  36. data/lib/httparty/exceptions.rb +26 -0
  37. data/lib/httparty/module_inheritable_attributes.rb +34 -0
  38. data/lib/httparty/net_digest_auth.rb +35 -0
  39. data/lib/httparty/parser.rb +141 -0
  40. data/lib/httparty/request.rb +277 -0
  41. data/lib/httparty/response.rb +79 -0
  42. data/spec/fixtures/delicious.xml +23 -0
  43. data/spec/fixtures/empty.xml +0 -0
  44. data/spec/fixtures/google.html +3 -0
  45. data/spec/fixtures/ssl/generate.sh +29 -0
  46. data/spec/fixtures/ssl/generated/1fe462c2.0 +15 -0
  47. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  48. data/spec/fixtures/ssl/generated/ca.crt +15 -0
  49. data/spec/fixtures/ssl/generated/ca.key +15 -0
  50. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  51. data/spec/fixtures/ssl/generated/server.crt +13 -0
  52. data/spec/fixtures/ssl/generated/server.key +15 -0
  53. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  54. data/spec/fixtures/twitter.json +1 -0
  55. data/spec/fixtures/twitter.xml +403 -0
  56. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  57. data/spec/httparty/cookie_hash_spec.rb +71 -0
  58. data/spec/httparty/parser_spec.rb +155 -0
  59. data/spec/httparty/request_spec.rb +488 -0
  60. data/spec/httparty/response_spec.rb +188 -0
  61. data/spec/httparty/ssl_spec.rb +55 -0
  62. data/spec/httparty_spec.rb +570 -0
  63. data/spec/spec.opts +3 -0
  64. data/spec/spec_helper.rb +20 -0
  65. data/spec/support/ssl_test_helper.rb +25 -0
  66. data/spec/support/ssl_test_server.rb +69 -0
  67. data/spec/support/stub_response.rb +30 -0
  68. data/website/css/common.css +47 -0
  69. data/website/index.html +73 -0
  70. metadata +244 -0
data/lib/httparty.rb ADDED
@@ -0,0 +1,383 @@
1
+ require 'pathname'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'uri'
5
+ require 'zlib'
6
+ require 'crack'
7
+
8
+ gem 'multipart-post'
9
+ require 'net/http/post/multipart'
10
+
11
+ dir = Pathname(__FILE__).dirname.expand_path
12
+
13
+ require dir + 'httparty/module_inheritable_attributes'
14
+ require dir + 'httparty/cookie_hash'
15
+ require dir + 'httparty/net_digest_auth'
16
+
17
+ module HTTParty
18
+ VERSION = "0.6.1".freeze
19
+ CRACK_DEPENDENCY = "0.1.8".freeze
20
+
21
+ module AllowedFormatsDeprecation
22
+ def const_missing(const)
23
+ if const.to_s =~ /AllowedFormats$/
24
+ Kernel.warn("Deprecated: Use HTTParty::Parser::SupportedFormats")
25
+ HTTParty::Parser::SupportedFormats
26
+ else
27
+ super
28
+ end
29
+ end
30
+ end
31
+
32
+ extend AllowedFormatsDeprecation
33
+
34
+ def self.included(base)
35
+ base.extend ClassMethods
36
+ base.send :include, HTTParty::ModuleInheritableAttributes
37
+ base.send(:mattr_inheritable, :default_options)
38
+ base.send(:mattr_inheritable, :default_cookies)
39
+ base.instance_variable_set("@default_options", {})
40
+ base.instance_variable_set("@default_cookies", CookieHash.new)
41
+ end
42
+
43
+ module ClassMethods
44
+ extend AllowedFormatsDeprecation
45
+
46
+ # Allows setting http proxy information to be used
47
+ #
48
+ # class Foo
49
+ # include HTTParty
50
+ # http_proxy 'http://foo.com', 80
51
+ # end
52
+ def http_proxy(addr=nil, port = nil)
53
+ default_options[:http_proxyaddr] = addr
54
+ default_options[:http_proxyport] = port
55
+ end
56
+
57
+ # Allows setting a base uri to be used for each request.
58
+ # Will normalize uri to include http, etc.
59
+ #
60
+ # class Foo
61
+ # include HTTParty
62
+ # base_uri 'twitter.com'
63
+ # end
64
+ def base_uri(uri=nil)
65
+ return default_options[:base_uri] unless uri
66
+ default_options[:base_uri] = HTTParty.normalize_base_uri(uri)
67
+ end
68
+
69
+ # Allows setting basic authentication username and password.
70
+ #
71
+ # class Foo
72
+ # include HTTParty
73
+ # basic_auth 'username', 'password'
74
+ # end
75
+ def basic_auth(u, p)
76
+ default_options[:basic_auth] = {:username => u, :password => p}
77
+ end
78
+
79
+ # Allows setting digest authentication username and password.
80
+ #
81
+ # class Foo
82
+ # include HTTParty
83
+ # digest_auth 'username', 'password'
84
+ # end
85
+ def digest_auth(u, p)
86
+ default_options[:digest_auth] = {:username => u, :password => p}
87
+ end
88
+
89
+ # Allows setting default parameters to be appended to each request.
90
+ # Great for api keys and such.
91
+ #
92
+ # class Foo
93
+ # include HTTParty
94
+ # default_params :api_key => 'secret', :another => 'foo'
95
+ # end
96
+ def default_params(h={})
97
+ raise ArgumentError, 'Default params must be a hash' unless h.is_a?(Hash)
98
+ default_options[:default_params] ||= {}
99
+ default_options[:default_params].merge!(h)
100
+ end
101
+
102
+ # Allows setting a default timeout for all HTTP calls
103
+ # Timeout is specified in seconds.
104
+ #
105
+ # class Foo
106
+ # include HTTParty
107
+ # default_timeout 10
108
+ # end
109
+ def default_timeout(t)
110
+ raise ArgumentError, 'Timeout must be an integer' unless t && t.is_a?(Integer)
111
+ default_options[:timeout] = t
112
+ end
113
+
114
+ # Set an output stream for debugging, defaults to $stderr.
115
+ # The output stream is passed on to Net::HTTP#set_debug_output.
116
+ #
117
+ # class Foo
118
+ # include HTTParty
119
+ # debug_output $stderr
120
+ # end
121
+ def debug_output(stream = $stderr)
122
+ default_options[:debug_output] = stream
123
+ end
124
+
125
+ # Allows setting HTTP headers to be used for each request.
126
+ #
127
+ # class Foo
128
+ # include HTTParty
129
+ # headers 'Accept' => 'text/html'
130
+ # end
131
+ def headers(h={})
132
+ raise ArgumentError, 'Headers must be a hash' unless h.is_a?(Hash)
133
+ default_options[:headers] ||= {}
134
+ default_options[:headers].merge!(h)
135
+ end
136
+
137
+ def cookies(h={})
138
+ raise ArgumentError, 'Cookies must be a hash' unless h.is_a?(Hash)
139
+ default_cookies.add_cookies(h)
140
+ end
141
+
142
+ # Allows setting the format with which to parse.
143
+ # Must be one of the allowed formats ie: json, xml
144
+ #
145
+ # class Foo
146
+ # include HTTParty
147
+ # format :json
148
+ # end
149
+ def format(f = nil)
150
+ if f.nil?
151
+ default_options[:format]
152
+ else
153
+ parser(Parser) if parser.nil?
154
+ default_options[:format] = f
155
+ validate_format
156
+ end
157
+ end
158
+
159
+ # Declare whether or not to follow redirects. When true, an
160
+ # {HTTParty::RedirectionTooDeep} error will raise upon encountering a
161
+ # redirect. You can then gain access to the response object via
162
+ # HTTParty::RedirectionTooDeep#response.
163
+ #
164
+ # @see HTTParty::ResponseError#response
165
+ #
166
+ # @example
167
+ # class Foo
168
+ # include HTTParty
169
+ # base_uri 'http://google.com'
170
+ # no_follow true
171
+ # end
172
+ #
173
+ # begin
174
+ # Foo.get('/')
175
+ # rescue HTTParty::RedirectionTooDeep => e
176
+ # puts e.response.body
177
+ # end
178
+ def no_follow(value = false)
179
+ default_options[:no_follow] = value
180
+ end
181
+
182
+ # Declare that you wish to maintain the chosen HTTP method across redirects.
183
+ # The default behavior is to follow redirects via the GET method.
184
+ # If you wish to maintain the original method, you can set this option to true.
185
+ #
186
+ # @example
187
+ # class Foo
188
+ # include HTTParty
189
+ # base_uri 'http://google.com'
190
+ # maintain_method_across_redirects true
191
+ # end
192
+
193
+ def maintain_method_across_redirects(value = true)
194
+ default_options[:maintain_method_across_redirects] = value
195
+ end
196
+
197
+ # Allows setting a PEM file to be used
198
+ #
199
+ # class Foo
200
+ # include HTTParty
201
+ # pem File.read('/home/user/my.pem')
202
+ # end
203
+ def pem(pem_contents)
204
+ default_options[:pem] = pem_contents
205
+ end
206
+
207
+ # Allows setting an OpenSSL certificate authority file
208
+ #
209
+ # class Foo
210
+ # include HTTParty
211
+ # ssl_ca_file '/etc/ssl/certs/ca-certificates.crt'
212
+ # end
213
+ def ssl_ca_file(path)
214
+ default_options[:ssl_ca_file] = path
215
+ end
216
+
217
+ # Allows setting an OpenSSL certificate authority path (directory)
218
+ #
219
+ # class Foo
220
+ # include HTTParty
221
+ # ssl_ca_path '/etc/ssl/certs/'
222
+ # end
223
+ def ssl_ca_path(path)
224
+ default_options[:ssl_ca_path] = path
225
+ end
226
+
227
+ # Allows setting a custom parser for the response.
228
+ #
229
+ # class Foo
230
+ # include HTTParty
231
+ # parser Proc.new {|data| ...}
232
+ # end
233
+ def parser(custom_parser = nil)
234
+ if custom_parser.nil?
235
+ default_options[:parser]
236
+ else
237
+ default_options[:parser] = custom_parser
238
+ validate_format
239
+ end
240
+ end
241
+
242
+ # Allows making a get request to a url.
243
+ #
244
+ # class Foo
245
+ # include HTTParty
246
+ # end
247
+ #
248
+ # # Simple get with full url
249
+ # Foo.get('http://foo.com/resource.json')
250
+ #
251
+ # # Simple get with full url and query parameters
252
+ # # ie: http://foo.com/resource.json?limit=10
253
+ # Foo.get('http://foo.com/resource.json', :query => {:limit => 10})
254
+ def get(path, options={})
255
+ perform_request Net::HTTP::Get, path, options
256
+ end
257
+
258
+ # Allows making a post request to a url.
259
+ #
260
+ # class Foo
261
+ # include HTTParty
262
+ # end
263
+ #
264
+ # # Simple post with full url and setting the body
265
+ # Foo.post('http://foo.com/resources', :body => {:bar => 'baz'})
266
+ #
267
+ # # Simple post with full url using :query option,
268
+ # # which gets set as form data on the request.
269
+ # Foo.post('http://foo.com/resources', :query => {:bar => 'baz'})
270
+ #
271
+ # Posting a few files as multipart:
272
+ # Foo.post('http://foo.com/resources',
273
+ # :multipart => {
274
+ # 'file' => {
275
+ # :path => '/tmp/foo.txt',
276
+ # :type => 'text/plain'
277
+ # },
278
+ # 'file2' => {
279
+ # :path => '/tmp/foo2.txt',
280
+ # :type => 'text/plain'
281
+ # }
282
+ # }
283
+ # )
284
+ def post(path, options={})
285
+ klass = options[:multipart] ? Net::HTTP::Post::Multipart : Net::HTTP::Post
286
+ perform_request klass, path, options
287
+ end
288
+
289
+ # Perform a PUT request to a path
290
+ def put(path, options={})
291
+ perform_request Net::HTTP::Put, path, options
292
+ end
293
+
294
+ # Perform a DELETE request to a path
295
+ def delete(path, options={})
296
+ perform_request Net::HTTP::Delete, path, options
297
+ end
298
+
299
+ # Perform a HEAD request to a path
300
+ def head(path, options={})
301
+ perform_request Net::HTTP::Head, path, options
302
+ end
303
+
304
+ # Perform an OPTIONS request to a path
305
+ def options(path, options={})
306
+ perform_request Net::HTTP::Options, path, options
307
+ end
308
+
309
+ def default_options #:nodoc:
310
+ @default_options
311
+ end
312
+
313
+ private
314
+
315
+ def perform_request(http_method, path, options) #:nodoc:
316
+ options = default_options.dup.merge(options)
317
+ process_cookies(options)
318
+ Request.new(http_method, path, options).perform
319
+ end
320
+
321
+ def process_cookies(options) #:nodoc:
322
+ return unless options[:cookies] || default_cookies.any?
323
+ options[:headers] ||= headers.dup
324
+ options[:headers]["cookie"] = cookies.merge(options.delete(:cookies) || {}).to_cookie_string
325
+ end
326
+
327
+ def validate_format
328
+ if format && parser.respond_to?(:supports_format?) && !parser.supports_format?(format)
329
+ raise UnsupportedFormat, "'#{format.inspect}' Must be one of: #{parser.supported_formats.map{|f| f.to_s}.sort.join(', ')}"
330
+ end
331
+ end
332
+ end
333
+
334
+ def self.normalize_base_uri(url) #:nodoc:
335
+ normalized_url = url.dup
336
+ use_ssl = (normalized_url =~ /^https/) || normalized_url.include?(':443')
337
+ ends_with_slash = normalized_url =~ /\/$/
338
+
339
+ normalized_url.chop! if ends_with_slash
340
+ normalized_url.gsub!(/^https?:\/\//i, '')
341
+
342
+ "http#{'s' if use_ssl}://#{normalized_url}"
343
+ end
344
+
345
+ class Basement #:nodoc:
346
+ include HTTParty
347
+ end
348
+
349
+ def self.get(*args)
350
+ Basement.get(*args)
351
+ end
352
+
353
+ def self.post(*args)
354
+ Basement.post(*args)
355
+ end
356
+
357
+ def self.put(*args)
358
+ Basement.put(*args)
359
+ end
360
+
361
+ def self.delete(*args)
362
+ Basement.delete(*args)
363
+ end
364
+
365
+ def self.head(*args)
366
+ Basement.head(*args)
367
+ end
368
+
369
+ def self.options(*args)
370
+ Basement.options(*args)
371
+ end
372
+
373
+ end
374
+
375
+ require dir + 'httparty/core_extensions'
376
+ require dir + 'httparty/exceptions'
377
+ require dir + 'httparty/parser'
378
+ require dir + 'httparty/request'
379
+ require dir + 'httparty/response'
380
+
381
+ if Crack::VERSION != HTTParty::CRACK_DEPENDENCY
382
+ warn "warning: HTTParty depends on version #{HTTParty::CRACK_DEPENDENCY} of crack, not #{Crack::VERSION}."
383
+ end
@@ -0,0 +1,22 @@
1
+ class HTTParty::CookieHash < Hash #:nodoc:
2
+
3
+ CLIENT_COOKIES = %w{path expires domain path secure HTTPOnly}
4
+
5
+ def add_cookies(value)
6
+ case value
7
+ when Hash
8
+ merge!(value)
9
+ when String
10
+ value.split('; ').each do |cookie|
11
+ array = cookie.split('=')
12
+ self[array[0].to_sym] = array[1]
13
+ end
14
+ else
15
+ raise "add_cookies only takes a Hash or a String"
16
+ end
17
+ end
18
+
19
+ def to_cookie_string
20
+ delete_if { |k, v| CLIENT_COOKIES.include?(k.to_s) }.collect { |k, v| "#{k}=#{v}" }.join("; ")
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ module HTTParty
2
+ if defined?(::BasicObject)
3
+ BasicObject = ::BasicObject #:nodoc:
4
+ else
5
+ class BasicObject #:nodoc:
6
+ instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
7
+ end
8
+ end
9
+ end
10
+
11
+ # 1.8.6 has mistyping of transitive in if statement
12
+ require "rexml/document"
13
+ module REXML #:nodoc:
14
+ class Document < Element #:nodoc:
15
+ def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
16
+ if xml_decl.encoding != "UTF-8" && !output.kind_of?(Output)
17
+ output = Output.new( output, xml_decl.encoding )
18
+ end
19
+ formatter = if indent > -1
20
+ if transitive
21
+ REXML::Formatters::Transitive.new( indent, ie_hack )
22
+ else
23
+ REXML::Formatters::Pretty.new( indent, ie_hack )
24
+ end
25
+ else
26
+ REXML::Formatters::Default.new( ie_hack )
27
+ end
28
+ formatter.write( self, output )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ module HTTParty
2
+ # Exception raised when you attempt to set a non-existant format
3
+ class UnsupportedFormat < StandardError; end
4
+
5
+ # Exception raised when using a URI scheme other than HTTP or HTTPS
6
+ class UnsupportedURIScheme < StandardError; end
7
+
8
+ # @abstract Exceptions which inherit from ResponseError contain the Net::HTTP
9
+ # response object accessible via the {#response} method.
10
+ class ResponseError < StandardError
11
+ # Returns the response of the last request
12
+ # @return [Net::HTTPResponse] A subclass of Net::HTTPResponse, e.g.
13
+ # Net::HTTPOK
14
+ attr_reader :response
15
+
16
+ # Instantiate an instance of ResponseError with a Net::HTTPResponse object
17
+ # @param [Net::HTTPResponse]
18
+ def initialize(response)
19
+ @response = response
20
+ end
21
+ end
22
+
23
+ # Exception that is raised when request has redirected too many times.
24
+ # Calling {#response} returns the Net:HTTP response object.
25
+ class RedirectionTooDeep < ResponseError; end
26
+ end