jnunemaker-httparty 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
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
- 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'] }
data/examples/rubyurl.rb CHANGED
@@ -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
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.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
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'
@@ -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: jnunemaker-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 -08:00
12
+ date: 2009-01-29 00:00:00 -08:00
13
13
  default_executable: httparty
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -39,6 +39,7 @@ extensions: []
39
39
  extra_rdoc_files:
40
40
  - bin/httparty
41
41
  - lib/core_extensions.rb
42
+ - lib/httparty/cookie_hash.rb
42
43
  - lib/httparty/exceptions.rb
43
44
  - lib/httparty/request.rb
44
45
  - lib/httparty/response.rb
@@ -48,6 +49,7 @@ extra_rdoc_files:
48
49
  - README
49
50
  files:
50
51
  - bin/httparty
52
+ - cucumber.yml
51
53
  - examples/aaws.rb
52
54
  - examples/basic.rb
53
55
  - examples/delicious.rb
@@ -55,9 +57,20 @@ files:
55
57
  - examples/rubyurl.rb
56
58
  - examples/twitter.rb
57
59
  - examples/whoismyrep.rb
60
+ - features/basic_authentication.feature
61
+ - features/command_line.feature
62
+ - features/deals_with_http_error_codes.feature
63
+ - features/handles_multiple_formats.feature
64
+ - features/steps/env.rb
65
+ - features/steps/httparty_response_steps.rb
66
+ - features/steps/httparty_steps.rb
67
+ - features/steps/mongrel_helper.rb
68
+ - features/steps/remote_service_steps.rb
69
+ - features/supports_redirection.feature
58
70
  - History
59
71
  - httparty.gemspec
60
72
  - lib/core_extensions.rb
73
+ - lib/httparty/cookie_hash.rb
61
74
  - lib/httparty/exceptions.rb
62
75
  - lib/httparty/request.rb
63
76
  - lib/httparty/response.rb
@@ -76,6 +89,7 @@ files:
76
89
  - spec/fixtures/twitter.json
77
90
  - spec/fixtures/twitter.xml
78
91
  - spec/fixtures/undefined_method_add_node_for_nil.xml
92
+ - spec/httparty/cookie_hash_spec.rb
79
93
  - spec/httparty/request_spec.rb
80
94
  - spec/httparty_spec.rb
81
95
  - spec/spec.opts