geetarista-httparty 0.4.5

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 (48) hide show
  1. data/History +131 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Manifest +47 -0
  4. data/README.rdoc +54 -0
  5. data/Rakefile +48 -0
  6. data/bin/httparty +98 -0
  7. data/cucumber.yml +1 -0
  8. data/examples/aaws.rb +32 -0
  9. data/examples/basic.rb +11 -0
  10. data/examples/delicious.rb +37 -0
  11. data/examples/google.rb +16 -0
  12. data/examples/rubyurl.rb +14 -0
  13. data/examples/twitter.rb +31 -0
  14. data/examples/whoismyrep.rb +10 -0
  15. data/features/basic_authentication.feature +20 -0
  16. data/features/command_line.feature +7 -0
  17. data/features/deals_with_http_error_codes.feature +26 -0
  18. data/features/handles_multiple_formats.feature +34 -0
  19. data/features/steps/env.rb +15 -0
  20. data/features/steps/httparty_response_steps.rb +26 -0
  21. data/features/steps/httparty_steps.rb +15 -0
  22. data/features/steps/mongrel_helper.rb +55 -0
  23. data/features/steps/remote_service_steps.rb +47 -0
  24. data/features/supports_redirection.feature +22 -0
  25. data/httparty.gemspec +41 -0
  26. data/lib/httparty.rb +206 -0
  27. data/lib/httparty/cookie_hash.rb +9 -0
  28. data/lib/httparty/core_extensions.rb +29 -0
  29. data/lib/httparty/exceptions.rb +7 -0
  30. data/lib/httparty/module_inheritable_attributes.rb +25 -0
  31. data/lib/httparty/request.rb +141 -0
  32. data/lib/httparty/response.rb +18 -0
  33. data/lib/httparty/version.rb +3 -0
  34. data/spec/fixtures/delicious.xml +23 -0
  35. data/spec/fixtures/empty.xml +0 -0
  36. data/spec/fixtures/google.html +3 -0
  37. data/spec/fixtures/twitter.json +1 -0
  38. data/spec/fixtures/twitter.xml +403 -0
  39. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  40. data/spec/httparty/cookie_hash_spec.rb +38 -0
  41. data/spec/httparty/request_spec.rb +199 -0
  42. data/spec/httparty/response_spec.rb +62 -0
  43. data/spec/httparty_spec.rb +290 -0
  44. data/spec/spec.opts +3 -0
  45. data/spec/spec_helper.rb +21 -0
  46. data/website/css/common.css +47 -0
  47. data/website/index.html +74 -0
  48. metadata +133 -0
