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 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
@@ -0,0 +1 @@
1
+ default: features
@@ -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
- pp response
7
+ puts response.body, response.code, response.headers.inspect
8
8
 
9
9
  response.each do |item|
10
10
  puts item['user']['screen_name']
@@ -34,3 +34,4 @@ delicious = Delicious.new(config['username'], config['password'])
34
34
  pp delicious.posts(:query => {:tag => 'ruby'})
35
35
  pp delicious.recent
36
36
 
37
+ delicious.recent['posts']['post'].each { |post| puts post['href'] }
@@ -11,4 +11,4 @@ class Rubyurl
11
11
  end
12
12
  end
13
13
 
14
- pp Rubyurl.shorten( 'http://istwitterdown.com/' )
14
+ pp Rubyurl.shorten( 'http://istwitterdown.com/')
@@ -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,7 @@
1
+ Feature: Command Line
2
+
3
+ As a developer
4
+ I want to be able to harness the power of HTTParty from the command line
5
+ Because that would make quick testing and debugging easy
6
+ And 'easy' is my middle name
7
+ And I'm kidding it's actually 'Danger'!
@@ -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
@@ -2,17 +2,17 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{httparty}
5
- s.version = "0.2.8"
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-28}
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!}
@@ -346,4 +346,8 @@ class Hash
346
346
  %{#{k.to_s.snake_case.sub(/^(.{1,1})/) { |m| m.downcase }}="#{v}"}
347
347
  end.join(' ')
348
348
  end
349
- end
349
+ end
350
+
351
+ class BlankSlate
352
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
353
+ end
@@ -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'
@@ -0,0 +1,9 @@
1
+ class HTTParty::CookieHash < Hash
2
+ def add_cookies(hash)
3
+ merge!(hash)
4
+ end
5
+
6
+ def to_cookie_string
7
+ collect { |k, v| "#{k}=#{v}" }.join("; ")
8
+ end
9
+ end
@@ -93,7 +93,7 @@ module HTTParty
93
93
  perform
94
94
  else
95
95
  parsed_response = parse_response(response.body)
96
- Response.new(parsed_response, response.body, response.code)
96
+ Response.new(parsed_response, response.body, response.code, response.to_hash)
97
97
  end
98
98
  end
99
99
 
@@ -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
@@ -1,3 +1,3 @@
1
1
  module HTTParty
2
- Version = '0.2.8'
2
+ Version = '0.2.9'
3
3
  end
@@ -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
- 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)
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
- 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)
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
- http = Net::HTTP.new('localhost', 80)
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
- response.stub!(:body).and_return("")
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
- http = Net::HTTP.new('localhost', 80)
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 "that respond with redirects" do
139
- def setup_redirect
140
- @http = Net::HTTP.new('localhost', 80)
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
- def setup_redirect_response
155
- @http.stub!(:request).and_return(@redirect)
144
+ @ok = stub_response('<hash><foo>bar</foo></hash>', 200)
156
145
  end
157
146
 
158
- def setup_successful_redirect
159
- setup_redirect
160
- setup_ok_response
161
- end
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
- it "should handle redirects for GET transparently" do
169
- setup_successful_redirect
170
- @request.perform.should == {"hash" => {"foo" => "bar"}}
171
- end
152
+ it "should be handled by GET transparently" do
153
+ @request.perform.should == {"hash" => {"foo" => "bar"}}
154
+ end
172
155
 
173
- it "should handle redirects for POST transparently" do
174
- setup_successful_redirect
175
- @request.http_method = Net::HTTP::Post
176
- @request.perform.should == {"hash" => {"foo" => "bar"}}
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
- it "should handle redirects for DELETE transparently" do
180
- setup_successful_redirect
181
- @request.http_method = Net::HTTP::Delete
182
- @request.perform.should == {"hash" => {"foo" => "bar"}}
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
- it "should handle redirects for PUT transparently" do
186
- setup_successful_redirect
187
- @request.http_method = Net::HTTP::Put
188
- @request.perform.should == {"hash" => {"foo" => "bar"}}
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
- it "should prevent infinite loops" do
192
- setup_infinite_redirect
172
+ describe "infinitely" do
173
+ before(:each) do
174
+ @http.stub!(:request).and_return(@redirect)
175
+ end
193
176
 
194
- lambda do
195
- @request.perform
196
- end.should raise_error(HTTParty::RedirectionTooDeep)
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
@@ -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
- Foo.base_uri('api.foo.com/v1')
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
- Foo.base_uri.should == 'http://api.foo.com/v1'
15
+ @klass.base_uri.should == 'http://api.foo.com/v1'
29
16
  end
30
17
 
31
18
  it 'should have writer' do
32
- Foo.base_uri('http://api.foobar.com')
33
- Foo.base_uri.should == 'http://api.foobar.com'
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
- Foo.headers.should == {}
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
- Foo.headers init_headers
62
- Foo.headers.should == init_headers
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
- Foo.default_params.should == {}
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
- Foo.default_params new_defaults
74
- Foo.default_params.should == new_defaults
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
- Foo.basic_auth 'foobar', 'secret'
81
- Foo.default_options[:basic_auth].should == {:username => 'foobar', :password => 'secret'}
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
- Foo.format :xml
88
- Foo.default_options[:format].should == :xml
138
+ @klass.format :xml
139
+ @klass.default_options[:format].should == :xml
89
140
  end
90
141
 
91
142
  it "should allow json" do
92
- Foo.format :json
93
- Foo.default_options[:format].should == :json
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
- Foo.format :foobar
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
- Foo.get('/foo', :no_follow => true)
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
- Foo.post('/foo', :no_follow => true)
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
- Foo.delete('/foo', :no_follow => true)
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
- Foo.put('/foo', :no_follow => true)
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
- HRest.default_options.should == {:base_uri => 'http://hrest.com', :default_params => {:two => 'three'}}
133
- GRest.default_options.should == {:base_uri => 'http://grest.com', :default_params => {:one => 'two'}}
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.8
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-28 00:00:00 -05:00
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