httparty-responsibly 0.17.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.
- checksums.yaml +7 -0
- data/.editorconfig +18 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +92 -0
- data/.rubocop_todo.yml +124 -0
- data/.simplecov +1 -0
- data/.travis.yml +11 -0
- data/CONTRIBUTING.md +23 -0
- data/Changelog.md +509 -0
- data/Gemfile +24 -0
- data/Guardfile +16 -0
- data/MIT-LICENSE +20 -0
- data/README.md +78 -0
- data/Rakefile +10 -0
- data/bin/httparty +123 -0
- data/cucumber.yml +1 -0
- data/docs/README.md +106 -0
- data/examples/README.md +86 -0
- data/examples/aaws.rb +32 -0
- data/examples/basic.rb +28 -0
- data/examples/body_stream.rb +14 -0
- data/examples/crack.rb +19 -0
- data/examples/custom_parsers.rb +68 -0
- data/examples/delicious.rb +37 -0
- data/examples/google.rb +16 -0
- data/examples/headers_and_user_agents.rb +10 -0
- data/examples/logging.rb +36 -0
- data/examples/microsoft_graph.rb +52 -0
- data/examples/multipart.rb +22 -0
- data/examples/nokogiri_html_parser.rb +19 -0
- data/examples/peer_cert.rb +9 -0
- data/examples/rescue_json.rb +17 -0
- data/examples/rubyurl.rb +14 -0
- data/examples/stackexchange.rb +24 -0
- data/examples/stream_download.rb +26 -0
- data/examples/tripit_sign_in.rb +44 -0
- data/examples/twitter.rb +31 -0
- data/examples/whoismyrep.rb +10 -0
- data/httparty-responsibly.gemspec +27 -0
- data/lib/httparty.rb +668 -0
- data/lib/httparty/connection_adapter.rb +254 -0
- data/lib/httparty/cookie_hash.rb +21 -0
- data/lib/httparty/exceptions.rb +33 -0
- data/lib/httparty/hash_conversions.rb +69 -0
- data/lib/httparty/headers_processor.rb +30 -0
- data/lib/httparty/logger/apache_formatter.rb +45 -0
- data/lib/httparty/logger/curl_formatter.rb +91 -0
- data/lib/httparty/logger/logger.rb +28 -0
- data/lib/httparty/logger/logstash_formatter.rb +59 -0
- data/lib/httparty/module_inheritable_attributes.rb +56 -0
- data/lib/httparty/net_digest_auth.rb +136 -0
- data/lib/httparty/parser.rb +150 -0
- data/lib/httparty/request.rb +386 -0
- data/lib/httparty/request/body.rb +84 -0
- data/lib/httparty/request/multipart_boundary.rb +11 -0
- data/lib/httparty/response.rb +140 -0
- data/lib/httparty/response/headers.rb +33 -0
- data/lib/httparty/response_fragment.rb +19 -0
- data/lib/httparty/text_encoder.rb +70 -0
- data/lib/httparty/utils.rb +11 -0
- data/lib/httparty/version.rb +3 -0
- data/script/release +42 -0
- data/website/css/common.css +47 -0
- data/website/index.html +73 -0
- metadata +138 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
module HTTParty
|
2
|
+
class Response < Object
|
3
|
+
def self.underscore(string)
|
4
|
+
string.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
5
|
+
end
|
6
|
+
|
7
|
+
def self._load(data)
|
8
|
+
req, resp, parsed_resp, resp_body = Marshal.load(data)
|
9
|
+
|
10
|
+
new(req, resp, -> { parsed_resp }, body: resp_body)
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :request, :response, :body, :headers
|
14
|
+
|
15
|
+
def initialize(request, response, parsed_block, options = {})
|
16
|
+
@request = request
|
17
|
+
@response = response
|
18
|
+
@body = options[:body] || response.body
|
19
|
+
@parsed_block = parsed_block
|
20
|
+
@headers = Headers.new(response.to_hash)
|
21
|
+
|
22
|
+
if request.options[:logger]
|
23
|
+
logger = ::HTTParty::Logger.build(
|
24
|
+
request.options[:logger],
|
25
|
+
request.options[:log_level],
|
26
|
+
request.options[:log_format]
|
27
|
+
)
|
28
|
+
logger.format(request, self)
|
29
|
+
end
|
30
|
+
|
31
|
+
throw_exception
|
32
|
+
end
|
33
|
+
|
34
|
+
def parsed_response
|
35
|
+
@parsed_response ||= @parsed_block.call
|
36
|
+
end
|
37
|
+
|
38
|
+
def code
|
39
|
+
response.code.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
def http_version
|
43
|
+
response.http_version
|
44
|
+
end
|
45
|
+
|
46
|
+
def tap
|
47
|
+
yield self
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def inspect
|
52
|
+
inspect_id = ::Kernel::format "%x", (object_id * 2)
|
53
|
+
%(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
|
54
|
+
end
|
55
|
+
|
56
|
+
CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
|
57
|
+
|
58
|
+
CODES_TO_OBJ.each do |response_code, klass|
|
59
|
+
name = klass.name.sub("Net::HTTP", '')
|
60
|
+
name = "#{underscore(name)}?".to_sym
|
61
|
+
|
62
|
+
define_method(name) do
|
63
|
+
klass === response
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Support old multiple_choice? method from pre 2.0.0 era.
|
68
|
+
if ::RUBY_VERSION >= "2.0.0" && ::RUBY_PLATFORM != "java"
|
69
|
+
alias_method :multiple_choice?, :multiple_choices?
|
70
|
+
end
|
71
|
+
|
72
|
+
# Support old status codes method from pre 2.6.0 era.
|
73
|
+
if ::RUBY_VERSION >= "2.6.0" && ::RUBY_PLATFORM != "java"
|
74
|
+
alias_method :gateway_time_out?, :gateway_timeout?
|
75
|
+
alias_method :request_entity_too_large?, :payload_too_large?
|
76
|
+
alias_method :request_time_out?, :request_timeout?
|
77
|
+
alias_method :request_uri_too_long?, :uri_too_long?
|
78
|
+
alias_method :requested_range_not_satisfiable?, :range_not_satisfiable?
|
79
|
+
end
|
80
|
+
|
81
|
+
def nil?
|
82
|
+
response.nil? || response.body.nil? || response.body.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
if !response.nil? && !response.body.nil? && response.body.respond_to?(:to_s)
|
87
|
+
response.body.to_s
|
88
|
+
else
|
89
|
+
inspect
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def pretty_print(pp)
|
94
|
+
if !parsed_response.nil? && parsed_response.respond_to?(:pretty_print)
|
95
|
+
parsed_response.pretty_print(pp)
|
96
|
+
else
|
97
|
+
super
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def display(port=$>)
|
102
|
+
if !parsed_response.nil? && parsed_response.respond_to?(:display)
|
103
|
+
parsed_response.display(port)
|
104
|
+
elsif !response.nil? && !response.body.nil? && response.body.respond_to?(:display)
|
105
|
+
response.body.display(port)
|
106
|
+
else
|
107
|
+
port.write(inspect)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def respond_to_missing?(name, *args)
|
112
|
+
return true if super
|
113
|
+
parsed_response.respond_to?(name) || response.respond_to?(name)
|
114
|
+
end
|
115
|
+
|
116
|
+
def _dump(_level)
|
117
|
+
Marshal.dump([request, response, parsed_response, body])
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
|
122
|
+
def method_missing(name, *args, &block)
|
123
|
+
if parsed_response.respond_to?(name)
|
124
|
+
parsed_response.send(name, *args, &block)
|
125
|
+
elsif response.respond_to?(name)
|
126
|
+
response.send(name, *args, &block)
|
127
|
+
else
|
128
|
+
super
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def throw_exception
|
133
|
+
if @request.options[:raise_on] && @request.options[:raise_on].include?(code)
|
134
|
+
::Kernel.raise ::HTTParty::ResponseError.new(@response), "Code #{code} - #{body}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
require 'httparty/response/headers'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module HTTParty
|
4
|
+
class Response #:nodoc:
|
5
|
+
class Headers < ::SimpleDelegator
|
6
|
+
include ::Net::HTTPHeader
|
7
|
+
|
8
|
+
def initialize(header_values = nil)
|
9
|
+
@header = {}
|
10
|
+
if header_values
|
11
|
+
header_values.each_pair do |k,v|
|
12
|
+
if v.is_a?(Array)
|
13
|
+
v.each do |sub_v|
|
14
|
+
add_field(k, sub_v)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
add_field(k, v)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
super(@header)
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
if other.is_a?(::Net::HTTPHeader)
|
26
|
+
@header == other.instance_variable_get(:@header)
|
27
|
+
elsif other.is_a?(Hash)
|
28
|
+
@header == other || @header == Headers.new(other).instance_variable_get(:@header)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module HTTParty
|
4
|
+
# Allow access to http_response and code by delegation on fragment
|
5
|
+
class ResponseFragment < SimpleDelegator
|
6
|
+
attr_reader :http_response, :connection
|
7
|
+
|
8
|
+
def code
|
9
|
+
@http_response.code.to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(fragment, http_response, connection)
|
13
|
+
@fragment = fragment
|
14
|
+
@http_response = http_response
|
15
|
+
@connection = connection
|
16
|
+
super fragment
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module HTTParty
|
2
|
+
class TextEncoder
|
3
|
+
attr_reader :text, :content_type, :assume_utf16_is_big_endian
|
4
|
+
|
5
|
+
def initialize(text, assume_utf16_is_big_endian: true, content_type: nil)
|
6
|
+
@text = text.dup
|
7
|
+
@content_type = content_type
|
8
|
+
@assume_utf16_is_big_endian = assume_utf16_is_big_endian
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
if can_encode?
|
13
|
+
encoded_text
|
14
|
+
else
|
15
|
+
text
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def can_encode?
|
22
|
+
''.respond_to?(:encoding) && charset
|
23
|
+
end
|
24
|
+
|
25
|
+
def encoded_text
|
26
|
+
if 'utf-16'.casecmp(charset) == 0
|
27
|
+
encode_utf_16
|
28
|
+
else
|
29
|
+
encode_with_ruby_encoding
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def encode_utf_16
|
34
|
+
if text.bytesize >= 2
|
35
|
+
if text.getbyte(0) == 0xFF && text.getbyte(1) == 0xFE
|
36
|
+
return text.force_encoding("UTF-16LE")
|
37
|
+
elsif text.getbyte(0) == 0xFE && text.getbyte(1) == 0xFF
|
38
|
+
return text.force_encoding("UTF-16BE")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if assume_utf16_is_big_endian # option
|
43
|
+
text.force_encoding("UTF-16BE")
|
44
|
+
else
|
45
|
+
text.force_encoding("UTF-16LE")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def encode_with_ruby_encoding
|
50
|
+
# NOTE: This will raise an argument error if the
|
51
|
+
# charset does not exist
|
52
|
+
encoding = Encoding.find(charset)
|
53
|
+
text.force_encoding(encoding.to_s)
|
54
|
+
rescue ArgumentError
|
55
|
+
text
|
56
|
+
end
|
57
|
+
|
58
|
+
def charset
|
59
|
+
return nil if content_type.nil?
|
60
|
+
|
61
|
+
if (matchdata = content_type.match(/;\s*charset\s*=\s*([^=,;"\s]+)/i))
|
62
|
+
return matchdata.captures.first
|
63
|
+
end
|
64
|
+
|
65
|
+
if (matchdata = content_type.match(/;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i))
|
66
|
+
return matchdata.captures.first.gsub(/\\(.)/, '\1')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/script/release
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#/ Usage: release
|
3
|
+
#/
|
4
|
+
#/ Tag the version in the repo and push the gem.
|
5
|
+
#/
|
6
|
+
|
7
|
+
set -e
|
8
|
+
cd $(dirname "$0")/..
|
9
|
+
|
10
|
+
[ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
|
11
|
+
grep '^#/' <"$0"| cut -c4-
|
12
|
+
exit 0
|
13
|
+
}
|
14
|
+
|
15
|
+
gem_name=httparty
|
16
|
+
|
17
|
+
# Build a new gem archive.
|
18
|
+
rm -rf $gem_name-*.gem
|
19
|
+
gem build -q $gem_name.gemspec
|
20
|
+
|
21
|
+
# Make sure we're on the master branch.
|
22
|
+
(git branch | grep -q '* master') || {
|
23
|
+
echo "Only release from the master branch."
|
24
|
+
exit 1
|
25
|
+
}
|
26
|
+
|
27
|
+
# Figure out what version we're releasing.
|
28
|
+
tag=v`ls $gem_name-*.gem | sed "s/^$gem_name-\(.*\)\.gem$/\1/"`
|
29
|
+
|
30
|
+
echo "Releasing $tag"
|
31
|
+
|
32
|
+
# Make sure we haven't released this version before.
|
33
|
+
git fetch -t origin
|
34
|
+
|
35
|
+
(git tag -l | grep -q "$tag") && {
|
36
|
+
echo "Whoops, there's already a '${tag}' tag."
|
37
|
+
exit 1
|
38
|
+
}
|
39
|
+
|
40
|
+
# Tag it and bag it.
|
41
|
+
gem push $gem_name-*.gem && git tag "$tag" &&
|
42
|
+
git push origin master && git push origin "$tag"
|
@@ -0,0 +1,47 @@
|
|
1
|
+
@media screen, projection {
|
2
|
+
/*
|
3
|
+
Copyright (c) 2007, Yahoo! Inc. All rights reserved.
|
4
|
+
Code licensed under the BSD License:
|
5
|
+
http://developer.yahoo.net/yui/license.txt
|
6
|
+
version: 2.2.0
|
7
|
+
*/
|
8
|
+
body {font:13px arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}table {font-size:inherit;font:100%;}select, input, textarea {font:99% arial,helvetica,clean,sans-serif;}pre, code {font:115% monospace;*font-size:100%;}body * {line-height:1.22em;}
|
9
|
+
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}/*ol,ul {list-style:none;}*/caption,th {text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;}
|
10
|
+
/* end of yahoo reset and fonts */
|
11
|
+
|
12
|
+
body {color:#333; background:#4b1a1a; line-height:1.3;}
|
13
|
+
p {margin:0 0 20px;}
|
14
|
+
a {color:#4b1a1a;}
|
15
|
+
a:hover {text-decoration:none;}
|
16
|
+
strong {font-weight:bold;}
|
17
|
+
em {font-style:italics;}
|
18
|
+
h1,h2,h3,h4,h5,h6 {font-weight:bold;}
|
19
|
+
h1 {font-size:197%; margin:30px 0; color:#4b1a1a;}
|
20
|
+
h2 {font-size:174%; margin:20px 0; color:#b8111a;}
|
21
|
+
h3 {font-size:152%; margin:10px 0;}
|
22
|
+
h4 {font-size:129%; margin:10px 0;}
|
23
|
+
pre {background:#eee; margin:0 0 20px; padding:20px; border:1px solid #ccc; font-size:100%; overflow:auto;}
|
24
|
+
code {font-size:100%; margin:0; padding:0;}
|
25
|
+
ul, ol {margin:10px 0 10px 25px;}
|
26
|
+
ol li {margin:0 0 10px;}
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
div#wrapper {background:#fff; width:560px; margin:0 auto; padding:20px; border:10px solid #bc8c46; border-width:0 10px;}
|
33
|
+
div#header {position:relative; border-bottom:1px dotted; margin:0 0 10px; padding:0 0 10px;}
|
34
|
+
div#header p {margin:0; padding:0;}
|
35
|
+
div#header h1 {margin:0; padding:0;}
|
36
|
+
ul#nav {position:absolute; top:0; right:0; list-style:none; margin:0; padding:0;}
|
37
|
+
ul#nav li {display:inline; padding:0 0 0 5px;}
|
38
|
+
ul#nav li a {}
|
39
|
+
div#content {}
|
40
|
+
div#footer {margin:40px 0 0; border-top:1px dotted; padding:10px 0 0;}
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
}
|
data/website/index.html
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
5
|
+
<title>HTTParty by John Nunemaker</title>
|
6
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" />
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
|
10
|
+
<div id="wrapper">
|
11
|
+
<div id="header">
|
12
|
+
<h1>HTTParty</h1>
|
13
|
+
<p>Tonight we're gonna HTTParty like it's 1999!</p>
|
14
|
+
|
15
|
+
<ul id="nav">
|
16
|
+
<li><a href="rdoc/">Docs</a></li>
|
17
|
+
<li><a href="http://github.com/jnunemaker/httparty">Github</a></li>
|
18
|
+
<li><a href="http://rubyforge.org/projects/httparty/">Rubyforge</a></li>
|
19
|
+
</ul>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div id="content">
|
23
|
+
<h2>Install</h2>
|
24
|
+
<pre><code>$ sudo gem install httparty</code></pre>
|
25
|
+
|
26
|
+
<h2>Some Quick Examples</h2>
|
27
|
+
|
28
|
+
<p>The following is a simple example of wrapping Twitter's API for posting updates.</p>
|
29
|
+
|
30
|
+
<pre><code>class Twitter
|
31
|
+
include HTTParty
|
32
|
+
base_uri 'twitter.com'
|
33
|
+
basic_auth 'username', 'password'
|
34
|
+
end
|
35
|
+
|
36
|
+
Twitter.post('/statuses/update.json', query: {status: "It's an HTTParty and everyone is invited!"})</code></pre>
|
37
|
+
|
38
|
+
<p>That is really it! The object returned is a ruby hash that is decoded from Twitter's json response. JSON parsing is used because of the .json extension in the path of the request. You can also explicitly set a format (see the examples). </p>
|
39
|
+
|
40
|
+
<p>That works and all but what if you don't want to embed your username and password in the class? Below is an example to fix that:</p>
|
41
|
+
|
42
|
+
<pre><code>class Twitter
|
43
|
+
include HTTParty
|
44
|
+
base_uri 'twitter.com'
|
45
|
+
|
46
|
+
def initialize(u, p)
|
47
|
+
@auth = {username: u, password: p}
|
48
|
+
end
|
49
|
+
|
50
|
+
def post(text)
|
51
|
+
options = { query: {status: text}, basic_auth: @auth }
|
52
|
+
self.class.post('/statuses/update.json', options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Twitter.new('username', 'password').post("It's an HTTParty and everyone is invited!")</code></pre>
|
57
|
+
|
58
|
+
<p><strong>More Examples:</strong> There are <a href="http://github.com/jnunemaker/httparty/tree/master/examples/">several examples in the gem itself</a>.</p>
|
59
|
+
|
60
|
+
<h2>Support</h2>
|
61
|
+
<p>Conversations welcome in the <a href="http://groups.google.com/group/httparty-gem">google group</a> and bugs/features over at <a href="http://github.com/jnunemaker/httparty">Github</a>.</p>
|
62
|
+
|
63
|
+
|
64
|
+
</div>
|
65
|
+
|
66
|
+
<div id="footer">
|
67
|
+
<p>Created by <a href="http://addictedtonew.com/about/">John Nunemaker</a> |
|
68
|
+
<a href="http://orderedlist.com/">Hire Me at Ordered List</a></p>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
|
72
|
+
</body>
|
73
|
+
</html>
|