data/lib/httparty.rb ADDED
@@ -0,0 +1,206 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require 'net/http'
4
+ require 'net/https'
5
+ require 'httparty/module_inheritable_attributes'
6
+ require 'rubygems'
7
+ gem 'crack'
8
+ require 'crack'
9
+
10
+ require 'httparty/cookie_hash'
11
+
12
+ module HTTParty
13
+
14
+ AllowedFormats = {
15
+ 'text/xml' => :xml,
16
+ 'application/xml' => :xml,
17
+ 'application/json' => :json,
18
+ 'text/json' => :json,
19
+ 'application/javascript' => :json,
20
+ 'text/javascript' => :json,
21
+ 'text/html' => :html,
22
+ 'application/x-yaml' => :yaml,
23
+ 'text/yaml' => :yaml,
24
+ 'text/plain' => :plain
25
+ } unless defined?(AllowedFormats)
26
+
27
+ def self.included(base)
28
+ base.extend ClassMethods
29
+ base.send :include, HTTParty::ModuleInheritableAttributes
30
+ base.send(:mattr_inheritable, :default_options)
31
+ base.send(:mattr_inheritable, :default_cookies)
32
+ base.instance_variable_set("@default_options", {})
33
+ base.instance_variable_set("@default_cookies", CookieHash.new)
34
+ end
35
+
36
+ module ClassMethods
37
+ # Allows setting http proxy information to be used
38
+ #
39
+ # class Foo
40
+ # include HTTParty
41
+ # http_proxy 'http://foo.com', 80
42
+ # end
43
+ def http_proxy(addr=nil, port = nil)
44
+ default_options[:http_proxyaddr] = addr
45
+ default_options[:http_proxyport] = port
46
+ end
47
+
48
+ # Allows setting a base uri to be used for each request.
49
+ # Will normalize uri to include http, etc.
50
+ #
51
+ # class Foo
52
+ # include HTTParty
53
+ # base_uri 'twitter.com'
54
+ # end
55
+ def base_uri(uri=nil)
56
+ return default_options[:base_uri] unless uri
57
+ default_options[:base_uri] = HTTParty.normalize_base_uri(uri)
58
+ end
59
+
60
+ # Allows setting basic authentication username and password.
61
+ #
62
+ # class Foo
63
+ # include HTTParty
64
+ # basic_auth 'username', 'password'
65
+ # end
66
+ def basic_auth(u, p)
67
+ default_options[:basic_auth] = {:username => u, :password => p}
68
+ end
69
+
70
+ # Allows setting default parameters to be appended to each request.
71
+ # Great for api keys and such.
72
+ #
73
+ # class Foo
74
+ # include HTTParty
75
+ # default_params :api_key => 'secret', :another => 'foo'
76
+ # end
77
+ def default_params(h={})
78
+ raise ArgumentError, 'Default params must be a hash' unless h.is_a?(Hash)
79
+ default_options[:default_params] ||= {}
80
+ default_options[:default_params].merge!(h)
81
+ end
82
+
83
+ # Allows setting a base uri to be used for each request.
84
+ #
85
+ # class Foo
86
+ # include HTTParty
87
+ # headers 'Accept' => 'text/html'
88
+ # end
89
+ def headers(h={})
90
+ raise ArgumentError, 'Headers must be a hash' unless h.is_a?(Hash)
91
+ default_options[:headers] ||= {}
92
+ default_options[:headers].merge!(h)
93
+ end
94
+
95
+ def cookies(h={})
96
+ raise ArgumentError, 'Cookies must be a hash' unless h.is_a?(Hash)
97
+ default_cookies.add_cookies(h)
98
+ end
99
+
100
+ # Allows setting the format with which to parse.
101
+ # Must be one of the allowed formats ie: json, xml
102
+ #
103
+ # class Foo
104
+ # include HTTParty
105
+ # format :json
106
+ # end
107
+ def format(f)
108
+ raise UnsupportedFormat, "Must be one of: #{AllowedFormats.values.map { |v| v.to_s }.uniq.sort.join(', ')}" unless AllowedFormats.value?(f)
109
+ default_options[:format] = f
110
+ end
111
+
112
+ # Allows making a get request to a url.
113
+ #
114
+ # class Foo
115
+ # include HTTParty
116
+ # end
117
+ #
118
+ # # Simple get with full url
119
+ # Foo.get('http://foo.com/resource.json')
120
+ #
121
+ # # Simple get with full url and query parameters
122
+ # # ie: http://foo.com/resource.json?limit=10
123
+ # Foo.get('http://foo.com/resource.json', :query => {:limit => 10})
124
+ def get(path, options={})
125
+ perform_request Net::HTTP::Get, path, options
126
+ end
127
+
128
+ # Allows making a post request to a url.
129
+ #
130
+ # class Foo
131
+ # include HTTParty
132
+ # end
133
+ #
134
+ # # Simple post with full url and setting the body
135
+ # Foo.post('http://foo.com/resources', :body => {:bar => 'baz'})
136
+ #
137
+ # # Simple post with full url using :query option,
138
+ # # which gets set as form data on the request.
139
+ # Foo.post('http://foo.com/resources', :query => {:bar => 'baz'})
140
+ def post(path, options={})
141
+ perform_request Net::HTTP::Post, path, options
142
+ end
143
+
144
+ def put(path, options={})
145
+ perform_request Net::HTTP::Put, path, options
146
+ end
147
+
148
+ def delete(path, options={})
149
+ perform_request Net::HTTP::Delete, path, options
150
+ end
151
+
152
+ def default_options #:nodoc:
153
+ @default_options
154
+ end
155
+
156
+ private
157
+ def perform_request(http_method, path, options) #:nodoc:
158
+ process_cookies(options)
159
+ Request.new(http_method, path, default_options.dup.merge(options)).perform
160
+ end
161
+
162
+ def process_cookies(options) #:nodoc:
163
+ return unless options[:cookies] || default_cookies
164
+ options[:headers] ||= {}
165
+ options[:headers]["cookie"] = cookies.merge(options[:cookies] || {}).to_cookie_string
166
+
167
+ options.delete(:cookies)
168
+ end
169
+ end
170
+
171
+ def self.normalize_base_uri(url) #:nodoc:
172
+ normalized_url = url.dup
173
+ use_ssl = (normalized_url =~ /^https/) || normalized_url.include?(':443')
174
+ ends_with_slash = normalized_url =~ /\/$/
175
+
176
+ normalized_url.chop! if ends_with_slash
177
+ normalized_url.gsub!(/^https?:\/\//i, '')
178
+
179
+ "http#{'s' if use_ssl}://#{normalized_url}"
180
+ end
181
+
182
+ class Basement #:nodoc:
183
+ include HTTParty
184
+ end
185
+
186
+ def self.get(*args)
187
+ Basement.get(*args)
188
+ end
189
+
190
+ def self.post(*args)
191
+ Basement.post(*args)
192
+ end
193
+
194
+ def self.put(*args)
195
+ Basement.put(*args)
196
+ end
197
+
198
+ def self.delete(*args)
199
+ Basement.delete(*args)
200
+ end
201
+ end
202
+
203
+ require 'httparty/core_extensions'
204
+ require 'httparty/exceptions'
205
+ require 'httparty/request'
206
+ require 'httparty/response'
@@ -0,0 +1,9 @@
1
+ class HTTParty::CookieHash < Hash #:nodoc:
2
+ def add_cookies(hash)
3
+ merge!(hash)
4
+ end
5
+
6
+ def to_cookie_string
7
+ collect { |k, v| "#{k}=#{v}" }.join("; ")
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ if RUBY_VERSION.to_f == 1.8
2
+ class BlankSlate #:nodoc:
3
+ instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval|object_id/ }
4
+ end
5
+ else
6
+ class BlankSlate < BasicObject; end
7
+ end
8
+
9
+ # 1.8.6 has mistyping of transitive in if statement
10
+ require "rexml/document"
11
+ module REXML #:nodoc:
12
+ class Document < Element #:nodoc:
13
+ def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
14
+ if xml_decl.encoding != "UTF-8" && !output.kind_of?(Output)
15
+ output = Output.new( output, xml_decl.encoding )
16
+ end
17
+ formatter = if indent > -1
18
+ if transitive
19
+ REXML::Formatters::Transitive.new( indent, ie_hack )
20
+ else
21
+ REXML::Formatters::Pretty.new( indent, ie_hack )
22
+ end
23
+ else
24
+ REXML::Formatters::Default.new( ie_hack )
25
+ end
26
+ formatter.write( self, output )
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module HTTParty
2
+ # Exception raised when you attempt to set a non-existant format
3
+ class UnsupportedFormat < StandardError; end
4
+
5
+ # Exception that is raised when request has redirected too many times
6
+ class RedirectionTooDeep < StandardError; end
7
+ end
@@ -0,0 +1,25 @@
1
+ module HTTParty
2
+ module ModuleInheritableAttributes #:nodoc:
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods #:nodoc:
8
+ def mattr_inheritable(*args)
9
+ @mattr_inheritable_attrs ||= [:mattr_inheritable_attrs]
10
+ @mattr_inheritable_attrs += args
11
+ args.each do |arg|
12
+ module_eval %(class << self; attr_accessor :#{arg} end)
13
+ end
14
+ @mattr_inheritable_attrs
15
+ end
16
+
17
+ def inherited(subclass)
18
+ @mattr_inheritable_attrs.each do |inheritable_attribute|
19
+ instance_var = "@#{inheritable_attribute}"
20
+ subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,141 @@
1
+ require 'uri'
2
+
3
+ module HTTParty
4
+ class Request #:nodoc:
5
+ SupportedHTTPMethods = [Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Put, Net::HTTP::Delete]
6
+
7
+ attr_accessor :http_method, :path, :options
8
+
9
+ def initialize(http_method, path, o={})
10
+ self.http_method = http_method
11
+ self.path = path
12
+ self.options = {
13
+ :limit => o.delete(:no_follow) ? 0 : 5,
14
+ :default_params => {},
15
+ }.merge(o)
16
+ end
17
+
18
+ def path=(uri)
19
+ @path = URI.parse(uri)
20
+ end
21
+
22
+ def uri
23
+ new_uri = path.relative? ? URI.parse("#{options[:base_uri]}#{path}") : path
24
+
25
+ # avoid double query string on redirects [#12]
26
+ unless @redirect
27
+ new_uri.query = query_string(new_uri)
28
+ end
29
+
30
+ new_uri
31
+ end
32
+
33
+ def format
34
+ options[:format]
35
+ end
36
+
37
+ def perform
38
+ validate
39
+ setup_raw_request
40
+ handle_response(get_response)
41
+ end
42
+
43
+ private
44
+ def http
45
+ http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport])
46
+ http.use_ssl = (uri.port == 443)
47
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
48
+ http
49
+ end
50
+
51
+ def body
52
+ options[:body].is_a?(Hash) ? options[:body].to_params : options[:body]
53
+ end
54
+
55
+ def username
56
+ options[:basic_auth][:username]
57
+ end
58
+
59
+ def password
60
+ options[:basic_auth][:password]
61
+ end
62
+
63
+ def setup_raw_request
64
+ @raw_request = http_method.new(uri.request_uri)
65
+ @raw_request.body = body if body
66
+ @raw_request.initialize_http_header(options[:headers])
67
+ @raw_request.basic_auth(username, password) if options[:basic_auth]
68
+ end
69
+
70
+ def perform_actual_request
71
+ http.request(@raw_request)
72
+ end
73
+
74
+ def get_response
75
+ response = perform_actual_request
76
+ options[:format] ||= format_from_mimetype(response['content-type'])
77
+ response
78
+ end
79
+
80
+ def query_string(uri)
81
+ query_string_parts = []
82
+ query_string_parts << uri.query unless uri.query.nil?
83
+
84
+ if options[:query].is_a?(Hash)
85
+ query_string_parts << options[:default_params].merge(options[:query]).to_params
86
+ else
87
+ query_string_parts << options[:default_params].to_params unless options[:default_params].nil?
88
+ query_string_parts << options[:query] unless options[:query].nil?
89
+ end
90
+
91
+ query_string_parts.size > 0 ? query_string_parts.join('&') : nil
92
+ end
93
+
94
+ # Raises exception Net::XXX (http error code) if an http error occured
95
+ def handle_response(response)
96
+ case response
97
+ when Net::HTTPRedirection
98
+ options[:limit] -= 1
99
+ self.path = response['location']
100
+ @redirect = true
101
+ perform
102
+ else
103
+ parsed_response = parse_response(response.body)
104
+ Response.new(parsed_response, response.body, response.code, response.message, response.to_hash)
105
+ end
106
+ end
107
+
108
+ def parse_response(body)
109
+ return nil if body.nil? or body.empty?
110
+ case format
111
+ when :xml
112
+ Crack::XML.parse(body)
113
+ when :json
114
+ Crack::JSON.parse(body)
115
+ when :yaml
116
+ YAML::load(body)
117
+ else
118
+ body
119
+ end
120
+ end
121
+
122
+ # Uses the HTTP Content-Type header to determine the format of the response
123
+ # It compares the MIME type returned to the types stored in the AllowedFormats hash
124
+ def format_from_mimetype(mimetype)
125
+ return nil if mimetype.nil?
126
+ AllowedFormats.each { |k, v| return v if mimetype.include?(k) }
127
+ end
128
+
129
+ def validate
130
+ raise HTTParty::RedirectionTooDeep, 'HTTP redirects too deep' if options[:limit].to_i <= 0
131
+ raise ArgumentError, 'only get, post, put and delete methods are supported' unless SupportedHTTPMethods.include?(http_method)
132
+ raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].is_a?(Hash)
133
+ raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].is_a?(Hash)
134
+ raise ArgumentError, ':query must be hash if using HTTP Post' if post? && !options[:query].nil? && !options[:query].is_a?(Hash)
135
+ end
136
+
137
+ def post?
138
+ Net::HTTP::Post == http_method
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,18 @@
1
+ module HTTParty
2
+ class Response < BlankSlate #:nodoc:
3
+ attr_accessor :body, :code, :message, :headers
4
+ attr_reader :delegate
5
+
6
+ def initialize(delegate, body, code, message, headers={})
7
+ @delegate = delegate
8
+ @body = body
9
+ @code = code.to_i
10
+ @message = message
11
+ @headers = headers
12
+ end
13
+
14
+ def method_missing(name, *args, &block)
15
+ @delegate.send(name, *args, &block)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module HTTParty #:nodoc:
2
+ Version = '0.4.4'
3
+ end