jugend-httparty 0.5.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +7 -0
  2. data/History +209 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest +47 -0
  5. data/README.rdoc +54 -0
  6. data/Rakefile +80 -0
  7. data/VERSION +1 -0
  8. data/bin/httparty +108 -0
  9. data/cucumber.yml +1 -0
  10. data/examples/aaws.rb +32 -0
  11. data/examples/basic.rb +11 -0
  12. data/examples/custom_parsers.rb +67 -0
  13. data/examples/delicious.rb +37 -0
  14. data/examples/google.rb +16 -0
  15. data/examples/rubyurl.rb +14 -0
  16. data/examples/twitter.rb +31 -0
  17. data/examples/whoismyrep.rb +10 -0
  18. data/features/basic_authentication.feature +20 -0
  19. data/features/command_line.feature +7 -0
  20. data/features/deals_with_http_error_codes.feature +26 -0
  21. data/features/handles_multiple_formats.feature +34 -0
  22. data/features/steps/env.rb +23 -0
  23. data/features/steps/httparty_response_steps.rb +26 -0
  24. data/features/steps/httparty_steps.rb +27 -0
  25. data/features/steps/mongrel_helper.rb +78 -0
  26. data/features/steps/remote_service_steps.rb +61 -0
  27. data/features/supports_redirection.feature +22 -0
  28. data/features/supports_timeout_option.feature +13 -0
  29. data/jugend-httparty.gemspec +127 -0
  30. data/lib/httparty/cookie_hash.rb +22 -0
  31. data/lib/httparty/core_extensions.rb +31 -0
  32. data/lib/httparty/exceptions.rb +26 -0
  33. data/lib/httparty/module_inheritable_attributes.rb +25 -0
  34. data/lib/httparty/parser.rb +141 -0
  35. data/lib/httparty/request.rb +206 -0
  36. data/lib/httparty/response.rb +62 -0
  37. data/lib/httparty.rb +343 -0
  38. data/spec/fixtures/delicious.xml +23 -0
  39. data/spec/fixtures/empty.xml +0 -0
  40. data/spec/fixtures/google.html +3 -0
  41. data/spec/fixtures/twitter.json +1 -0
  42. data/spec/fixtures/twitter.xml +403 -0
  43. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  44. data/spec/httparty/cookie_hash_spec.rb +71 -0
  45. data/spec/httparty/parser_spec.rb +154 -0
  46. data/spec/httparty/request_spec.rb +415 -0
  47. data/spec/httparty/response_spec.rb +83 -0
  48. data/spec/httparty_spec.rb +514 -0
  49. data/spec/spec.opts +3 -0
  50. data/spec/spec_helper.rb +19 -0
  51. data/spec/support/stub_response.rb +30 -0
  52. data/website/css/common.css +47 -0
  53. data/website/index.html +73 -0
  54. metadata +209 -0
