httparty 0.2.8 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of httparty might be problematic. Click here for more details.
- data/History +6 -0
- data/Manifest +13 -0
- data/Rakefile +6 -1
- data/cucumber.yml +1 -0
- data/examples/basic.rb +1 -1
- data/examples/delicious.rb +1 -0
- data/examples/rubyurl.rb +1 -1
- data/features/basic_authentication.feature +20 -0
- data/features/command_line.feature +7 -0
- data/features/deals_with_http_error_codes.feature +26 -0
- data/features/handles_multiple_formats.feature +34 -0
- data/features/steps/env.rb +15 -0
- data/features/steps/httparty_response_steps.rb +26 -0
- data/features/steps/httparty_steps.rb +15 -0
- data/features/steps/mongrel_helper.rb +55 -0
- data/features/steps/remote_service_steps.rb +47 -0
- data/features/supports_redirection.feature +22 -0
- data/httparty.gemspec +4 -4
- data/lib/core_extensions.rb +5 -1
- data/lib/httparty.rb +18 -1
- data/lib/httparty/cookie_hash.rb +9 -0
- data/lib/httparty/request.rb +1 -1
- data/lib/httparty/response.rb +5 -16
- data/lib/httparty/version.rb +1 -1
- data/spec/httparty/cookie_hash_spec.rb +38 -0
- data/spec/httparty/request_spec.rb +58 -75
- data/spec/httparty_spec.rb +107 -42
- metadata +16 -2
data/History
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== 0.2.9 2009-01-29
|
2
|
+
* 3 minor enhancements
|
3
|
+
* Added a 'headers' accessor to the response with a hash of any HTTP headers. (Don Peterson)
|
4
|
+
* Add support for a ":cookies" option to be used at the class level, or as an option on any individual call. It should be passed a hash, which will be converted to the proper format and added to the request headers when the call is made. (Don Peterson)
|
5
|
+
* Refactored several specs and added a full suite of cucumber features (Don Peterson)
|
6
|
+
|
1
7
|
== 0.2.8 2009-01-28
|
2
8
|
* 1 major fix
|
3
9
|
* fixed major bug with response where it wouldn't iterate or really work at all with parsed responses
|
data/Manifest
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
bin/httparty
|
2
|
+
cucumber.yml
|
2
3
|
examples/aaws.rb
|
3
4
|
examples/basic.rb
|
4
5
|
examples/delicious.rb
|
@@ -6,9 +7,20 @@ examples/google.rb
|
|
6
7
|
examples/rubyurl.rb
|
7
8
|
examples/twitter.rb
|
8
9
|
examples/whoismyrep.rb
|
10
|
+
features/basic_authentication.feature
|
11
|
+
features/command_line.feature
|
12
|
+
features/deals_with_http_error_codes.feature
|
13
|
+
features/handles_multiple_formats.feature
|
14
|
+
features/steps/env.rb
|
15
|
+
features/steps/httparty_response_steps.rb
|
16
|
+
features/steps/httparty_steps.rb
|
17
|
+
features/steps/mongrel_helper.rb
|
18
|
+
features/steps/remote_service_steps.rb
|
19
|
+
features/supports_redirection.feature
|
9
20
|
History
|
10
21
|
httparty.gemspec
|
11
22
|
lib/core_extensions.rb
|
23
|
+
lib/httparty/cookie_hash.rb
|
12
24
|
lib/httparty/exceptions.rb
|
13
25
|
lib/httparty/request.rb
|
14
26
|
lib/httparty/response.rb
|
@@ -27,6 +39,7 @@ spec/fixtures/google.html
|
|
27
39
|
spec/fixtures/twitter.json
|
28
40
|
spec/fixtures/twitter.xml
|
29
41
|
spec/fixtures/undefined_method_add_node_for_nil.xml
|
42
|
+
spec/httparty/cookie_hash_spec.rb
|
30
43
|
spec/httparty/request_spec.rb
|
31
44
|
spec/httparty_spec.rb
|
32
45
|
spec/spec.opts
|
data/Rakefile
CHANGED
@@ -6,6 +6,7 @@ require 'rake'
|
|
6
6
|
require 'echoe'
|
7
7
|
require 'spec/rake/spectask'
|
8
8
|
require "lib/#{ProjectName}/version"
|
9
|
+
require 'cucumber/rake/task'
|
9
10
|
|
10
11
|
Echoe.new(ProjectName, HTTParty::Version) do |p|
|
11
12
|
p.description = "Makes http fun! Also, makes consuming restful web services dead easy."
|
@@ -40,4 +41,8 @@ Rake::Task[:default].prerequisites.clear
|
|
40
41
|
task :default => :spec
|
41
42
|
Spec::Rake::SpecTask.new do |t|
|
42
43
|
t.spec_files = FileList["spec/**/*_spec.rb"]
|
43
|
-
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
47
|
+
t.cucumber_opts = "--format pretty"
|
48
|
+
end
|
data/cucumber.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
default: features
|
data/examples/basic.rb
CHANGED
@@ -4,7 +4,7 @@ require 'pp'
|
|
4
4
|
|
5
5
|
# You can also use post, put, delete in the same fashion
|
6
6
|
response = HTTParty.get('http://twitter.com/statuses/public_timeline.json')
|
7
|
-
|
7
|
+
puts response.body, response.code, response.headers.inspect
|
8
8
|
|
9
9
|
response.each do |item|
|
10
10
|
puts item['user']['screen_name']
|
data/examples/delicious.rb
CHANGED
data/examples/rubyurl.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
Feature: Basic Authentication
|
2
|
+
|
3
|
+
As a developer
|
4
|
+
I want to be able to use a service that requires Basic Authentication
|
5
|
+
Because that is not an uncommon requirement
|
6
|
+
|
7
|
+
Scenario: Passing no credentials to a page requiring Basic Authentication
|
8
|
+
Given a restricted page at '/protected.html'
|
9
|
+
When I call HTTParty#get with '/protected.html'
|
10
|
+
Then it should return a response with a 401 response code
|
11
|
+
|
12
|
+
Scenario: Passing proper credentials to a page requiring Basic Authentication
|
13
|
+
Given a remote service that returns 'Authenticated Page'
|
14
|
+
And that service is accessed at the path '/protected.html'
|
15
|
+
And that service is protected by Basic Authentication
|
16
|
+
And that service requires the username 'jcash' with the password 'maninblack'
|
17
|
+
When I call HTTParty#get with '/protected.html' and a basic_auth hash:
|
18
|
+
| username | password |
|
19
|
+
| jcash | maninblack |
|
20
|
+
Then the return value should match 'Authenticated Page'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: Deals with HTTP error codes
|
2
|
+
|
3
|
+
As a developer
|
4
|
+
I want to be informed of non-successful responses
|
5
|
+
Because sometimes thing explode
|
6
|
+
And I should probably know what happened
|
7
|
+
|
8
|
+
Scenario: A response of '404 - Not Found'
|
9
|
+
Given a remote service that returns a 404 status code
|
10
|
+
And that service is accessed at the path '/service.html'
|
11
|
+
When I call HTTParty#get with '/service.html'
|
12
|
+
Then it should return a response with a 404 response code
|
13
|
+
|
14
|
+
Scenario: A response of '500 - Internal Server Error'
|
15
|
+
Given a remote service that returns a 500 status code
|
16
|
+
And that service is accessed at the path '/service.html'
|
17
|
+
When I call HTTParty#get with '/service.html'
|
18
|
+
Then it should return a response with a 500 response code
|
19
|
+
|
20
|
+
Scenario: A non-successful response where I need the body
|
21
|
+
Given a remote service that returns a 400 status code
|
22
|
+
And the response from the service has a body of 'Bad response'
|
23
|
+
And that service is accessed at the path '/service.html'
|
24
|
+
When I call HTTParty#get with '/service.html'
|
25
|
+
Then it should return a response with a 400 response code
|
26
|
+
And the return value should match 'Bad response'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Feature: Handles Multiple Formats
|
2
|
+
|
3
|
+
As a developer
|
4
|
+
I want to be able to consume remote services of many different formats
|
5
|
+
And I want those formats to be automatically detected and handled
|
6
|
+
Because web services take many forms
|
7
|
+
And I don't want to have to do any extra work
|
8
|
+
|
9
|
+
Scenario: An HTML service
|
10
|
+
Given a remote service that returns '<h1>Some HTML</h1>'
|
11
|
+
And that service is accessed at the path '/service.html'
|
12
|
+
And the response from the service has a Content-Type of 'text/html'
|
13
|
+
When I call HTTParty#get with '/service.html'
|
14
|
+
Then it should return a String
|
15
|
+
And the return value should match '<h1>Some HTML</h1>'
|
16
|
+
|
17
|
+
Scenario: A JSON service
|
18
|
+
Given a remote service that returns '{ "jennings": "waylon", "cash": "johnny" }'
|
19
|
+
And that service is accessed at the path '/service.json'
|
20
|
+
And the response from the service has a Content-Type of 'application/json'
|
21
|
+
When I call HTTParty#get with '/service.json'
|
22
|
+
Then it should return a Hash equaling:
|
23
|
+
| key | value |
|
24
|
+
| jennings | waylon |
|
25
|
+
| cash | johnny |
|
26
|
+
|
27
|
+
Scenario: An XML Service
|
28
|
+
Given a remote service that returns '<singer>waylon jennings</singer>'
|
29
|
+
And that service is accessed at the path '/service.xml'
|
30
|
+
And the response from the service has a Content-Type of 'text/xml'
|
31
|
+
When I call HTTParty#get with '/service.xml'
|
32
|
+
Then it should return a Hash equaling:
|
33
|
+
| key | value |
|
34
|
+
| singer | waylon jennings |
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'mongrel'
|
2
|
+
require 'activesupport'
|
3
|
+
require 'lib/httparty'
|
4
|
+
require 'spec/expectations'
|
5
|
+
|
6
|
+
Before do
|
7
|
+
port = ENV["HTTPARTY_PORT"] || 31981
|
8
|
+
@host_and_port = "0.0.0.0:#{port}"
|
9
|
+
@server = Mongrel::HttpServer.new("0.0.0.0", port)
|
10
|
+
@server.run
|
11
|
+
end
|
12
|
+
|
13
|
+
After do
|
14
|
+
@server.stop
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Then /it should return an? (\w+)$/ do |class_string|
|
2
|
+
@response_from_httparty.should be_an_instance_of(class_string.constantize)
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /the return value should match '(.*)'/ do |expected_text|
|
6
|
+
@response_from_httparty.should eql(expected_text)
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /it should return a Hash equaling:/ do |hash_table|
|
10
|
+
@response_from_httparty.should be_an_instance_of(Hash)
|
11
|
+
@response_from_httparty.keys.length.should eql(hash_table.rows.length)
|
12
|
+
hash_table.hashes.each do |pair|
|
13
|
+
key, value = pair["key"], pair["value"]
|
14
|
+
@response_from_httparty.keys.should include(key)
|
15
|
+
@response_from_httparty[key].should eql(value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Then /it should return a response with a (\d+) response code/ do |code|
|
20
|
+
@response_from_httparty.code.should eql(code)
|
21
|
+
end
|
22
|
+
|
23
|
+
Then /it should raise an HTTParty::RedirectionTooDeep exception/ do
|
24
|
+
@exception_from_httparty.should_not be_nil
|
25
|
+
@exception_from_httparty.class.should eql(HTTParty::RedirectionTooDeep)
|
26
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
When /I call HTTParty#get with '(.*)'$/ do |url|
|
2
|
+
begin
|
3
|
+
@response_from_httparty = HTTParty.get("http://#{@host_and_port}#{url}")
|
4
|
+
rescue HTTParty::RedirectionTooDeep => e
|
5
|
+
@exception_from_httparty = e
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
When /I call HTTParty#get with '(.*)' and a basic_auth hash:/ do |url, auth_table|
|
10
|
+
h = auth_table.hashes.first
|
11
|
+
@response_from_httparty = HTTParty.get(
|
12
|
+
"http://#{@host_and_port}#{url}",
|
13
|
+
:basic_auth => { :username => h["username"], :password => h["password"] }
|
14
|
+
)
|
15
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
def basic_mongrel_handler
|
2
|
+
Class.new(Mongrel::HttpHandler) do
|
3
|
+
attr_writer :content_type, :response_body, :response_code
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@content_type = "text/html"
|
7
|
+
@response_body = ""
|
8
|
+
@response_code = 200
|
9
|
+
@custom_headers = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def process(request, response)
|
13
|
+
reply_with(response, @response_code, @response_body)
|
14
|
+
end
|
15
|
+
|
16
|
+
def reply_with(response, code, response_body)
|
17
|
+
response.start(code) do |head, body|
|
18
|
+
head["Content-Type"] = @content_type
|
19
|
+
@custom_headers.each { |k,v| head[k] = v }
|
20
|
+
body.write(response_body)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def new_mongrel_handler
|
27
|
+
basic_mongrel_handler.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_basic_authentication_to(handler)
|
31
|
+
m = Module.new do
|
32
|
+
attr_writer :username, :password
|
33
|
+
|
34
|
+
def self.extended(base)
|
35
|
+
base.instance_eval { @custom_headers["WWW-Authenticate"] = 'Basic Realm="Super Secret Page"' }
|
36
|
+
base.class_eval { alias_method_chain :process, :basic_authentication }
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_with_basic_authentication(request, response)
|
40
|
+
if authorized?(request) then process_without_basic_authentication(request, response)
|
41
|
+
else reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def authorized?(request)
|
46
|
+
request.params["HTTP_AUTHORIZATION"] == "Basic " + Base64.encode64("#{@username}:#{@password}").strip
|
47
|
+
end
|
48
|
+
end
|
49
|
+
handler.extend(m)
|
50
|
+
end
|
51
|
+
|
52
|
+
def new_mongrel_redirector(target_url, relative_path = false)
|
53
|
+
target_url = "http://#{@host_and_port}#{target_url}" unless relative_path
|
54
|
+
Mongrel::RedirectHandler.new(target_url)
|
55
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
Given /a remote service that returns '(.*)'/ do |response_body|
|
2
|
+
@handler = new_mongrel_handler
|
3
|
+
Given "the response from the service has a body of '#{response_body}'"
|
4
|
+
end
|
5
|
+
|
6
|
+
Given /a remote service that returns a (\d+) status code/ do |code|
|
7
|
+
@handler = new_mongrel_handler
|
8
|
+
@handler.response_code = code
|
9
|
+
end
|
10
|
+
|
11
|
+
Given /that service is accessed at the path '(.*)'/ do |path|
|
12
|
+
@server.register(path, @handler)
|
13
|
+
end
|
14
|
+
|
15
|
+
Given /the response from the service has a Content-Type of '(.*)'/ do |content_type|
|
16
|
+
@handler.content_type = content_type
|
17
|
+
end
|
18
|
+
|
19
|
+
Given /the response from the service has a body of '(.*)'/ do |response_body|
|
20
|
+
@handler.response_body = response_body
|
21
|
+
end
|
22
|
+
|
23
|
+
Given /the url '(.*)' redirects to '(.*)'/ do |redirection_url, target_url|
|
24
|
+
@server.register redirection_url, new_mongrel_redirector(target_url)
|
25
|
+
end
|
26
|
+
|
27
|
+
Given /that service is protected by Basic Authentication/ do
|
28
|
+
add_basic_authentication_to @handler
|
29
|
+
end
|
30
|
+
|
31
|
+
Given /that service requires the username '(.*)' with the password '(.*)'/ do |username, password|
|
32
|
+
@handler.username = username
|
33
|
+
@handler.password = password
|
34
|
+
end
|
35
|
+
|
36
|
+
Given /a restricted page at '(.*)'/ do |url|
|
37
|
+
Given "a remote service that returns 'A response I will never see'"
|
38
|
+
And "that service is accessed at the path '#{url}'"
|
39
|
+
And "that service is protected by Basic Authentication"
|
40
|
+
And "that service requires the username 'something' with the password 'secret'"
|
41
|
+
end
|
42
|
+
|
43
|
+
# This joins the server thread, and halts cucumber, so you can actually hit the
|
44
|
+
# server with a browser. Runs until you kill it with Ctrl-c
|
45
|
+
Given /I want to hit this in a browser/ do
|
46
|
+
@server.acceptor.join
|
47
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
Feature: Supports Redirection
|
2
|
+
|
3
|
+
As a developer
|
4
|
+
I want to work with services that may redirect me
|
5
|
+
And I want it to follow a reasonable number of redirects
|
6
|
+
Because sometimes web services do that
|
7
|
+
|
8
|
+
Scenario: A service that redirects once
|
9
|
+
Given a remote service that returns 'Service Response'
|
10
|
+
And that service is accessed at the path '/service.html'
|
11
|
+
And the url '/redirector.html' redirects to '/service.html'
|
12
|
+
When I call HTTParty#get with '/redirector.html'
|
13
|
+
Then the return value should match 'Service Response'
|
14
|
+
|
15
|
+
# TODO: Look in to why this actually fails...
|
16
|
+
Scenario: A service that redirects to a relative URL
|
17
|
+
|
18
|
+
Scenario: A service that redirects infinitely
|
19
|
+
Given the url '/first.html' redirects to '/second.html'
|
20
|
+
And the url '/second.html' redirects to '/first.html'
|
21
|
+
When I call HTTParty#get with '/first.html'
|
22
|
+
Then it should raise an HTTParty::RedirectionTooDeep exception
|
data/httparty.gemspec
CHANGED
@@ -2,17 +2,17 @@
|
|
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.9"
|
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-29}
|
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}
|
13
13
|
s.executables = ["httparty"]
|
14
|
-
s.extra_rdoc_files = ["bin/httparty", "lib/core_extensions.rb", "lib/httparty/exceptions.rb", "lib/httparty/request.rb", "lib/httparty/response.rb", "lib/httparty/version.rb", "lib/httparty.rb", "lib/module_level_inheritable_attributes.rb", "README"]
|
15
|
-
s.files = ["bin/httparty", "examples/aaws.rb", "examples/basic.rb", "examples/delicious.rb", "examples/google.rb", "examples/rubyurl.rb", "examples/twitter.rb", "examples/whoismyrep.rb", "History", "httparty.gemspec", "lib/core_extensions.rb", "lib/httparty/exceptions.rb", "lib/httparty/request.rb", "lib/httparty/response.rb", "lib/httparty/version.rb", "lib/httparty.rb", "lib/module_level_inheritable_attributes.rb", "Manifest", "MIT-LICENSE", "Rakefile", "README", "setup.rb", "spec/as_buggery_spec.rb", "spec/fixtures/delicious.xml", "spec/fixtures/empty.xml", "spec/fixtures/google.html", "spec/fixtures/twitter.json", "spec/fixtures/twitter.xml", "spec/fixtures/undefined_method_add_node_for_nil.xml", "spec/httparty/request_spec.rb", "spec/httparty_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "website/css/common.css", "website/index.html"]
|
14
|
+
s.extra_rdoc_files = ["bin/httparty", "lib/core_extensions.rb", "lib/httparty/cookie_hash.rb", "lib/httparty/exceptions.rb", "lib/httparty/request.rb", "lib/httparty/response.rb", "lib/httparty/version.rb", "lib/httparty.rb", "lib/module_level_inheritable_attributes.rb", "README"]
|
15
|
+
s.files = ["bin/httparty", "cucumber.yml", "examples/aaws.rb", "examples/basic.rb", "examples/delicious.rb", "examples/google.rb", "examples/rubyurl.rb", "examples/twitter.rb", "examples/whoismyrep.rb", "features/basic_authentication.feature", "features/command_line.feature", "features/deals_with_http_error_codes.feature", "features/handles_multiple_formats.feature", "features/steps/env.rb", "features/steps/httparty_response_steps.rb", "features/steps/httparty_steps.rb", "features/steps/mongrel_helper.rb", "features/steps/remote_service_steps.rb", "features/supports_redirection.feature", "History", "httparty.gemspec", "lib/core_extensions.rb", "lib/httparty/cookie_hash.rb", "lib/httparty/exceptions.rb", "lib/httparty/request.rb", "lib/httparty/response.rb", "lib/httparty/version.rb", "lib/httparty.rb", "lib/module_level_inheritable_attributes.rb", "Manifest", "MIT-LICENSE", "Rakefile", "README", "setup.rb", "spec/as_buggery_spec.rb", "spec/fixtures/delicious.xml", "spec/fixtures/empty.xml", "spec/fixtures/google.html", "spec/fixtures/twitter.json", "spec/fixtures/twitter.xml", "spec/fixtures/undefined_method_add_node_for_nil.xml", "spec/httparty/cookie_hash_spec.rb", "spec/httparty/request_spec.rb", "spec/httparty_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "website/css/common.css", "website/index.html"]
|
16
16
|
s.has_rdoc = true
|
17
17
|
s.homepage = %q{http://httparty.rubyforge.org}
|
18
18
|
s.post_install_message = %q{When you HTTParty, you must party hard!}
|
data/lib/core_extensions.rb
CHANGED
data/lib/httparty.rb
CHANGED
@@ -58,6 +58,12 @@ module HTTParty
|
|
58
58
|
default_options[:headers] ||= {}
|
59
59
|
default_options[:headers].merge!(h)
|
60
60
|
end
|
61
|
+
|
62
|
+
def cookies(h={})
|
63
|
+
raise ArgumentError, 'Cookies must be a hash' unless h.is_a?(Hash)
|
64
|
+
default_options[:cookies] ||= CookieHash.new
|
65
|
+
default_options[:cookies].add_cookies(h)
|
66
|
+
end
|
61
67
|
|
62
68
|
def format(f)
|
63
69
|
raise UnsupportedFormat, "Must be one of: #{AllowedFormats.values.join(', ')}" unless AllowedFormats.value?(f)
|
@@ -82,8 +88,18 @@ module HTTParty
|
|
82
88
|
|
83
89
|
private
|
84
90
|
def perform_request(http_method, path, options) #:nodoc:
|
91
|
+
process_cookies(options)
|
85
92
|
Request.new(http_method, path, default_options.dup.merge(options)).perform
|
86
93
|
end
|
94
|
+
|
95
|
+
def process_cookies(options) #:nodoc:
|
96
|
+
return unless options[:cookies] || default_options[:cookies]
|
97
|
+
options[:headers] ||= {}
|
98
|
+
options[:headers]["cookie"] = cookies(options[:cookies] || {}).to_cookie_string
|
99
|
+
|
100
|
+
default_options.delete(:cookies)
|
101
|
+
options.delete(:cookies)
|
102
|
+
end
|
87
103
|
end
|
88
104
|
|
89
105
|
def self.normalize_base_uri(url) #:nodoc:
|
@@ -119,4 +135,5 @@ end
|
|
119
135
|
|
120
136
|
require 'httparty/exceptions'
|
121
137
|
require 'httparty/request'
|
122
|
-
require 'httparty/response'
|
138
|
+
require 'httparty/response'
|
139
|
+
require 'httparty/cookie_hash'
|
data/lib/httparty/request.rb
CHANGED
data/lib/httparty/response.rb
CHANGED
@@ -1,27 +1,16 @@
|
|
1
1
|
module HTTParty
|
2
|
-
class Response
|
3
|
-
attr_accessor :body, :code
|
2
|
+
class Response < BlankSlate
|
3
|
+
attr_accessor :body, :code, :headers
|
4
4
|
|
5
|
-
def initialize(delegate, body, code)
|
5
|
+
def initialize(delegate, body, code, headers)
|
6
6
|
@delegate = delegate
|
7
7
|
@body = body
|
8
8
|
@code = code
|
9
|
+
@headers = headers
|
9
10
|
end
|
10
11
|
|
11
12
|
def method_missing(name, *args, &block)
|
12
13
|
@delegate.send(name, *args, &block)
|
13
14
|
end
|
14
|
-
|
15
|
-
def ==(other)
|
16
|
-
@delegate == other
|
17
|
-
end
|
18
|
-
|
19
|
-
def nil?
|
20
|
-
@delegate.nil?
|
21
|
-
end
|
22
|
-
|
23
|
-
def pretty_print(q)
|
24
|
-
@delegate.pretty_print(q)
|
25
|
-
end
|
26
15
|
end
|
27
|
-
end
|
16
|
+
end
|
data/lib/httparty/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '../spec_helper')
|
2
|
+
|
3
|
+
describe HTTParty::CookieHash do
|
4
|
+
before(:each) do
|
5
|
+
@cookie_hash = HTTParty::CookieHash.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#add_cookies" do
|
9
|
+
it "should add new key/value pairs to the hash" do
|
10
|
+
@cookie_hash.add_cookies(:foo => "bar")
|
11
|
+
@cookie_hash.add_cookies(:rofl => "copter")
|
12
|
+
@cookie_hash.length.should eql(2)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should overwrite any existing key" do
|
16
|
+
@cookie_hash.add_cookies(:foo => "bar")
|
17
|
+
@cookie_hash.add_cookies(:foo => "copter")
|
18
|
+
@cookie_hash.length.should eql(1)
|
19
|
+
@cookie_hash[:foo].should eql("copter")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# The regexen are required because Hashes aren't ordered, so a test against
|
24
|
+
# a hardcoded string was randomly failing.
|
25
|
+
describe "#to_cookie_string" do
|
26
|
+
before(:each) do
|
27
|
+
@cookie_hash.add_cookies(:foo => "bar")
|
28
|
+
@cookie_hash.add_cookies(:rofl => "copter")
|
29
|
+
@s = @cookie_hash.to_cookie_string
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should format the key/value pairs, delimited by semi-colons" do
|
33
|
+
@s.should match(/foo=bar/)
|
34
|
+
@s.should match(/rofl=copter/)
|
35
|
+
@s.should match(/^\w+=\w+; \w+=\w+$/)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,6 +1,20 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
2
|
|
3
3
|
describe HTTParty::Request do
|
4
|
+
def stub_response(body, code = 200)
|
5
|
+
unless @http
|
6
|
+
@http = Net::HTTP.new('localhost', 80)
|
7
|
+
@request.stub!(:http).and_return(@http)
|
8
|
+
@request.stub!(:uri).and_return(URI.parse("http://foo.com/foobar"))
|
9
|
+
end
|
10
|
+
|
11
|
+
response = Net::HTTPResponse::CODE_TO_OBJ[code.to_s].new("1.1", code, body)
|
12
|
+
response.stub!(:body).and_return(body)
|
13
|
+
|
14
|
+
@http.stub!(:request).and_return(response)
|
15
|
+
response
|
16
|
+
end
|
17
|
+
|
4
18
|
before do
|
5
19
|
@request = HTTParty::Request.new(Net::HTTP::Get, 'http://api.foo.com/v1', :format => :xml)
|
6
20
|
end
|
@@ -79,14 +93,19 @@ describe HTTParty::Request do
|
|
79
93
|
@request.options[:format] = :json
|
80
94
|
@request.send(:parse_response, json).should == {'books' => {'book' => {'id' => '1234', 'name' => 'Foo Bar!'}}}
|
81
95
|
end
|
96
|
+
|
97
|
+
it "should include any HTTP headers in the returned response" do
|
98
|
+
@request.options[:format] = :html
|
99
|
+
response = stub_response "Content"
|
100
|
+
response.initialize_http_header("key" => "value")
|
101
|
+
|
102
|
+
@request.perform.headers.should == { "key" => ["value"] }
|
103
|
+
end
|
82
104
|
|
83
105
|
describe 'with non-200 responses' do
|
84
106
|
|
85
107
|
it 'should return a valid object for 4xx response' do
|
86
|
-
|
87
|
-
http_response.stub!(:body).and_return('<foo><bar>yes</bar></foo>')
|
88
|
-
|
89
|
-
@request.should_receive(:get_response).and_return(http_response)
|
108
|
+
stub_response '<foo><bar>yes</bar></foo>', 401
|
90
109
|
resp = @request.perform
|
91
110
|
resp.code.should == 401
|
92
111
|
resp.body.should == "<foo><bar>yes</bar></foo>"
|
@@ -94,10 +113,7 @@ describe HTTParty::Request do
|
|
94
113
|
end
|
95
114
|
|
96
115
|
it 'should return a valid object for 5xx response' do
|
97
|
-
|
98
|
-
http_response.stub!(:body).and_return('<foo><bar>error</bar></foo>')
|
99
|
-
|
100
|
-
@request.should_receive(:get_response).and_return(http_response)
|
116
|
+
stub_response '<foo><bar>error</bar></foo>', 500
|
101
117
|
resp = @request.perform
|
102
118
|
resp.code.should == 500
|
103
119
|
resp.body.should == "<foo><bar>error</bar></foo>"
|
@@ -108,92 +124,59 @@ describe HTTParty::Request do
|
|
108
124
|
end
|
109
125
|
|
110
126
|
it "should not attempt to parse empty responses" do
|
111
|
-
|
112
|
-
@request.stub!(:http).and_return(http)
|
113
|
-
response = Net::HTTPNoContent.new("1.1", 204, "No content for you")
|
114
|
-
response.stub!(:body).and_return(nil)
|
115
|
-
http.stub!(:request).and_return(response)
|
116
|
-
|
117
|
-
@request.options[:format] = :xml
|
118
|
-
@request.perform.should be_nil
|
127
|
+
stub_response "", 204
|
119
128
|
|
120
|
-
|
129
|
+
@request.options[:format] = :xml
|
121
130
|
@request.perform.should be_nil
|
122
131
|
end
|
123
132
|
|
124
133
|
it "should not fail for missing mime type" do
|
125
|
-
|
126
|
-
@request.stub!(:http).and_return(http)
|
127
|
-
|
128
|
-
response = Net::HTTPOK.new("1.1", 200, "Content for you")
|
129
|
-
response.stub!(:[]).with('content-type').and_return(nil)
|
130
|
-
response.stub!(:body).and_return('Content for you')
|
131
|
-
|
132
|
-
http.stub!(:request).and_return(response)
|
133
|
-
|
134
|
+
stub_response "Content for you"
|
134
135
|
@request.options[:format] = :html
|
135
136
|
@request.perform.should == 'Content for you'
|
136
137
|
end
|
137
138
|
|
138
|
-
describe "
|
139
|
-
|
140
|
-
@
|
141
|
-
@request.stub!(:http).and_return(@http)
|
142
|
-
@request.stub!(:uri).and_return(URI.parse("http://foo.com/foobar"))
|
143
|
-
@redirect = Net::HTTPFound.new("1.1", 302, "")
|
139
|
+
describe "a request that redirects" do
|
140
|
+
before(:each) do
|
141
|
+
@redirect = stub_response("", 302)
|
144
142
|
@redirect['location'] = '/foo'
|
145
|
-
end
|
146
|
-
|
147
|
-
def setup_ok_response
|
148
|
-
@ok = Net::HTTPOK.new("1.1", 200, "Content for you")
|
149
|
-
@ok.stub!(:body).and_return('<hash><foo>bar</foo></hash>')
|
150
|
-
@http.should_receive(:request).and_return(@redirect, @ok)
|
151
|
-
@request.options[:format] = :xml
|
152
|
-
end
|
153
143
|
|
154
|
-
|
155
|
-
@http.stub!(:request).and_return(@redirect)
|
144
|
+
@ok = stub_response('<hash><foo>bar</foo></hash>', 200)
|
156
145
|
end
|
157
146
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
def setup_infinite_redirect
|
164
|
-
setup_redirect
|
165
|
-
setup_redirect_response
|
166
|
-
end
|
147
|
+
describe "once" do
|
148
|
+
before(:each) do
|
149
|
+
@http.stub!(:request).and_return(@redirect, @ok)
|
150
|
+
end
|
167
151
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
152
|
+
it "should be handled by GET transparently" do
|
153
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
154
|
+
end
|
172
155
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
end
|
156
|
+
it "should be handled by POST transparently" do
|
157
|
+
@request.http_method = Net::HTTP::Post
|
158
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
159
|
+
end
|
178
160
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
end
|
161
|
+
it "should be handled by DELETE transparently" do
|
162
|
+
@request.http_method = Net::HTTP::Delete
|
163
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
164
|
+
end
|
184
165
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
166
|
+
it "should be handled by PUT transparently" do
|
167
|
+
@request.http_method = Net::HTTP::Put
|
168
|
+
@request.perform.should == {"hash" => {"foo" => "bar"}}
|
169
|
+
end
|
189
170
|
end
|
190
171
|
|
191
|
-
|
192
|
-
|
172
|
+
describe "infinitely" do
|
173
|
+
before(:each) do
|
174
|
+
@http.stub!(:request).and_return(@redirect)
|
175
|
+
end
|
193
176
|
|
194
|
-
|
195
|
-
@request.perform
|
196
|
-
end
|
177
|
+
it "should raise an exception" do
|
178
|
+
lambda { @request.perform }.should raise_error(HTTParty::RedirectionTooDeep)
|
179
|
+
end
|
197
180
|
end
|
198
181
|
end
|
199
182
|
end
|
@@ -204,4 +187,4 @@ describe HTTParty::Request, "with POST http method" do
|
|
204
187
|
HTTParty::Request.new(Net::HTTP::Post, 'http://api.foo.com/v1', :format => :xml, :query => 'astring').perform
|
205
188
|
}.should raise_error(ArgumentError)
|
206
189
|
end
|
207
|
-
end
|
190
|
+
end
|
data/spec/httparty_spec.rb
CHANGED
@@ -1,36 +1,23 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
2
|
|
3
|
-
class Foo
|
4
|
-
include HTTParty
|
5
|
-
base_uri 'api.foo.com/v1'
|
6
|
-
end
|
7
|
-
|
8
|
-
class GRest
|
9
|
-
include HTTParty
|
10
|
-
base_uri "grest.com"
|
11
|
-
default_params :one => 'two'
|
12
|
-
end
|
13
|
-
|
14
|
-
class HRest
|
15
|
-
include HTTParty
|
16
|
-
base_uri "hrest.com"
|
17
|
-
default_params :two => 'three'
|
18
|
-
end
|
19
|
-
|
20
3
|
describe HTTParty do
|
21
|
-
|
4
|
+
before(:each) do
|
5
|
+
@klass = Class.new
|
6
|
+
@klass.instance_eval { include HTTParty }
|
7
|
+
end
|
8
|
+
|
22
9
|
describe "base uri" do
|
23
|
-
before do
|
24
|
-
|
10
|
+
before(:each) do
|
11
|
+
@klass.base_uri('api.foo.com/v1')
|
25
12
|
end
|
26
13
|
|
27
14
|
it "should have reader" do
|
28
|
-
|
15
|
+
@klass.base_uri.should == 'http://api.foo.com/v1'
|
29
16
|
end
|
30
17
|
|
31
18
|
it 'should have writer' do
|
32
|
-
|
33
|
-
|
19
|
+
@klass.base_uri('http://api.foobar.com')
|
20
|
+
@klass.base_uri.should == 'http://api.foobar.com'
|
34
21
|
end
|
35
22
|
end
|
36
23
|
|
@@ -53,49 +40,113 @@ describe HTTParty do
|
|
53
40
|
|
54
41
|
describe "headers" do
|
55
42
|
it "should default to empty hash" do
|
56
|
-
|
43
|
+
@klass.headers.should == {}
|
57
44
|
end
|
58
45
|
|
59
46
|
it "should be able to be updated" do
|
60
47
|
init_headers = {:foo => 'bar', :baz => 'spax'}
|
61
|
-
|
62
|
-
|
48
|
+
@klass.headers init_headers
|
49
|
+
@klass.headers.should == init_headers
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "cookies" do
|
54
|
+
def expect_cookie_header(s)
|
55
|
+
HTTParty::Request.should_receive(:new) \
|
56
|
+
.with(anything, anything, hash_including({ :headers => { "cookie" => s } })) \
|
57
|
+
.and_return(mock("mock response", :perform => nil))
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should not be in the headers by default" do
|
61
|
+
HTTParty::Request.stub!(:new).and_return(stub(nil, :perform => nil))
|
62
|
+
@klass.get("")
|
63
|
+
@klass.headers.keys.should_not include("cookie")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should raise an ArgumentError if passed a non-Hash" do
|
67
|
+
lambda do
|
68
|
+
@klass.cookies("nonsense")
|
69
|
+
end.should raise_error(ArgumentError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should allow a cookie to be specified with a one-off request" do
|
73
|
+
expect_cookie_header "type=snickerdoodle"
|
74
|
+
@klass.get("", :cookies => { :type => "snickerdoodle" })
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "when a cookie is set at the class level" do
|
78
|
+
before(:each) do
|
79
|
+
@klass.cookies({ :type => "snickerdoodle" })
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should include that cookie in the request" do
|
83
|
+
expect_cookie_header "type=snickerdoodle"
|
84
|
+
@klass.get("")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should allow the class defaults to be overridden" do
|
88
|
+
expect_cookie_header "type=chocolate_chip"
|
89
|
+
|
90
|
+
@klass.get("", :cookies => { :type => "chocolate_chip" })
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "in a class with multiple methods that use different cookies" do
|
95
|
+
before(:each) do
|
96
|
+
@klass.instance_eval do
|
97
|
+
def first_method
|
98
|
+
get("first_method", :cookies => { :first_method_cookie => "foo" })
|
99
|
+
end
|
100
|
+
|
101
|
+
def second_method
|
102
|
+
get("second_method", :cookies => { :second_method_cookie => "foo" })
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should not allow cookies used in one method to carry over into other methods" do
|
108
|
+
expect_cookie_header "first_method_cookie=foo"
|
109
|
+
@klass.first_method
|
110
|
+
|
111
|
+
expect_cookie_header "second_method_cookie=foo"
|
112
|
+
@klass.second_method
|
113
|
+
end
|
63
114
|
end
|
64
115
|
end
|
65
116
|
|
66
117
|
describe "default params" do
|
67
118
|
it "should default to empty hash" do
|
68
|
-
|
119
|
+
@klass.default_params.should == {}
|
69
120
|
end
|
70
121
|
|
71
122
|
it "should be able to be updated" do
|
72
123
|
new_defaults = {:foo => 'bar', :baz => 'spax'}
|
73
|
-
|
74
|
-
|
124
|
+
@klass.default_params new_defaults
|
125
|
+
@klass.default_params.should == new_defaults
|
75
126
|
end
|
76
127
|
end
|
77
128
|
|
78
129
|
describe "basic http authentication" do
|
79
130
|
it "should work" do
|
80
|
-
|
81
|
-
|
131
|
+
@klass.basic_auth 'foobar', 'secret'
|
132
|
+
@klass.default_options[:basic_auth].should == {:username => 'foobar', :password => 'secret'}
|
82
133
|
end
|
83
134
|
end
|
84
135
|
|
85
136
|
describe "format" do
|
86
137
|
it "should allow xml" do
|
87
|
-
|
88
|
-
|
138
|
+
@klass.format :xml
|
139
|
+
@klass.default_options[:format].should == :xml
|
89
140
|
end
|
90
141
|
|
91
142
|
it "should allow json" do
|
92
|
-
|
93
|
-
|
143
|
+
@klass.format :json
|
144
|
+
@klass.default_options[:format].should == :json
|
94
145
|
end
|
95
146
|
|
96
147
|
it 'should not allow funky format' do
|
97
148
|
lambda do
|
98
|
-
|
149
|
+
@klass.format :foobar
|
99
150
|
end.should raise_error(HTTParty::UnsupportedFormat)
|
100
151
|
end
|
101
152
|
end
|
@@ -104,33 +155,47 @@ describe HTTParty do
|
|
104
155
|
|
105
156
|
it "should fail with redirected GET" do
|
106
157
|
lambda do
|
107
|
-
|
158
|
+
@klass.get('/foo', :no_follow => true)
|
108
159
|
end.should raise_error(HTTParty::RedirectionTooDeep)
|
109
160
|
end
|
110
161
|
|
111
162
|
it "should fail with redirected POST" do
|
112
163
|
lambda do
|
113
|
-
|
164
|
+
@klass.post('/foo', :no_follow => true)
|
114
165
|
end.should raise_error(HTTParty::RedirectionTooDeep)
|
115
166
|
end
|
116
167
|
|
117
168
|
it "should fail with redirected DELETE" do
|
118
169
|
lambda do
|
119
|
-
|
170
|
+
@klass.delete('/foo', :no_follow => true)
|
120
171
|
end.should raise_error(HTTParty::RedirectionTooDeep)
|
121
172
|
end
|
122
173
|
|
123
174
|
it "should fail with redirected PUT" do
|
124
175
|
lambda do
|
125
|
-
|
176
|
+
@klass.put('/foo', :no_follow => true)
|
126
177
|
end.should raise_error(HTTParty::RedirectionTooDeep)
|
127
178
|
end
|
128
179
|
end
|
129
180
|
|
130
181
|
describe "with multiple class definitions" do
|
182
|
+
before(:each) do
|
183
|
+
@klass.instance_eval do
|
184
|
+
base_uri "http://first.com"
|
185
|
+
default_params :one => 1
|
186
|
+
end
|
187
|
+
|
188
|
+
@additional_klass = Class.new
|
189
|
+
@additional_klass.instance_eval do
|
190
|
+
include HTTParty
|
191
|
+
base_uri "http://second.com"
|
192
|
+
default_params :two => 2
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
131
196
|
it "should not run over each others options" do
|
132
|
-
|
133
|
-
|
197
|
+
@klass.default_options.should == { :base_uri => 'http://first.com', :default_params => { :one => 1 } }
|
198
|
+
@additional_klass.default_options.should == { :base_uri => 'http://second.com', :default_params => { :two => 2 } }
|
134
199
|
end
|
135
200
|
end
|
136
201
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httparty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
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-29 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -41,6 +41,7 @@ extensions: []
|
|
41
41
|
extra_rdoc_files:
|
42
42
|
- bin/httparty
|
43
43
|
- lib/core_extensions.rb
|
44
|
+
- lib/httparty/cookie_hash.rb
|
44
45
|
- lib/httparty/exceptions.rb
|
45
46
|
- lib/httparty/request.rb
|
46
47
|
- lib/httparty/response.rb
|
@@ -50,6 +51,7 @@ extra_rdoc_files:
|
|
50
51
|
- README
|
51
52
|
files:
|
52
53
|
- bin/httparty
|
54
|
+
- cucumber.yml
|
53
55
|
- examples/aaws.rb
|
54
56
|
- examples/basic.rb
|
55
57
|
- examples/delicious.rb
|
@@ -57,9 +59,20 @@ files:
|
|
57
59
|
- examples/rubyurl.rb
|
58
60
|
- examples/twitter.rb
|
59
61
|
- examples/whoismyrep.rb
|
62
|
+
- features/basic_authentication.feature
|
63
|
+
- features/command_line.feature
|
64
|
+
- features/deals_with_http_error_codes.feature
|
65
|
+
- features/handles_multiple_formats.feature
|
66
|
+
- features/steps/env.rb
|
67
|
+
- features/steps/httparty_response_steps.rb
|
68
|
+
- features/steps/httparty_steps.rb
|
69
|
+
- features/steps/mongrel_helper.rb
|
70
|
+
- features/steps/remote_service_steps.rb
|
71
|
+
- features/supports_redirection.feature
|
60
72
|
- History
|
61
73
|
- httparty.gemspec
|
62
74
|
- lib/core_extensions.rb
|
75
|
+
- lib/httparty/cookie_hash.rb
|
63
76
|
- lib/httparty/exceptions.rb
|
64
77
|
- lib/httparty/request.rb
|
65
78
|
- lib/httparty/response.rb
|
@@ -78,6 +91,7 @@ files:
|
|
78
91
|
- spec/fixtures/twitter.json
|
79
92
|
- spec/fixtures/twitter.xml
|
80
93
|
- spec/fixtures/undefined_method_add_node_for_nil.xml
|
94
|
+
- spec/httparty/cookie_hash_spec.rb
|
81
95
|
- spec/httparty/request_spec.rb
|
82
96
|
- spec/httparty_spec.rb
|
83
97
|
- spec/spec.opts
|