jnunemaker-httparty 0.2.6 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/History +9 -0
- data/Manifest +3 -0
- data/bin/httparty +38 -27
- data/httparty.gemspec +2 -2
- data/lib/core_extensions.rb +3 -3
- data/lib/httparty/request.rb +41 -35
- data/lib/httparty/version.rb +1 -1
- data/lib/httparty.rb +14 -4
- data/spec/httparty/request_spec.rb +75 -3
- data/spec/httparty_spec.rb +12 -0
- data/spec/spec_helper.rb +5 -8
- metadata +2 -2
data/History
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
== 0.2.7 2009-01-28
|
2
|
+
* 2 minor fixes, 2 minor enhancements, 2 major enhancements
|
3
|
+
* fixed undefined method add_node for nil class error that occasionally happened (juliocesar)
|
4
|
+
* Handle nil or unexpected values better when typecasting. (Brian Landau)
|
5
|
+
* More robust handling of mime types (Alex Vollmer)
|
6
|
+
* Fixed support for specifying headers and added support for basic auth to CLI. (Alex Vollmer)
|
7
|
+
* Added first class response object that includes original body and status code (Alex Vollmer)
|
8
|
+
* Now parsing all response types as some non-200 responses provide important information, this means no more exception raising (Alex Vollmer)
|
9
|
+
|
1
10
|
== 0.2.6 2009-01-05
|
2
11
|
* 1 minor bug fix
|
3
12
|
* added explicit require of time as Time#parse failed outside of rails (willcodeforfoo)
|
data/Manifest
CHANGED
@@ -11,6 +11,7 @@ httparty.gemspec
|
|
11
11
|
lib/core_extensions.rb
|
12
12
|
lib/httparty/exceptions.rb
|
13
13
|
lib/httparty/request.rb
|
14
|
+
lib/httparty/response.rb
|
14
15
|
lib/httparty/version.rb
|
15
16
|
lib/httparty.rb
|
16
17
|
lib/module_level_inheritable_attributes.rb
|
@@ -21,9 +22,11 @@ README
|
|
21
22
|
setup.rb
|
22
23
|
spec/as_buggery_spec.rb
|
23
24
|
spec/fixtures/delicious.xml
|
25
|
+
spec/fixtures/empty.xml
|
24
26
|
spec/fixtures/google.html
|
25
27
|
spec/fixtures/twitter.json
|
26
28
|
spec/fixtures/twitter.xml
|
29
|
+
spec/fixtures/undefined_method_add_node_for_nil.xml
|
27
30
|
spec/httparty/request_spec.rb
|
28
31
|
spec/httparty_spec.rb
|
29
32
|
spec/spec.opts
|
data/bin/httparty
CHANGED
@@ -10,26 +10,30 @@ require "httparty"
|
|
10
10
|
opts = {
|
11
11
|
:action => :get,
|
12
12
|
:headers => {},
|
13
|
-
:
|
14
|
-
:verbose => false,
|
15
|
-
:pretty_print => false
|
13
|
+
:verbose => false
|
16
14
|
}
|
17
15
|
|
16
|
+
def die(msg)
|
17
|
+
STDERR.puts(msg)
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
18
21
|
OptionParser.new do |o|
|
19
22
|
o.banner = "USAGE: #{$0} [options] [url]"
|
23
|
+
|
20
24
|
o.on("-f",
|
21
25
|
"--format [FORMAT]",
|
22
|
-
"
|
23
|
-
|
24
|
-
|
25
|
-
o.on("-r", "--ruby", "Dump output in Ruby pretty-print format") do |r|
|
26
|
-
opts[:pretty_print] = true
|
26
|
+
"Output format to use instead of pretty-print ruby: " +
|
27
|
+
"plain, json or xml") do |f|
|
28
|
+
opts[:output_format] = f.downcase.to_sym
|
27
29
|
end
|
30
|
+
|
28
31
|
o.on("-a",
|
29
32
|
"--action [ACTION]",
|
30
33
|
"HTTP action: get (default), post, put or delete") do |a|
|
31
34
|
opts[:action] = a.downcase.to_sym
|
32
35
|
end
|
36
|
+
|
33
37
|
o.on("-d",
|
34
38
|
"--data [BODY]",
|
35
39
|
"Data to put in request body (prefix with '@' for file)") do |d|
|
@@ -39,13 +43,23 @@ OptionParser.new do |o|
|
|
39
43
|
opts[:data] = d
|
40
44
|
end
|
41
45
|
end
|
46
|
+
|
42
47
|
o.on("-H", "--header [NAME=VALUE]", "Additional HTTP headers in NAME=VALUE form") do |h|
|
43
|
-
|
44
|
-
|
48
|
+
die "Invalid header specification, should be Name:Value" unless h =~ /.+:.+/
|
49
|
+
name, value = h.split(':')
|
50
|
+
opts[:headers][name.strip] = value.strip
|
45
51
|
end
|
52
|
+
|
46
53
|
o.on("-v", "--verbose", "If set, print verbose output") do |v|
|
47
54
|
opts[:verbose] = true
|
48
55
|
end
|
56
|
+
|
57
|
+
o.on("-u", "--user [CREDS]", "Use basic authentication. Value should be user:password") do |u|
|
58
|
+
die "Invalid credentials format. Must be user:password" unless u =~ /.+:.+/
|
59
|
+
user, password = u.split(':')
|
60
|
+
opts[:basic_auth] = { :username => user, :password => password }
|
61
|
+
end
|
62
|
+
|
49
63
|
o.on("-h", "--help", "Show help documentation") do |h|
|
50
64
|
puts o
|
51
65
|
exit
|
@@ -76,28 +90,25 @@ module REXML
|
|
76
90
|
REXML::Formatters::Default.new( ie_hack )
|
77
91
|
end
|
78
92
|
formatter.write( self, output )
|
79
|
-
|
93
|
+
end
|
80
94
|
end
|
81
95
|
end
|
82
96
|
|
83
|
-
if opts[:
|
84
|
-
|
97
|
+
if opts[:output_format].nil?
|
98
|
+
response = HTTParty.send(opts[:action], ARGV.first, opts)
|
99
|
+
puts "Status: #{response.code}"
|
100
|
+
pp response
|
85
101
|
else
|
86
|
-
print_format = opts[:
|
87
|
-
opts.merge!(:format => :plain) if opts[:format]
|
102
|
+
print_format = opts[:output_format]
|
88
103
|
response = HTTParty.send(opts[:action], ARGV.first, opts)
|
89
|
-
|
90
|
-
|
91
|
-
|
104
|
+
puts "Status: #{response.code}"
|
105
|
+
case opts[:output_format]
|
106
|
+
when :json
|
107
|
+
puts JSON.pretty_generate(response)
|
108
|
+
when :xml
|
109
|
+
REXML::Document.new(response.body).write(STDOUT, 2)
|
110
|
+
puts
|
92
111
|
else
|
93
|
-
|
94
|
-
when :json
|
95
|
-
puts JSON.pretty_generate(JSON.parse(response))
|
96
|
-
when :xml
|
97
|
-
REXML::Document.new(response).write(STDOUT, 2)
|
98
|
-
puts
|
99
|
-
else
|
100
|
-
puts response
|
101
|
-
end
|
112
|
+
puts response
|
102
113
|
end
|
103
114
|
end
|
data/httparty.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{httparty}
|
5
|
-
s.version = "0.2.
|
5
|
+
s.version = "0.2.7"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["John Nunemaker"]
|
9
|
-
s.date = %q{2009-01-
|
9
|
+
s.date = %q{2009-01-28}
|
10
10
|
s.default_executable = %q{httparty}
|
11
11
|
s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
|
12
12
|
s.email = %q{nunemaker@gmail.com}
|
data/lib/core_extensions.rb
CHANGED
@@ -115,10 +115,10 @@ class REXMLUtilityNode
|
|
115
115
|
self.typecasts["datetime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
|
116
116
|
self.typecasts["date"] = lambda{|v| v.nil? ? nil : Date.parse(v)}
|
117
117
|
self.typecasts["dateTime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
|
118
|
-
self.typecasts["decimal"] = lambda{|v| BigDecimal(v)}
|
118
|
+
self.typecasts["decimal"] = lambda{|v| v.nil? ? nil : BigDecimal(v.to_s)}
|
119
119
|
self.typecasts["double"] = lambda{|v| v.nil? ? nil : v.to_f}
|
120
120
|
self.typecasts["float"] = lambda{|v| v.nil? ? nil : v.to_f}
|
121
|
-
self.typecasts["symbol"] = lambda{|v| v.to_sym}
|
121
|
+
self.typecasts["symbol"] = lambda{|v| v.nil? ? nil : v.to_sym}
|
122
122
|
self.typecasts["string"] = lambda{|v| v.to_s}
|
123
123
|
self.typecasts["yaml"] = lambda{|v| v.nil? ? nil : YAML.load(v)}
|
124
124
|
self.typecasts["base64Binary"] = lambda{|v| v.unpack('m').first }
|
@@ -280,7 +280,7 @@ class ToHashParser
|
|
280
280
|
stack.last.add_node(temp)
|
281
281
|
end
|
282
282
|
when :text, :cdata
|
283
|
-
stack.last.add_node(event[1]) unless event[1].strip.length == 0
|
283
|
+
stack.last.add_node(event[1]) unless event[1].strip.length == 0 || stack.empty?
|
284
284
|
end
|
285
285
|
end
|
286
286
|
stack.pop.to_hash
|
data/lib/httparty/request.rb
CHANGED
@@ -20,9 +20,9 @@ module HTTParty
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def uri
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
new_uri = path.relative? ? URI.parse("#{options[:base_uri]}#{path}") : path
|
24
|
+
new_uri.query = query_string(new_uri)
|
25
|
+
new_uri
|
26
26
|
end
|
27
27
|
|
28
28
|
def format
|
@@ -31,32 +31,41 @@ module HTTParty
|
|
31
31
|
|
32
32
|
def perform
|
33
33
|
validate!
|
34
|
-
|
34
|
+
setup_raw_request
|
35
|
+
handle_response!(get_response)
|
35
36
|
end
|
36
37
|
|
37
38
|
private
|
38
|
-
def http
|
39
|
+
def http #:nodoc:
|
39
40
|
http = Net::HTTP.new(uri.host, uri.port, options[:http_proxyaddr], options[:http_proxyport])
|
40
41
|
http.use_ssl = (uri.port == 443)
|
41
42
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
42
43
|
http
|
43
44
|
end
|
44
|
-
|
45
|
-
def
|
46
|
-
|
45
|
+
|
46
|
+
def configure_basic_auth
|
47
|
+
@raw_request.basic_auth(options[:basic_auth][:username], options[:basic_auth][:password])
|
48
|
+
end
|
49
|
+
|
50
|
+
def setup_raw_request
|
51
|
+
@raw_request = http_method.new(uri.request_uri)
|
47
52
|
|
48
53
|
if post? && options[:query]
|
49
|
-
|
54
|
+
@raw_request.set_form_data(options[:query])
|
50
55
|
end
|
51
56
|
|
52
|
-
|
53
|
-
|
57
|
+
@raw_request.body = options[:body].is_a?(Hash) ? options[:body].to_params : options[:body] unless options[:body].blank?
|
58
|
+
@raw_request.initialize_http_header options[:headers]
|
54
59
|
|
55
|
-
if options[:basic_auth]
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
+
configure_basic_auth if options[:basic_auth]
|
61
|
+
end
|
62
|
+
|
63
|
+
def perform_actual_request
|
64
|
+
http.request(@raw_request)
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_response #:nodoc:
|
68
|
+
response = perform_actual_request
|
60
69
|
options[:format] ||= format_from_mimetype(response['content-type'])
|
61
70
|
response
|
62
71
|
end
|
@@ -78,36 +87,33 @@ module HTTParty
|
|
78
87
|
# Raises exception Net::XXX (http error code) if an http error occured
|
79
88
|
def handle_response!(response) #:nodoc:
|
80
89
|
case response
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
begin; response.body_parsed = parse_response(response.body); rescue; end
|
90
|
-
response.error! # raises exception corresponding to http error Net::XXX
|
91
|
-
end
|
90
|
+
when Net::HTTPRedirection
|
91
|
+
options[:limit] -= 1
|
92
|
+
self.path = response['location']
|
93
|
+
perform
|
94
|
+
else
|
95
|
+
parsed_response = parse_response(response.body)
|
96
|
+
Response.new(parsed_response, response.body, response.code)
|
97
|
+
end
|
92
98
|
end
|
93
99
|
|
94
100
|
def parse_response(body) #:nodoc:
|
95
101
|
return nil if body.nil? or body.empty?
|
96
102
|
case format
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
103
|
+
when :xml
|
104
|
+
ToHashParser.from_xml(body)
|
105
|
+
when :json
|
106
|
+
JSON.parse(body)
|
107
|
+
else
|
108
|
+
body
|
109
|
+
end
|
104
110
|
end
|
105
111
|
|
106
112
|
# Uses the HTTP Content-Type header to determine the format of the response
|
107
113
|
# It compares the MIME type returned to the types stored in the AllowedFormats hash
|
108
114
|
def format_from_mimetype(mimetype) #:nodoc:
|
109
115
|
return nil if mimetype.nil?
|
110
|
-
AllowedFormats.each { |k, v| return
|
116
|
+
AllowedFormats.each { |k, v| return v if mimetype.include?(k) }
|
111
117
|
end
|
112
118
|
|
113
119
|
def validate! #:nodoc:
|
data/lib/httparty/version.rb
CHANGED
data/lib/httparty.rb
CHANGED
@@ -9,8 +9,17 @@ require 'json'
|
|
9
9
|
require 'module_level_inheritable_attributes'
|
10
10
|
require 'core_extensions'
|
11
11
|
|
12
|
-
module HTTParty
|
13
|
-
|
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
|
+
} unless defined?(AllowedFormats)
|
14
23
|
|
15
24
|
def self.included(base)
|
16
25
|
base.extend ClassMethods
|
@@ -51,7 +60,7 @@ module HTTParty
|
|
51
60
|
end
|
52
61
|
|
53
62
|
def format(f)
|
54
|
-
raise UnsupportedFormat, "Must be one of: #{AllowedFormats.
|
63
|
+
raise UnsupportedFormat, "Must be one of: #{AllowedFormats.values.join(', ')}" unless AllowedFormats.value?(f)
|
55
64
|
default_options[:format] = f
|
56
65
|
end
|
57
66
|
|
@@ -109,4 +118,5 @@ module HTTParty
|
|
109
118
|
end
|
110
119
|
|
111
120
|
require 'httparty/exceptions'
|
112
|
-
require 'httparty/request'
|
121
|
+
require 'httparty/request'
|
122
|
+
require 'httparty/response'
|
@@ -13,11 +13,57 @@ describe HTTParty::Request do
|
|
13
13
|
|
14
14
|
describe 'http' do
|
15
15
|
it "should use ssl for port 443" do
|
16
|
-
|
16
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'https://api.foo.com/v1:443')
|
17
|
+
request.send(:http).use_ssl?.should == true
|
17
18
|
end
|
18
19
|
|
19
20
|
it 'should not use ssl for port 80' do
|
20
|
-
|
21
|
+
request = HTTParty::Request.new(Net::HTTP::Get, 'http://foobar.com')
|
22
|
+
@request.send(:http).use_ssl?.should == false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should use basic auth when configured" do
|
26
|
+
@request.options[:basic_auth] = {:username => 'foobar', :password => 'secret'}
|
27
|
+
@request.send(:setup_raw_request)
|
28
|
+
@request.instance_variable_get(:@raw_request)['authorization'].should_not be_nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#format_from_mimetype' do
|
33
|
+
it 'should handle text/xml' do
|
34
|
+
["text/xml", "text/xml; charset=iso8859-1"].each do |ct|
|
35
|
+
@request.send(:format_from_mimetype, ct).should == :xml
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should handle application/xml' do
|
40
|
+
["application/xml", "application/xml; charset=iso8859-1"].each do |ct|
|
41
|
+
@request.send(:format_from_mimetype, ct).should == :xml
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should handle text/json' do
|
46
|
+
["text/json", "text/json; charset=iso8859-1"].each do |ct|
|
47
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should handle application/json' do
|
52
|
+
["application/json", "application/json; charset=iso8859-1"].each do |ct|
|
53
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should handle text/javascript' do
|
58
|
+
["text/javascript", "text/javascript; charset=iso8859-1"].each do |ct|
|
59
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should handle application/javascript' do
|
64
|
+
["application/javascript", "application/javascript; charset=iso8859-1"].each do |ct|
|
65
|
+
@request.send(:format_from_mimetype, ct).should == :json
|
66
|
+
end
|
21
67
|
end
|
22
68
|
end
|
23
69
|
|
@@ -33,6 +79,32 @@ describe HTTParty::Request do
|
|
33
79
|
@request.options[:format] = :json
|
34
80
|
@request.send(:parse_response, json).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
|
35
81
|
end
|
82
|
+
|
83
|
+
describe 'with non-200 responses' do
|
84
|
+
|
85
|
+
it 'should return a valid object for 4xx response' do
|
86
|
+
http_response = Net::HTTPUnauthorized.new('1.1', 401, '')
|
87
|
+
http_response.stub!(:body).and_return('<foo><bar>yes</bar></foo>')
|
88
|
+
|
89
|
+
@request.should_receive(:get_response).and_return(http_response)
|
90
|
+
resp = @request.perform
|
91
|
+
resp.code.should == 401
|
92
|
+
resp.body.should == "<foo><bar>yes</bar></foo>"
|
93
|
+
resp['foo']['bar'].should == "yes"
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should return a valid object for 5xx response' do
|
97
|
+
http_response = Net::HTTPUnauthorized.new('1.1', 500, '')
|
98
|
+
http_response.stub!(:body).and_return('<foo><bar>error</bar></foo>')
|
99
|
+
|
100
|
+
@request.should_receive(:get_response).and_return(http_response)
|
101
|
+
resp = @request.perform
|
102
|
+
resp.code.should == 500
|
103
|
+
resp.body.should == "<foo><bar>error</bar></foo>"
|
104
|
+
resp['foo']['bar'].should == "error"
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
36
108
|
end
|
37
109
|
|
38
110
|
it "should not attempt to parse empty responses" do
|
@@ -41,7 +113,7 @@ describe HTTParty::Request do
|
|
41
113
|
response = Net::HTTPNoContent.new("1.1", 204, "No content for you")
|
42
114
|
response.stub!(:body).and_return(nil)
|
43
115
|
http.stub!(:request).and_return(response)
|
44
|
-
|
116
|
+
|
45
117
|
@request.options[:format] = :xml
|
46
118
|
@request.perform.should be_nil
|
47
119
|
|
data/spec/httparty_spec.rb
CHANGED
@@ -173,5 +173,17 @@ describe HTTParty do
|
|
173
173
|
"location" => nil
|
174
174
|
}
|
175
175
|
end
|
176
|
+
|
177
|
+
it "should not get undefined method add_node for nil class for the following xml" do
|
178
|
+
stub_http_response_with('undefined_method_add_node_for_nil.xml')
|
179
|
+
result = HTTParty.get('http://foobar.com')
|
180
|
+
result.should == {"Entities"=>{"href"=>"https://s3-sandbox.parature.com/api/v1/5578/5633/Account", "results"=>"0", "total"=>"0", "page_size"=>"25", "page"=>"1"}}
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should parse empty response fine" do
|
184
|
+
stub_http_response_with('empty.xml')
|
185
|
+
result = HTTParty.get('http://foobar.com')
|
186
|
+
result.should == nil
|
187
|
+
end
|
176
188
|
end
|
177
189
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -10,15 +10,12 @@ end
|
|
10
10
|
def stub_http_response_with(filename)
|
11
11
|
format = filename.split('.').last.intern
|
12
12
|
data = file_fixture(filename)
|
13
|
-
|
14
|
-
|
13
|
+
|
15
14
|
response = Net::HTTPOK.new("1.1", 200, "Content for you")
|
16
15
|
response.stub!(:body).and_return(data)
|
17
|
-
|
18
|
-
|
19
|
-
http_request
|
20
|
-
|
21
|
-
http_request.stub!(:format).and_return(format)
|
22
|
-
|
16
|
+
|
17
|
+
http_request = HTTParty::Request.new(Net::HTTP::Get, 'http://localhost', :format => format)
|
18
|
+
http_request.stub!(:perform_actual_request).and_return(response)
|
19
|
+
|
23
20
|
HTTParty::Request.should_receive(:new).and_return(http_request)
|
24
21
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jnunemaker-httparty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-28 00:00:00 -08:00
|
13
13
|
default_executable: httparty
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|