@@ -0,0 +1,141 @@
1
+ module HTTParty
2
+ # The default parser used by HTTParty, supports xml, json, html, yaml, and
3
+ # plain text.
4
+ #
5
+ # == Custom Parsers
6
+ #
7
+ # If you'd like to do your own custom parsing, subclassing HTTParty::Parser
8
+ # will make that process much easier. There are a few different ways you can
9
+ # utilize HTTParty::Parser as a superclass.
10
+ #
11
+ # @example Intercept the parsing for all formats
12
+ # class SimpleParser < HTTParty::Parser
13
+ # def parse
14
+ # perform_parsing
15
+ # end
16
+ # end
17
+ #
18
+ # @example Add the atom format and parsing method to the default parser
19
+ # class AtomParsingIncluded < HTTParty::Parser
20
+ # SupportedFormats.merge!(
21
+ # {"application/atom+xml" => :atom}
22
+ # )
23
+ #
24
+ # def atom
25
+ # perform_atom_parsing
26
+ # end
27
+ # end
28
+ #
29
+ # @example Only support the atom format
30
+ # class ParseOnlyAtom < HTTParty::Parser
31
+ # SupportedFormats = {"application/atom+xml" => :atom}
32
+ #
33
+ # def atom
34
+ # perform_atom_parsing
35
+ # end
36
+ # end
37
+ #
38
+ # @abstract Read the Custom Parsers section for more information.
39
+ class Parser
40
+ SupportedFormats = {
41
+ 'text/xml' => :xml,
42
+ 'application/xml' => :xml,
43
+ 'application/json' => :json,
44
+ 'text/json' => :json,
45
+ 'application/javascript' => :json,
46
+ 'text/javascript' => :json,
47
+ 'text/html' => :html,
48
+ 'application/x-yaml' => :yaml,
49
+ 'text/yaml' => :yaml,
50
+ 'text/plain' => :plain
51
+ }
52
+
53
+ # The response body of the request
54
+ # @return [String]
55
+ attr_reader :body
56
+
57
+ # The intended parsing format for the request
58
+ # @return [Symbol] e.g. :json
59
+ attr_reader :format
60
+
61
+ # Instantiate the parser and call {#parse}.
62
+ # @param [String] body the response body
63
+ # @param [Symbol] format the response format
64
+ # @return parsed response
65
+ def self.call(body, format)
66
+ new(body, format).parse
67
+ end
68
+
69
+ # @return [Hash] the SupportedFormats hash
70
+ def self.formats
71
+ const_get(:SupportedFormats)
72
+ end
73
+
74
+ # @param [String] mimetype response MIME type
75
+ # @return [Symbol]
76
+ # @return [nil] mime type not supported
77
+ def self.format_from_mimetype(mimetype)
78
+ formats[formats.keys.detect {|k| mimetype.include?(k)}]
79
+ end
80
+
81
+ # @return [Array<Symbol>] list of supported formats
82
+ def self.supported_formats
83
+ formats.values.uniq
84
+ end
85
+
86
+ # @param [Symbol] format e.g. :json, :xml
87
+ # @return [Boolean]
88
+ def self.supports_format?(format)
89
+ supported_formats.include?(format)
90
+ end
91
+
92
+ def initialize(body, format)
93
+ @body = body
94
+ @format = format
95
+ end
96
+ private_class_method :new
97
+
98
+ # @return [Object] the parsed body
99
+ # @return [nil] when the response body is nil or an empty string
100
+ def parse
101
+ return nil if body.nil? || body.empty?
102
+ if supports_format?
103
+ parse_supported_format
104
+ else
105
+ body
106
+ end
107
+ end
108
+
109
+ protected
110
+
111
+ def xml
112
+ Crack::XML.parse(body)
113
+ end
114
+
115
+ def json
116
+ Crack::JSON.parse(body)
117
+ end
118
+
119
+ def yaml
120
+ YAML.load(body)
121
+ end
122
+
123
+ def html
124
+ body
125
+ end
126
+
127
+ def plain
128
+ body
129
+ end
130
+
131
+ def supports_format?
132
+ self.class.supports_format?(format)
133
+ end
134
+
135
+ def parse_supported_format
136
+ send(format)
137
+ rescue NoMethodError
138
+ raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format."
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,206 @@
1
+ require 'uri'
2
+
3
+ module HTTParty
4
+ class Request #:nodoc:
5
+ SupportedHTTPMethods = [
6
+ Net::HTTP::Get,
7
+ Net::HTTP::Post,
8
+ Net::HTTP::Put,
9
+ Net::HTTP::Delete,
10
+ Net::HTTP::Head,
11
+ Net::HTTP::Options
12
+ ]
13
+
14
+ SupportedURISchemes = [URI::HTTP, URI::HTTPS]
15
+
16
+ attr_accessor :http_method, :path, :options, :last_response, :redirect
17
+
18
+ def initialize(http_method, path, o={})
19
+ self.http_method = http_method
20
+ self.path = path
21
+ self.options = {
22
+ :limit => o.delete(:no_follow) ? 1 : 5,
23
+ :default_params => {},
24
+ :parser => Parser
25
+ }.merge(o)
26
+ end
27
+
28
+ def path=(uri)
29
+ @path = URI.parse(uri)
30
+ end
31
+
32
+ def uri
33
+ new_uri = path.relative? ? URI.parse("#{options[:base_uri]}#{path}") : path
34
+
35
+ # avoid double query string on redirects [#12]
36
+ unless redirect
37
+ new_uri.query = query_string(new_uri)
38
+ end
39
+
40
+ unless SupportedURISchemes.include? new_uri.class
41
+ raise UnsupportedURIScheme, "'#{new_uri}' Must be HTTP or HTTPS"
42
+ end
43
+
44
+ new_uri
45
+ end
46
+
47
+ def format
48
+ options[:format]
49
+ end
50
+
51
+ def parser
52
+ options[:parser]
53
+ end
54
+
55
+ def perform
56
+ validate
57
+ setup_raw_request
58
+ get_response
59
+ handle_response
60
+ end
61
+
62
+ private
63
+
64
+ def http
65
+ http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport])
66
+ http.use_ssl = ssl_implied?
67
+
68
+ if options[:timeout] && options[:timeout].is_a?(Integer)
69
+ http.open_timeout = options[:timeout]
70
+ http.read_timeout = options[:timeout]
71
+ end
72
+
73
+ if options[:pem] && http.use_ssl?
74
+ http.cert = OpenSSL::X509::Certificate.new(options[:pem])
75
+ http.key = OpenSSL::PKey::RSA.new(options[:pem])
76
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
77
+ else
78
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
79
+ end
80
+
81
+ if options[:debug_output]
82
+ http.set_debug_output(options[:debug_output])
83
+ end
84
+
85
+ http
86
+ end
87
+
88
+ def ssl_implied?
89
+ uri.port == 443 || uri.instance_of?(URI::HTTPS)
90
+ end
91
+
92
+ def body
93
+ options[:body].is_a?(Hash) ? options[:body].to_params : options[:body]
94
+ end
95
+
96
+ def credentials
97
+ options[:basic_auth] || options[:digest_auth]
98
+ end
99
+
100
+ def username
101
+ credentials[:username]
102
+ end
103
+
104
+ def password
105
+ credentials[:password]
106
+ end
107
+
108
+ def setup_raw_request
109
+ @raw_request = http_method.new(uri.request_uri)
110
+ @raw_request.body = body if body
111
+ @raw_request.initialize_http_header(options[:headers])
112
+ @raw_request.basic_auth(username, password) if options[:basic_auth]
113
+ setup_digest_auth if options[:digest_auth]
114
+ end
115
+
116
+ def setup_digest_auth
117
+ res = http.head(uri.request_uri, options[:headers])
118
+ if res['www-authenticate'] != nil && res['www-authenticate'].length > 0
119
+ @raw_request.digest_auth(username, password, res)
120
+ end
121
+ end
122
+
123
+ def perform_actual_request
124
+ http.request(@raw_request)
125
+ end
126
+
127
+ def get_response
128
+ self.last_response = perform_actual_request
129
+ options[:format] ||= format_from_mimetype(last_response['content-type'])
130
+ end
131
+
132
+ def query_string(uri)
133
+ query_string_parts = []
134
+ query_string_parts << uri.query unless uri.query.nil?
135
+
136
+ if options[:query].is_a?(Hash)
137
+ query_string_parts << options[:default_params].merge(options[:query]).to_params
138
+ else
139
+ query_string_parts << options[:default_params].to_params unless options[:default_params].empty?
140
+ query_string_parts << options[:query] unless options[:query].nil?
141
+ end
142
+
143
+ query_string_parts.size > 0 ? query_string_parts.join('&') : nil
144
+ end
145
+
146
+ # Raises exception Net::XXX (http error code) if an http error occured
147
+ def handle_response
148
+ case last_response
149
+ when Net::HTTPMultipleChoice, # 300
150
+ Net::HTTPMovedPermanently, # 301
151
+ Net::HTTPFound, # 302
152
+ Net::HTTPSeeOther, # 303
153
+ Net::HTTPUseProxy, # 305
154
+ Net::HTTPTemporaryRedirect
155
+ if last_response.key?('location')
156
+ options[:limit] -= 1
157
+ self.path = last_response['location']
158
+ self.redirect = true
159
+ self.http_method = Net::HTTP::Get unless options[:maintain_method_across_redirects]
160
+ capture_cookies(last_response)
161
+ perform
162
+ else
163
+ last_response
164
+ end
165
+ else
166
+ Response.new(last_response, parse_response(last_response.body))
167
+ end
168
+ end
169
+
170
+ def parse_response(body)
171
+ parser.call(body, format)
172
+ end
173
+
174
+ def capture_cookies(response)
175
+ return unless response['Set-Cookie']
176
+ cookies_hash = HTTParty::CookieHash.new()
177
+ cookies_hash.add_cookies(options[:headers]['Cookie']) if options[:headers] && options[:headers]['Cookie']
178
+ cookies_hash.add_cookies(response['Set-Cookie'])
179
+ options[:headers] ||= {}
180
+ options[:headers]['Cookie'] = cookies_hash.to_cookie_string
181
+ end
182
+
183
+ # Uses the HTTP Content-Type header to determine the format of the
184
+ # response It compares the MIME type returned to the types stored in the
185
+ # SupportedFormats hash
186
+ def format_from_mimetype(mimetype)
187
+ if mimetype && parser.respond_to?(:format_from_mimetype)
188
+ parser.format_from_mimetype(mimetype)
189
+ end
190
+ end
191
+
192
+ def validate
193
+ raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0
194
+ raise ArgumentError, 'only get, post, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method)
195
+ raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].is_a?(Hash)
196
+ raise ArgumentError, 'only one authentication method, :basic_auth or :digest_auth may be used at a time' if options[:basic_auth] && options[:digest_auth]
197
+ raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].is_a?(Hash)
198
+ raise ArgumentError, ':digest_auth must be a hash' if options[:digest_auth] && !options[:digest_auth].is_a?(Hash)
199
+ raise ArgumentError, ':query must be hash if using HTTP Post' if post? && !options[:query].nil? && !options[:query].is_a?(Hash)
200
+ end
201
+
202
+ def post?
203
+ Net::HTTP::Post == http_method
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,62 @@
1
+ module HTTParty
2
+ class Response < HTTParty::BasicObject #:nodoc:
3
+ class Headers
4
+ include Net::HTTPHeader
5
+
6
+ def initialize(header)
7
+ @header = header
8
+ end
9
+
10
+ def ==(other)
11
+ @header == other
12
+ end
13
+
14
+ def inspect
15
+ @header.inspect
16
+ end
17
+
18
+ def method_missing(name, *args, &block)
19
+ if @header.respond_to?(name)
20
+ @header.send(name, *args, &block)
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def respond_to?(method)
27
+ super || @header.respond_to?(method)
28
+ end
29
+ end
30
+
31
+ attr_reader :response, :parsed_response, :body, :headers
32
+
33
+ def initialize(response, parsed_response)
34
+ @response = response
35
+ @body = response.body
36
+ @parsed_response = parsed_response
37
+ @headers = Headers.new(response.to_hash)
38
+ end
39
+
40
+ def class
41
+ Object.instance_method(:class).bind(self).call
42
+ end
43
+
44
+ def code
45
+ response.code.to_i
46
+ end
47
+
48
+ def inspect
49
+ %(<#{self.class} @response=#{response.inspect}>)
50
+ end
51
+
52
+ def method_missing(name, *args, &block)
53
+ if parsed_response.respond_to?(name)
54
+ parsed_response.send(name, *args, &block)
55
+ elsif response.respond_to?(name)
56
+ response.send(name, *args, &block)
57
+ else
58
+ super
59
+ end
60
+ end
61
+ end
62
+ end