bartzon-httparty 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/.gitignore +8 -0
  2. data/History +216 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest +47 -0
  5. data/README.rdoc +54 -0
  6. data/Rakefile +89 -0
  7. data/VERSION +1 -0
  8. data/bartzon-httparty.gemspec +147 -0
  9. data/bin/httparty +108 -0
  10. data/cucumber.yml +1 -0
  11. data/examples/aaws.rb +32 -0
  12. data/examples/basic.rb +11 -0
  13. data/examples/custom_parsers.rb +67 -0
  14. data/examples/delicious.rb +37 -0
  15. data/examples/google.rb +16 -0
  16. data/examples/rubyurl.rb +14 -0
  17. data/examples/twitter.rb +31 -0
  18. data/examples/whoismyrep.rb +10 -0
  19. data/features/basic_authentication.feature +20 -0
  20. data/features/command_line.feature +7 -0
  21. data/features/deals_with_http_error_codes.feature +26 -0
  22. data/features/digest_authentication.feature +20 -0
  23. data/features/handles_compressed_responses.feature +19 -0
  24. data/features/handles_multiple_formats.feature +34 -0
  25. data/features/steps/env.rb +23 -0
  26. data/features/steps/httparty_response_steps.rb +26 -0
  27. data/features/steps/httparty_steps.rb +27 -0
  28. data/features/steps/mongrel_helper.rb +94 -0
  29. data/features/steps/remote_service_steps.rb +69 -0
  30. data/features/supports_redirection.feature +22 -0
  31. data/features/supports_timeout_option.feature +13 -0
  32. data/httparty.gemspec +146 -0
  33. data/lib/httparty.rb +383 -0
  34. data/lib/httparty/cookie_hash.rb +22 -0
  35. data/lib/httparty/core_extensions.rb +31 -0
  36. data/lib/httparty/exceptions.rb +26 -0
  37. data/lib/httparty/module_inheritable_attributes.rb +34 -0
  38. data/lib/httparty/net_digest_auth.rb +35 -0
  39. data/lib/httparty/parser.rb +141 -0
  40. data/lib/httparty/request.rb +277 -0
  41. data/lib/httparty/response.rb +79 -0
  42. data/spec/fixtures/delicious.xml +23 -0
  43. data/spec/fixtures/empty.xml +0 -0
  44. data/spec/fixtures/google.html +3 -0
  45. data/spec/fixtures/ssl/generate.sh +29 -0
  46. data/spec/fixtures/ssl/generated/1fe462c2.0 +15 -0
  47. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  48. data/spec/fixtures/ssl/generated/ca.crt +15 -0
  49. data/spec/fixtures/ssl/generated/ca.key +15 -0
  50. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  51. data/spec/fixtures/ssl/generated/server.crt +13 -0
  52. data/spec/fixtures/ssl/generated/server.key +15 -0
  53. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  54. data/spec/fixtures/twitter.json +1 -0
  55. data/spec/fixtures/twitter.xml +403 -0
  56. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  57. data/spec/httparty/cookie_hash_spec.rb +71 -0
  58. data/spec/httparty/parser_spec.rb +155 -0
  59. data/spec/httparty/request_spec.rb +488 -0
  60. data/spec/httparty/response_spec.rb +188 -0
  61. data/spec/httparty/ssl_spec.rb +55 -0
  62. data/spec/httparty_spec.rb +570 -0
  63. data/spec/spec.opts +3 -0
  64. data/spec/spec_helper.rb +20 -0
  65. data/spec/support/ssl_test_helper.rb +25 -0
  66. data/spec/support/ssl_test_server.rb +69 -0
  67. data/spec/support/stub_response.rb +30 -0
  68. data/website/css/common.css +47 -0
  69. data/website/index.html +73 -0
  70. metadata +244 -0
