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
data/examples/aaws.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
|
4
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
require File.join(dir, 'httparty')
|
6
|
+
require 'pp'
|
7
|
+
config = YAML.load(File.read(File.join(ENV['HOME'], '.aaws')))
|
8
|
+
|
9
|
+
module AAWS
|
10
|
+
class Book
|
11
|
+
include HTTParty
|
12
|
+
base_uri 'http://ecs.amazonaws.com'
|
13
|
+
default_params Service: 'AWSECommerceService', Operation: 'ItemSearch', SearchIndex: 'Books'
|
14
|
+
|
15
|
+
def initialize(key)
|
16
|
+
self.class.default_params AWSAccessKeyId: key
|
17
|
+
end
|
18
|
+
|
19
|
+
def search(options = {})
|
20
|
+
raise ArgumentError, 'You must search for something' if options[:query].blank?
|
21
|
+
|
22
|
+
# amazon uses nasty camelized query params
|
23
|
+
options[:query] = options[:query].inject({}) { |h, q| h[q[0].to_s.camelize] = q[1]; h }
|
24
|
+
|
25
|
+
# make a request and return the items (NOTE: this doesn't handle errors at this point)
|
26
|
+
self.class.get('/onca/xml', options)['ItemSearchResponse']['Items']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
aaws = AAWS::Book.new(config[:access_key])
|
32
|
+
pp aaws.search(query: { title: 'Ruby On Rails' })
|
data/examples/basic.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
# You can also use post, put, delete, head, options in the same fashion
|
6
|
+
response = HTTParty.get('https://api.stackexchange.com/2.2/questions?site=stackoverflow')
|
7
|
+
puts response.body, response.code, response.message, response.headers.inspect
|
8
|
+
|
9
|
+
# An example post to a minimal rails app in the development environment
|
10
|
+
# Note that "skip_before_filter :verify_authenticity_token" must be set in the
|
11
|
+
# "pears" controller for this example
|
12
|
+
|
13
|
+
class Partay
|
14
|
+
include HTTParty
|
15
|
+
base_uri 'http://localhost:3000'
|
16
|
+
end
|
17
|
+
|
18
|
+
options = {
|
19
|
+
body: {
|
20
|
+
pear: { # your resource
|
21
|
+
foo: '123', # your columns/data
|
22
|
+
bar: 'second',
|
23
|
+
baz: 'last thing'
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
pp Partay.post('/pears.xml', options)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# To upload file to a server use :body_stream
|
2
|
+
|
3
|
+
HTTParty.put(
|
4
|
+
'http://localhost:3000/train',
|
5
|
+
body_stream: File.open('sample_configs/config_train_server_md.yml', 'r')
|
6
|
+
)
|
7
|
+
|
8
|
+
|
9
|
+
# Actually, it works with any IO object
|
10
|
+
|
11
|
+
HTTParty.put(
|
12
|
+
'http://localhost:3000/train',
|
13
|
+
body_stream: StringIO.new('foo')
|
14
|
+
)
|
data/examples/crack.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'crack'
|
3
|
+
|
4
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
require File.join(dir, 'httparty')
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
class Rep
|
9
|
+
include HTTParty
|
10
|
+
|
11
|
+
parser(
|
12
|
+
proc do |body, format|
|
13
|
+
Crack::XML.parse(body)
|
14
|
+
end
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
pp Rep.get('http://whoismyrepresentative.com/getall_mems.php?zip=46544')
|
19
|
+
pp Rep.get('http://whoismyrepresentative.com/getall_mems.php', query: { zip: 46544 })
|
@@ -0,0 +1,68 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
class ParseAtom
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
# Support Atom along with the default parsers: xml, json, etc.
|
9
|
+
class Parser::Atom < HTTParty::Parser
|
10
|
+
SupportedFormats.merge!({"application/atom+xml" => :atom})
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
# perform atom parsing on body
|
15
|
+
def atom
|
16
|
+
body.to_atom
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
parser Parser::Atom
|
21
|
+
end
|
22
|
+
|
23
|
+
class OnlyParseAtom
|
24
|
+
include HTTParty
|
25
|
+
|
26
|
+
# Only support Atom
|
27
|
+
class Parser::OnlyAtom < HTTParty::Parser
|
28
|
+
SupportedFormats = { "application/atom+xml" => :atom }
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
# perform atom parsing on body
|
33
|
+
def atom
|
34
|
+
body.to_atom
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
parser Parser::OnlyAtom
|
39
|
+
end
|
40
|
+
|
41
|
+
class SkipParsing
|
42
|
+
include HTTParty
|
43
|
+
|
44
|
+
# Parse the response body however you like
|
45
|
+
class Parser::Simple < HTTParty::Parser
|
46
|
+
def parse
|
47
|
+
body
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
parser Parser::Simple
|
52
|
+
end
|
53
|
+
|
54
|
+
class AdHocParsing
|
55
|
+
include HTTParty
|
56
|
+
parser(
|
57
|
+
proc do |body, format|
|
58
|
+
case format
|
59
|
+
when :json
|
60
|
+
body.to_json
|
61
|
+
when :xml
|
62
|
+
body.to_xml
|
63
|
+
else
|
64
|
+
body
|
65
|
+
end
|
66
|
+
end
|
67
|
+
)
|
68
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
require 'pp'
|
4
|
+
config = YAML.load(File.read(File.join(ENV['HOME'], '.delicious')))
|
5
|
+
|
6
|
+
class Delicious
|
7
|
+
include HTTParty
|
8
|
+
base_uri 'https://api.del.icio.us/v1'
|
9
|
+
|
10
|
+
def initialize(u, p)
|
11
|
+
@auth = { username: u, password: p }
|
12
|
+
end
|
13
|
+
|
14
|
+
# query params that filter the posts are:
|
15
|
+
# tag (optional). Filter by this tag.
|
16
|
+
# dt (optional). Filter by this date (CCYY-MM-DDThh:mm:ssZ).
|
17
|
+
# url (optional). Filter by this url.
|
18
|
+
# ie: posts(query: {tag: 'ruby'})
|
19
|
+
def posts(options = {})
|
20
|
+
options.merge!({ basic_auth: @auth })
|
21
|
+
self.class.get('/posts/get', options)
|
22
|
+
end
|
23
|
+
|
24
|
+
# query params that filter the posts are:
|
25
|
+
# tag (optional). Filter by this tag.
|
26
|
+
# count (optional). Number of items to retrieve (Default:15, Maximum:100).
|
27
|
+
def recent(options = {})
|
28
|
+
options.merge!({ basic_auth: @auth })
|
29
|
+
self.class.get('/posts/recent', options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
delicious = Delicious.new(config['username'], config['password'])
|
34
|
+
pp delicious.posts(query: { tag: 'ruby' })
|
35
|
+
pp delicious.recent
|
36
|
+
|
37
|
+
delicious.recent['posts']['post'].each { |post| puts post['href'] }
|
data/examples/google.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
class Google
|
6
|
+
include HTTParty
|
7
|
+
format :html
|
8
|
+
end
|
9
|
+
|
10
|
+
# google.com redirects to www.google.com so this is live test for redirection
|
11
|
+
pp Google.get('http://google.com')
|
12
|
+
|
13
|
+
puts '', '*' * 70, ''
|
14
|
+
|
15
|
+
# check that ssl is requesting right
|
16
|
+
pp Google.get('https://www.google.com')
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# To send custom user agents to identify your application to a web service (or mask as a specific browser for testing), send "User-Agent" as a hash to headers as shown below.
|
2
|
+
|
3
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
require File.join(dir, 'httparty')
|
5
|
+
require 'pp'
|
6
|
+
|
7
|
+
response = HTTParty.get('http://example.com', {
|
8
|
+
headers: {"User-Agent" => "Httparty"},
|
9
|
+
debug_output: STDOUT, # To show that User-Agent is Httparty
|
10
|
+
})
|
data/examples/logging.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
require 'logger'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
my_logger = Logger.new STDOUT
|
7
|
+
|
8
|
+
my_logger.info "Logging can be used on the main HTTParty class. It logs redirects too."
|
9
|
+
HTTParty.get "http://google.com", logger: my_logger
|
10
|
+
|
11
|
+
my_logger.info '*' * 70
|
12
|
+
|
13
|
+
my_logger.info "It can be used also on a custom class."
|
14
|
+
|
15
|
+
class Google
|
16
|
+
include HTTParty
|
17
|
+
logger ::Logger.new STDOUT
|
18
|
+
end
|
19
|
+
|
20
|
+
Google.get "http://google.com"
|
21
|
+
|
22
|
+
my_logger.info '*' * 70
|
23
|
+
|
24
|
+
my_logger.info "The default formatter is :apache. The :curl formatter can also be used."
|
25
|
+
my_logger.info "You can tell which method to call on the logger too. It is info by default."
|
26
|
+
HTTParty.get "http://google.com", logger: my_logger, log_level: :debug, log_format: :curl
|
27
|
+
|
28
|
+
my_logger.info '*' * 70
|
29
|
+
|
30
|
+
my_logger.info "These configs are also available on custom classes."
|
31
|
+
class Google
|
32
|
+
include HTTParty
|
33
|
+
logger ::Logger.new(STDOUT), :debug, :curl
|
34
|
+
end
|
35
|
+
|
36
|
+
Google.get "http://google.com"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
class MicrosoftGraph
|
4
|
+
MS_BASE_URL = "https://login.microsoftonline.com".freeze
|
5
|
+
TOKEN_REQUEST_PATH = "oauth2/v2.0/token".freeze
|
6
|
+
|
7
|
+
def initialize(tenant_id)
|
8
|
+
@tenant_id = tenant_id
|
9
|
+
end
|
10
|
+
|
11
|
+
# Make a request to the Microsoft Graph API, for instance https://graph.microsoft.com/v1.0/users
|
12
|
+
def request(url)
|
13
|
+
return false unless (token = bearer_token)
|
14
|
+
|
15
|
+
response = HTTParty.get(
|
16
|
+
url,
|
17
|
+
headers: {
|
18
|
+
Authorization: "Bearer #{token}"
|
19
|
+
}
|
20
|
+
)
|
21
|
+
|
22
|
+
return false unless response.code == 200
|
23
|
+
|
24
|
+
return JSON.parse(response.body)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# A post to the Microsoft Graph to get a bearer token for the specified tenant. In this example
|
30
|
+
# our Rails application has already been given permission to request these tokens by the admin of
|
31
|
+
# the specified tenant_id.
|
32
|
+
#
|
33
|
+
# See here for more information https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_service
|
34
|
+
#
|
35
|
+
# This request also makes use of the multipart/form-data post body.
|
36
|
+
def bearer_token
|
37
|
+
response = HTTParty.post(
|
38
|
+
"#{MS_BASE_URL}/#{@tenant_id}/#{TOKEN_REQUEST_PATH}",
|
39
|
+
multipart: true,
|
40
|
+
body: {
|
41
|
+
client_id: Rails.application.credentials[Rails.env.to_sym][:microsoft_client_id],
|
42
|
+
client_secret: Rails.application.credentials[Rails.env.to_sym][:microsoft_client_secret],
|
43
|
+
scope: 'https://graph.microsoft.com/.default',
|
44
|
+
grant_type: 'client_credentials'
|
45
|
+
}
|
46
|
+
)
|
47
|
+
|
48
|
+
return false unless response.code == 200
|
49
|
+
|
50
|
+
JSON.parse(response.body)['access_token']
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# If you are uploading file in params, multipart will used as content-type automatically
|
2
|
+
|
3
|
+
HTTParty.post(
|
4
|
+
'http://localhost:3000/user',
|
5
|
+
body: {
|
6
|
+
name: 'Foo Bar',
|
7
|
+
email: 'example@email.com',
|
8
|
+
avatar: File.open('/full/path/to/avatar.jpg')
|
9
|
+
}
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
# However, you can force it yourself
|
14
|
+
|
15
|
+
HTTParty.post(
|
16
|
+
'http://localhost:3000/user',
|
17
|
+
multipart: true,
|
18
|
+
body: {
|
19
|
+
name: 'Foo Bar',
|
20
|
+
email: 'example@email.com'
|
21
|
+
}
|
22
|
+
)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
require File.join(dir, 'httparty')
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
class HtmlParserIncluded < HTTParty::Parser
|
9
|
+
def html
|
10
|
+
Nokogiri::HTML(body)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Page
|
15
|
+
include HTTParty
|
16
|
+
parser HtmlParserIncluded
|
17
|
+
end
|
18
|
+
|
19
|
+
pp Page.get('http://www.google.com')
|
@@ -0,0 +1,9 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
|
4
|
+
peer_cert = nil
|
5
|
+
HTTParty.get("https://www.example.com") do |fragment|
|
6
|
+
peer_cert ||= fragment.connection.peer_cert
|
7
|
+
end
|
8
|
+
|
9
|
+
puts "The server's certificate expires #{peer_cert.not_after}"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
|
4
|
+
# Take note of the "; 1" at the end of the following line. It's required only if
|
5
|
+
# running this in IRB, because IRB will try to inspect the variable named
|
6
|
+
# "request", triggering the exception.
|
7
|
+
request = HTTParty.get 'https://rubygems.org/api/v1/versions/doesnotexist.json' ; 1
|
8
|
+
|
9
|
+
# Check an exception due to parsing the response
|
10
|
+
# because HTTParty evaluate the response lazily
|
11
|
+
begin
|
12
|
+
request.inspect
|
13
|
+
# This would also suffice by forcing the request to be parsed:
|
14
|
+
# request.parsed_response
|
15
|
+
rescue => e
|
16
|
+
puts "Rescued #{e.inspect}"
|
17
|
+
end
|
data/examples/rubyurl.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
class Rubyurl
|
6
|
+
include HTTParty
|
7
|
+
base_uri 'rubyurl.com'
|
8
|
+
|
9
|
+
def self.shorten(website_url)
|
10
|
+
post('/api/links.json', query: { link: { website_url: website_url } })
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
pp Rubyurl.shorten('http://istwitterdown.com/')
|
@@ -0,0 +1,24 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
class StackExchange
|
6
|
+
include HTTParty
|
7
|
+
base_uri 'api.stackexchange.com'
|
8
|
+
|
9
|
+
def initialize(service, page)
|
10
|
+
@options = { query: { site: service, page: page } }
|
11
|
+
end
|
12
|
+
|
13
|
+
def questions
|
14
|
+
self.class.get("/2.2/questions", @options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def users
|
18
|
+
self.class.get("/2.2/users", @options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
stack_exchange = StackExchange.new("stackoverflow", 1)
|
23
|
+
pp stack_exchange.questions
|
24
|
+
pp stack_exchange.users
|
@@ -0,0 +1,26 @@
|
|
1
|
+
dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require File.join(dir, 'httparty')
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
# download file linux-4.6.4.tar.xz without using the memory
|
6
|
+
response = nil
|
7
|
+
filename = "linux-4.6.4.tar.xz"
|
8
|
+
url = "https://cdn.kernel.org/pub/linux/kernel/v4.x/#{filename}"
|
9
|
+
|
10
|
+
File.open(filename, "w") do |file|
|
11
|
+
response = HTTParty.get(url, stream_body: true) do |fragment|
|
12
|
+
if [301, 302].include?(fragment.code)
|
13
|
+
print "skip writing for redirect"
|
14
|
+
elsif fragment.code == 200
|
15
|
+
print "."
|
16
|
+
file.write(fragment)
|
17
|
+
else
|
18
|
+
raise StandardError, "Non-success status code while streaming #{fragment.code}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
puts
|
23
|
+
|
24
|
+
pp "Success: #{response.success?}"
|
25
|
+
pp File.stat(filename).inspect
|
26
|
+
File.unlink(filename)
|