bartzon-httparty 0.6.1

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.
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