@@ -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 '/html_service.html'
12
+ And the response from the service has a Content-Type of 'text/html'
13
+ When I call HTTParty#get with '/html_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,23 @@
1
+ require 'mongrel'
2
+ require 'active_support'
3
+ require 'lib/httparty'
4
+ require 'spec/expectations'
5
+
6
+ Before do
7
+ def new_port
8
+ server = TCPServer.new('0.0.0.0', nil)
9
+ port = server.addr[1]
10
+ ensure
11
+ server.close
12
+ end
13
+
14
+ port = ENV["HTTPARTY_PORT"] || new_port
15
+ @host_and_port = "0.0.0.0:#{port}"
16
+ @server = Mongrel::HttpServer.new("0.0.0.0", port)
17
+ @server.run
18
+ @request_options = {}
19
+ end
20
+
21
+ After do
22
+ @server.stop
23
+ 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.class)
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.to_i)
21
+ end
22
+
23
+ Then /it should raise (?:an|a) ([\w:]+) exception/ do |exception|
24
+ @exception_from_httparty.should_not be_nil
25
+ @exception_from_httparty.class.name.should eql(exception)
26
+ end
@@ -0,0 +1,27 @@
1
+ When /^I set my HTTParty timeout option to (\d+)$/ do |timeout|
2
+ @request_options[:timeout] = timeout.to_i
3
+ end
4
+
5
+ When /I call HTTParty#get with '(.*)'$/ do |url|
6
+ begin
7
+ @response_from_httparty = HTTParty.get("http://#{@host_and_port}#{url}", @request_options)
8
+ rescue HTTParty::RedirectionTooDeep, Timeout::Error => e
9
+ @exception_from_httparty = e
10
+ end
11
+ end
12
+
13
+ When /I call HTTParty#get with '(.*)' and a basic_auth hash:/ do |url, auth_table|
14
+ h = auth_table.hashes.first
15
+ @response_from_httparty = HTTParty.get(
16
+ "http://#{@host_and_port}#{url}",
17
+ :basic_auth => { :username => h["username"], :password => h["password"] }
18
+ )
19
+ end
20
+
21
+ When /I call HTTParty#get with '(.*)' and a digest_auth hash:/ do |url, auth_table|
22
+ h = auth_table.hashes.first
23
+ @response_from_httparty = HTTParty.get(
24
+ "http://#{@host_and_port}#{url}",
25
+ :digest_auth => { :username => h["username"], :password => h["password"] }
26
+ )
27
+ end
@@ -0,0 +1,94 @@
1
+ require 'base64'
2
+ class BasicMongrelHandler < Mongrel::HttpHandler
3
+ attr_accessor :content_type, :custom_headers, :response_body, :response_code, :preprocessor, :username, :password
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
+ instance_eval &preprocessor if preprocessor
14
+ reply_with(response, response_code, response_body)
15
+ end
16
+
17
+ def reply_with(response, code, response_body)
18
+ response.start(code) do |head, body|
19
+ head["Content-Type"] = content_type
20
+ custom_headers.each { |k,v| head[k] = v }
21
+ body.write(response_body)
22
+ end
23
+ end
24
+ end
25
+
26
+ class DeflateHandler < BasicMongrelHandler
27
+ def process(request, response)
28
+ response.start do |head, body|
29
+ head['Content-Encoding'] = 'deflate'
30
+ body.write Zlib::Deflate.deflate(response_body)
31
+ end
32
+ end
33
+ end
34
+
35
+ class GzipHandler < BasicMongrelHandler
36
+ def process(request, response)
37
+ response.start do |head, body|
38
+ head['Content-Encoding'] = 'gzip'
39
+ body.write gzip(response_body)
40
+ end
41
+ end
42
+
43
+ protected
44
+
45
+ def gzip(string)
46
+ sio = StringIO.new('', 'r+')
47
+ gz = Zlib::GzipWriter.new sio
48
+ gz.write string
49
+ gz.finish
50
+ sio.rewind
51
+ sio.read
52
+ end
53
+ end
54
+
55
+ module BasicAuthentication
56
+ def self.extended(base)
57
+ base.custom_headers["WWW-Authenticate"] = 'Basic Realm="Super Secret Page"'
58
+ end
59
+
60
+ def process(request, response)
61
+ if authorized?(request)
62
+ super
63
+ else
64
+ reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
65
+ end
66
+ end
67
+
68
+ def authorized?(request)
69
+ request.params["HTTP_AUTHORIZATION"] == "Basic " + Base64.encode64("#{@username}:#{@password}").strip
70
+ end
71
+ end
72
+
73
+ module DigestAuthentication
74
+ def self.extended(base)
75
+ base.custom_headers["WWW-Authenticate"] = 'Digest realm="testrealm@host.com",qop="auth,auth-int",nonce="nonce",opaque="opaque"'
76
+ end
77
+
78
+ def process(request, response)
79
+ if authorized?(request)
80
+ super
81
+ else
82
+ reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
83
+ end
84
+ end
85
+
86
+ def authorized?(request)
87
+ request.params["HTTP_AUTHORIZATION"] =~ /Digest.*uri=/
88
+ end
89
+ end
90
+
91
+ def new_mongrel_redirector(target_url, relative_path = false)
92
+ target_url = "http://#{@host_and_port}#{target_url}" unless relative_path
93
+ Mongrel::RedirectHandler.new(target_url)
94
+ end
@@ -0,0 +1,69 @@
1
+ Given /a remote service that returns '(.*)'/ do |response_body|
2
+ @handler = BasicMongrelHandler.new
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 = BasicMongrelHandler.new
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 /^that service takes (\d+) seconds to generate a response$/ do |time|
16
+ @server_response_time = time.to_i
17
+ @handler.preprocessor = lambda { sleep time.to_i }
18
+ end
19
+
20
+ Given /^a remote deflate service$/ do
21
+ @handler = DeflateHandler.new
22
+ end
23
+
24
+ Given /^a remote gzip service$/ do
25
+ @handler = GzipHandler.new
26
+ end
27
+
28
+ Given /the response from the service has a Content-Type of '(.*)'/ do |content_type|
29
+ @handler.content_type = content_type
30
+ end
31
+
32
+ Given /the response from the service has a body of '(.*)'/ do |response_body|
33
+ @handler.response_body = response_body
34
+ end
35
+
36
+ Given /the url '(.*)' redirects to '(.*)'/ do |redirection_url, target_url|
37
+ @server.register redirection_url, new_mongrel_redirector(target_url)
38
+ end
39
+
40
+ Given /that service is protected by Basic Authentication/ do
41
+ @handler.extend BasicAuthentication
42
+ end
43
+
44
+ Given /that service is protected by Digest Authentication/ do
45
+ @handler.extend DigestAuthentication
46
+ end
47
+
48
+ Given /that service requires the username '(.*)' with the password '(.*)'/ do |username, password|
49
+ @handler.username = username
50
+ @handler.password = password
51
+ end
52
+
53
+ Given /a restricted page at '(.*)'/ do |url|
54
+ Given "a remote service that returns 'A response I will never see'"
55
+ And "that service is accessed at the path '#{url}'"
56
+ And "that service is protected by Basic Authentication"
57
+ And "that service requires the username 'something' with the password 'secret'"
58
+ end
59
+
60
+ # This joins the server thread, and halts cucumber, so you can actually hit the
61
+ # server with a browser. Runs until you kill it with Ctrl-c
62
+ Given /I want to hit this in a browser/ do
63
+ @server.acceptor.join
64
+ end
65
+
66
+ Then /I wait for the server to recover/ do
67
+ timeout = @request_options[:timeout] || 0
68
+ sleep @server_response_time - timeout
69
+ 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 '/landing_service.html'
11
+ And the url '/redirector.html' redirects to '/landing_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
@@ -0,0 +1,13 @@
1
+ Feature: Supports the timeout option
2
+ In order to handle inappropriately slow response times
3
+ As a developer
4
+ I want my request to raise an exception after my specified timeout as elapsed
5
+
6
+ Scenario: A long running response
7
+ Given a remote service that returns '<h1>Some HTML</h1>'
8
+ And that service is accessed at the path '/long_running_service.html'
9
+ And that service takes 2 seconds to generate a response
10
+ When I set my HTTParty timeout option to 1
11
+ And I call HTTParty#get with '/long_running_service.html'
12
+ Then it should raise a Timeout::Error exception
13
+ And I wait for the server to recover
data/httparty.gemspec ADDED
@@ -0,0 +1,146 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{httparty}
8
+ s.version = "0.6.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["John Nunemaker", "Sandro Turriate"]
12
+ s.date = %q{2010-08-09}
13
+ s.default_executable = %q{httparty}
14
+ s.description = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
15
+ s.email = %q{nunemaker@gmail.com}
16
+ s.executables = ["httparty"]
17
+ s.extra_rdoc_files = [
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ "History",
23
+ "MIT-LICENSE",
24
+ "Manifest",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "bin/httparty",
29
+ "cucumber.yml",
30
+ "examples/aaws.rb",
31
+ "examples/basic.rb",
32
+ "examples/custom_parsers.rb",
33
+ "examples/delicious.rb",
34
+ "examples/google.rb",
35
+ "examples/rubyurl.rb",
36
+ "examples/twitter.rb",
37
+ "examples/whoismyrep.rb",
38
+ "features/basic_authentication.feature",
39
+ "features/command_line.feature",
40
+ "features/deals_with_http_error_codes.feature",
41
+ "features/digest_authentication.feature",
42
+ "features/handles_compressed_responses.feature",
43
+ "features/handles_multiple_formats.feature",
44
+ "features/steps/env.rb",
45
+ "features/steps/httparty_response_steps.rb",
46
+ "features/steps/httparty_steps.rb",
47
+ "features/steps/mongrel_helper.rb",
48
+ "features/steps/remote_service_steps.rb",
49
+ "features/supports_redirection.feature",
50
+ "features/supports_timeout_option.feature",
51
+ "httparty.gemspec",
52
+ "lib/httparty.rb",
53
+ "lib/httparty/cookie_hash.rb",
54
+ "lib/httparty/core_extensions.rb",
55
+ "lib/httparty/exceptions.rb",
56
+ "lib/httparty/module_inheritable_attributes.rb",
57
+ "lib/httparty/net_digest_auth.rb",
58
+ "lib/httparty/parser.rb",
59
+ "lib/httparty/request.rb",
60
+ "lib/httparty/response.rb",
61
+ "spec/fixtures/delicious.xml",
62
+ "spec/fixtures/empty.xml",
63
+ "spec/fixtures/google.html",
64
+ "spec/fixtures/ssl/generate.sh",
65
+ "spec/fixtures/ssl/generated/1fe462c2.0",
66
+ "spec/fixtures/ssl/generated/bogushost.crt",
67
+ "spec/fixtures/ssl/generated/ca.crt",
68
+ "spec/fixtures/ssl/generated/ca.key",
69
+ "spec/fixtures/ssl/generated/selfsigned.crt",
70
+ "spec/fixtures/ssl/generated/server.crt",
71
+ "spec/fixtures/ssl/generated/server.key",
72
+ "spec/fixtures/ssl/openssl-exts.cnf",
73
+ "spec/fixtures/twitter.json",
74
+ "spec/fixtures/twitter.xml",
75
+ "spec/fixtures/undefined_method_add_node_for_nil.xml",
76
+ "spec/httparty/cookie_hash_spec.rb",
77
+ "spec/httparty/parser_spec.rb",
78
+ "spec/httparty/request_spec.rb",
79
+ "spec/httparty/response_spec.rb",
80
+ "spec/httparty/ssl_spec.rb",
81
+ "spec/httparty_spec.rb",
82
+ "spec/spec.opts",
83
+ "spec/spec_helper.rb",
84
+ "spec/support/ssl_test_helper.rb",
85
+ "spec/support/ssl_test_server.rb",
86
+ "spec/support/stub_response.rb",
87
+ "website/css/common.css",
88
+ "website/index.html"
89
+ ]
90
+ s.homepage = %q{http://httparty.rubyforge.org}
91
+ s.post_install_message = %q{When you HTTParty, you must party hard!}
92
+ s.rdoc_options = ["--charset=UTF-8"]
93
+ s.require_paths = ["lib"]
94
+ s.rubyforge_project = %q{httparty}
95
+ s.rubygems_version = %q{1.3.7}
96
+ s.summary = %q{Makes http fun! Also, makes consuming restful web services dead easy.}
97
+ s.test_files = [
98
+ "spec/httparty/cookie_hash_spec.rb",
99
+ "spec/httparty/parser_spec.rb",
100
+ "spec/httparty/request_spec.rb",
101
+ "spec/httparty/response_spec.rb",
102
+ "spec/httparty/ssl_spec.rb",
103
+ "spec/httparty_spec.rb",
104
+ "spec/spec_helper.rb",
105
+ "spec/support/ssl_test_helper.rb",
106
+ "spec/support/ssl_test_server.rb",
107
+ "spec/support/stub_response.rb",
108
+ "examples/aaws.rb",
109
+ "examples/basic.rb",
110
+ "examples/custom_parsers.rb",
111
+ "examples/delicious.rb",
112
+ "examples/google.rb",
113
+ "examples/rubyurl.rb",
114
+ "examples/twitter.rb",
115
+ "examples/whoismyrep.rb"
116
+ ]
117
+
118
+ if s.respond_to? :specification_version then
119
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
120
+ s.specification_version = 3
121
+
122
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
123
+ s.add_runtime_dependency(%q<crack>, ["= 0.1.8"])
124
+ s.add_development_dependency(%q<activesupport>, ["~> 2.3"])
125
+ s.add_development_dependency(%q<cucumber>, ["~> 0.7"])
126
+ s.add_development_dependency(%q<fakeweb>, ["~> 1.2"])
127
+ s.add_development_dependency(%q<mongrel>, ["~> 1.1"])
128
+ s.add_development_dependency(%q<rspec>, ["~> 1.3"])
129
+ else
130
+ s.add_dependency(%q<crack>, ["= 0.1.8"])
131
+ s.add_dependency(%q<activesupport>, ["~> 2.3"])
132
+ s.add_dependency(%q<cucumber>, ["~> 0.7"])
133
+ s.add_dependency(%q<fakeweb>, ["~> 1.2"])
134
+ s.add_dependency(%q<mongrel>, ["~> 1.1"])
135
+ s.add_dependency(%q<rspec>, ["~> 1.3"])
136
+ end
137
+ else
138
+ s.add_dependency(%q<crack>, ["= 0.1.8"])
139
+ s.add_dependency(%q<activesupport>, ["~> 2.3"])
140
+ s.add_dependency(%q<cucumber>, ["~> 0.7"])
141
+ s.add_dependency(%q<fakeweb>, ["~> 1.2"])
142
+ s.add_dependency(%q<mongrel>, ["~> 1.1"])
143
+ s.add_dependency(%q<rspec>, ["~> 1.3"])
144
+ end
145
+ end
146
+