persistent_httparty 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.1.0 - 2012-09-09
2
+
3
+ * initial release
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Matt Campbell
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ [![Build Status](https://secure.travis-ci.org/soupmatt/persistent_httparty.png?branch=master)](http://travis-ci.org/soupmatt/persistent_httparty)
2
+
3
+ # persistent_httparty
4
+
5
+ Persistent HTTP connections for HTTParty!
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'persistent_httparty'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install persistent_httparty
20
+
21
+ ## Requirements
22
+
23
+ * [httparty](/jnunemaker/httparty) and [persistent_http](/bpardee/persistent_http)
24
+ * You like to `Keep-Alive` the party!
25
+
26
+ ## Usage
27
+
28
+ Just call `persistent_connection_adapter` and then use HTTParty as
29
+ normal.
30
+
31
+ ```ruby
32
+ class Twitter
33
+ include HTTParty
34
+ persistent_connection_adapter
35
+ end
36
+ ```
37
+
38
+ You can also pass in parameters to the `persistent_http` gem. The
39
+ regular HTTParty config will be passed through as applicable.
40
+
41
+ ```ruby
42
+ class MyCoolRestClient
43
+ include HTTParty
44
+ persistent_connection_adapter { :name => 'my_cool_rest_client',
45
+ :pool_size => 2,
46
+ :idle_timeout => 10,
47
+ :keep-alive => 30 }
48
+ end
49
+ ```
50
+
51
+ ## License
52
+
53
+ Distributed under the [MIT License](/soupmatt/persistent_httparty/blob/master/LICENSE)
54
+
55
+ ## Special Thanks
56
+
57
+ * To @jnunemaker for maintaining a tight ship on the extremely useful
58
+ [httparty](/jnunemaker/httparty)
59
+ * To @bpardee for writing the best persistent http connection library
60
+ for ruby I've found in [persistent_http](/bpardee/persistent_http)
61
+ * To @vibes for letting me open source as much of the work I do there
62
+ as possible.
63
+
64
+ ## Contributing
65
+
66
+ 1. Fork it
67
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
68
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
69
+ 4. Push to the branch (`git push origin my-new-feature`)
70
+ 5. Create new Pull Request
@@ -0,0 +1,13 @@
1
+ Feature: Persistent HTTP Connections
2
+
3
+ Scenario:
4
+ Given I have an http end point "http://www.example.com/api/thing.json"
5
+ And I perform a "get" against that end point
6
+ And The end point returns "application/json"
7
+ """
8
+ {"name":"foo", "type":"thing"}
9
+ """
10
+ When I visit that end point with HTTParty
11
+ Then the parsed_response contains
12
+ | name | foo |
13
+ | type | thing |
@@ -0,0 +1,22 @@
1
+ Given /^I have an http end point "(.*?)"$/ do |uri|
2
+ @web_request = WebRequest.new
3
+ @web_request.uri = uri
4
+ end
5
+
6
+ Given /^I perform a "(.*?)" against that end point$/ do |verb|
7
+ @web_request.verb = verb
8
+ end
9
+
10
+ Given /^The end point returns "(.*?)"$/ do |content_type, body|
11
+ @web_request.content_type = content_type
12
+ @web_request.response_body = body
13
+ end
14
+
15
+ When /^I visit that end point with HTTParty$/ do
16
+ @web_request.mock
17
+ @response = @web_request.perform
18
+ end
19
+
20
+ Then /^the parsed_response contains$/ do |table|
21
+ @response.parsed_response.should == table.rows_hash
22
+ end
@@ -0,0 +1 @@
1
+ require 'persistent_httparty'
@@ -0,0 +1,40 @@
1
+ class TestHTTPartyClient
2
+ include HTTParty
3
+ persistent_connection_adapter
4
+ end
5
+
6
+ class WebRequest
7
+ include WebMock::API
8
+
9
+ attr_accessor :uri, :verb, :content_type, :request_body, :response_body
10
+
11
+ def initialize
12
+ @uri = 'http://www.example.com/'
13
+ @verb = :get
14
+ @content_type = 'text/plain'
15
+ @response_body = 'Hello!'
16
+ end
17
+
18
+ def verb=(arg)
19
+ unless arg.is_a? Symbol
20
+ @verb = arg.to_s.downcase.to_sym
21
+ else
22
+ @verb = arg
23
+ end
24
+ end
25
+
26
+ VALID_VERBS = %w(get head post put delete patch options).collect { |v| v.to_sym }
27
+
28
+ def mock
29
+ stub_request(verb, uri).to_return(:body => response_body, :headers => {:content_type => content_type})
30
+ end
31
+
32
+ def perform
33
+ case verb
34
+ when :get
35
+ TestHTTPartyClient.get(uri)
36
+ else
37
+ raise "#{verb} is not a supported HTTP verb"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1 @@
1
+ require 'webmock/cucumber'
@@ -0,0 +1,15 @@
1
+ require 'httparty'
2
+
3
+ module HTTParty::Persistent
4
+ module ClassMethods
5
+ def persistent_connection_adapter(opts={})
6
+ connection_adapter(HTTParty::Persistent::ConnectionAdapter.new, opts)
7
+ end
8
+ end
9
+ end
10
+
11
+ HTTParty::ClassMethods.module_exec do
12
+ include HTTParty::Persistent::ClassMethods
13
+ end
14
+
15
+ require 'httparty/persistent/connection_adapter'
@@ -0,0 +1,34 @@
1
+ require 'persistent_http'
2
+
3
+ module HTTParty::Persistent
4
+ class ConnectionAdapter
5
+
6
+ attr_accessor :persistent_http
7
+
8
+ def call(uri, options)
9
+ if @persistent_http.nil?
10
+ @persistent_http = build_persistent_http(uri, options)
11
+ end
12
+ @persistent_http
13
+ end
14
+
15
+ def build_persistent_http(uri, options)
16
+ opts = {:url => uri}
17
+ opts.merge!(options[:connection_adapter_options]) if options[:connection_adapter_options]
18
+ if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
19
+ opts.merge!(:read_timeout => options[:timeout], :open_timeout => options[:timeout])
20
+ end
21
+ opts.merge!(:debug_output => options[:debug_output]) if options[:debug_output]
22
+
23
+ if options[:http_proxyaddr]
24
+ proxy_opts = {:host => options[:http_proxyaddr]}
25
+ proxy_opts[:port] = options[:http_proxyport] if options[:http_proxyport]
26
+ opts[:proxy] = URI::HTTP.build(proxy_opts)
27
+ opts[:proxy].user = options[:http_proxyuser] if options[:http_proxyuser]
28
+ opts[:proxy].password = options[:http_proxypass] if options[:http_proxypass]
29
+ end
30
+
31
+ PersistentHTTP.new(opts)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,2 @@
1
+ require "persistent_httparty/version"
2
+ require "httparty/persistent"
@@ -0,0 +1,3 @@
1
+ module PersistentHttparty
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+
3
+ describe HTTParty::Persistent::ConnectionAdapter do
4
+ let(:uri) { URI 'http://foo.com:8085' }
5
+ let(:connection_adapter_options) { {} }
6
+ let(:options) { {:connection_adapter_options => connection_adapter_options} }
7
+ let!(:adapter) { HTTParty::Persistent::ConnectionAdapter.new }
8
+ subject { adapter }
9
+
10
+ describe "#call" do
11
+ subject { adapter.call(uri, options) }
12
+
13
+ it "returns a PersistentHTTP" do
14
+ subject.should be_a PersistentHTTP
15
+ end
16
+
17
+ it "returns the same PersistentHTTP across calls" do
18
+ adapter.call(uri, options).should be subject
19
+ end
20
+
21
+ describe "the resulting PersistentHTTP" do
22
+ subject { adapter.call(uri, options) }
23
+
24
+ it { should_not be_nil }
25
+ it { should be_instance_of PersistentHTTP }
26
+ its(:host) { should == uri.host }
27
+ its(:port) { should == uri.port }
28
+
29
+ it "is the same across multiple calls" do
30
+ adapter.call(uri, options).should be subject
31
+ adapter.call(uri, options).should be subject
32
+ end
33
+
34
+ context "when dealing with ssl" do
35
+ Spec::Matchers.define :use_ssl do
36
+ match do |connection|
37
+ connection.use_ssl
38
+ end
39
+ end
40
+
41
+ context "using port 443 for ssl" do
42
+ let(:uri) { URI 'https://api.foo.com/v1:443' }
43
+ it { should use_ssl }
44
+ end
45
+
46
+ context "using port 80" do
47
+ let(:uri) { URI 'http://foobar.com' }
48
+ it { should_not use_ssl }
49
+ end
50
+
51
+ context "https scheme with default port" do
52
+ let(:uri) { URI 'https://foobar.com' }
53
+ it { should use_ssl }
54
+ end
55
+
56
+ context "https scheme with non-standard port" do
57
+ let(:uri) { URI 'https://foobar.com:123456' }
58
+ it { should use_ssl }
59
+ end
60
+ end
61
+
62
+ context "when setting timeout" do
63
+ context "to 5 seconds" do
64
+ let(:options) { {:timeout => 5} }
65
+
66
+ its(:open_timeout) { should == 5 }
67
+ its(:read_timeout) { should == 5 }
68
+ end
69
+
70
+ context "and timeout is a string" do
71
+ let(:options) { {:timeout => "five seconds"} }
72
+
73
+ it "doesn't set the timeout" do
74
+ subject.open_timeout.should be_nil
75
+ subject.read_timeout.should be_nil
76
+ end
77
+ end
78
+ end
79
+
80
+ context "when debug_output" do
81
+ context "is set to $stderr" do
82
+ let(:options) { {:debug_output => $stderr} }
83
+ it "has debug output set" do
84
+ subject.debug_output.should == $stderr
85
+ end
86
+ end
87
+
88
+ context "is not provided" do
89
+ let(:options) { {} }
90
+ it "does not set_debug_output" do
91
+ subject.debug_output.should be_nil
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'when providing proxy address and port' do
97
+ let(:options) { {:http_proxyaddr => '1.2.3.4', :http_proxyport => 8080} }
98
+
99
+ its(:proxy_uri) { should == URI.parse('http://1.2.3.4:8080') }
100
+
101
+ context 'as well as proxy user and password' do
102
+ let(:options) do
103
+ {:http_proxyaddr => '1.2.3.4', :http_proxyport => 8080,
104
+ :http_proxyuser => 'user', :http_proxypass => 'pass'}
105
+ end
106
+ its(:proxy_uri) do
107
+ uri = URI.parse('http://1.2.3.4:8080')
108
+ uri.user = 'user'
109
+ uri.password = 'pass'
110
+ should == uri
111
+ end
112
+ end
113
+ end
114
+
115
+ context "when providing PEM certificates", :pending => true do
116
+ let(:pem) { :pem_contents }
117
+ let(:options) { {:pem => pem, :pem_password => "password"} }
118
+
119
+ context "when scheme is https" do
120
+ let(:uri) { URI 'https://google.com' }
121
+ let(:cert) { mock("OpenSSL::X509::Certificate") }
122
+ let(:key) { mock("OpenSSL::PKey::RSA") }
123
+
124
+ before do
125
+ OpenSSL::X509::Certificate.should_receive(:new).with(pem).and_return(cert)
126
+ OpenSSL::PKey::RSA.should_receive(:new).with(pem, "password").and_return(key)
127
+ end
128
+
129
+ it "uses the provided PEM certificate " do
130
+ subject.cert.should == cert
131
+ subject.key.should == key
132
+ end
133
+
134
+ it "will verify the certificate" do
135
+ subject.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
136
+ end
137
+ end
138
+
139
+ context "when scheme is not https" do
140
+ let(:uri) { URI 'http://google.com' }
141
+ let(:http) { Net::HTTP.new(uri) }
142
+
143
+ before do
144
+ Net::HTTP.stub(:new => http)
145
+ OpenSSL::X509::Certificate.should_not_receive(:new).with(pem)
146
+ OpenSSL::PKey::RSA.should_not_receive(:new).with(pem, "password")
147
+ http.should_not_receive(:cert=)
148
+ http.should_not_receive(:key=)
149
+ end
150
+
151
+ it "has no PEM certificate " do
152
+ subject.cert.should be_nil
153
+ subject.key.should be_nil
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ describe HTTParty::Persistent do
4
+ let!(:klass) { Class.new }
5
+ before(:each) do
6
+ klass.instance_eval { include HTTParty }
7
+ end
8
+
9
+ describe HTTParty do
10
+ it "includes HTTParty::Persistent" do
11
+ HTTParty::ClassMethods.should include_module(HTTParty::Persistent::ClassMethods)
12
+ end
13
+
14
+ context "across multiple requests" do
15
+ let(:base_uri) { URI 'http://example.com' }
16
+
17
+ it "reuses the same http connection" do
18
+ http = Net::HTTP.new(base_uri.host, base_uri.port)
19
+ Net::HTTP.should_receive(:new).once().and_return(http)
20
+
21
+ stub_request(:get, "#{base_uri.to_s}/status").
22
+ to_return(:status => 200, :body => "", :headers => {})
23
+
24
+ stub_request(:get, "#{base_uri.to_s}/info").
25
+ to_return(:status => 200, :body => "", :headers => {})
26
+
27
+ klass.base_uri base_uri.to_s
28
+ klass.persistent_connection_adapter
29
+
30
+ klass.get('/status')
31
+ klass.get('/info')
32
+ end
33
+ end
34
+ end
35
+
36
+ describe "#persistent_connection_adapter" do
37
+ before(:each) { klass.persistent_connection_adapter }
38
+
39
+ it "sets the connection_adapter to HTTParty::Persistent::ConnectionAdapter" do
40
+ klass.connection_adapter.should be_a HTTParty::Persistent::ConnectionAdapter
41
+ end
42
+
43
+ context "with connection_adapter_options" do
44
+ let(:connection_adapter_options) { {:foo => :bar} }
45
+ before(:each) { klass.persistent_connection_adapter(connection_adapter_options) }
46
+
47
+ it "sets the connection_adapter_options that are passed to it" do
48
+ klass.default_options[:connection_adapter_options].should == connection_adapter_options
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,25 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'persistent_httparty'
9
+ require 'webmock/rspec'
10
+
11
+ app_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
12
+
13
+ Dir[File.join(app_root, "spec/support/**/*.rb")].each {|f| require f}
14
+
15
+ RSpec.configure do |config|
16
+ config.treat_symbols_as_metadata_keys_with_true_values = true
17
+ config.run_all_when_everything_filtered = true
18
+ config.filter_run :focus
19
+
20
+ # Run specs in random order to surface order dependencies. If you find an
21
+ # order dependency and want to debug it, you can fix the order by providing
22
+ # the seed, which is printed after each run.
23
+ # --seed 1234
24
+ config.order = 'random'
25
+ end
@@ -0,0 +1,11 @@
1
+ RSpec::Matchers.define :include_module do |module_to_include|
2
+ match do |subject|
3
+ subject.include? module_to_include
4
+ end
5
+ end
6
+
7
+ RSpec::Matchers.define :define_instance_variable do |instance_variable|
8
+ match do |subject|
9
+ subject.instance_variable_defined? instance_variable
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: persistent_httparty
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Matt Campbell
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: persistent_http
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 2.11.0
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 2.11.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: cucumber
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: webmock
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Persistent HTTP connections for HTTParty using the persistent_http gem.
111
+ Keep the party alive!
112
+ email:
113
+ - persistent_httparty@soupmatt.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - lib/httparty/persistent.rb
119
+ - lib/httparty/persistent/connection_adapter.rb
120
+ - lib/persistent_httparty.rb
121
+ - lib/persistent_httparty/version.rb
122
+ - README.md
123
+ - LICENSE
124
+ - CHANGELOG.md
125
+ - features/persistent_http_connections.feature
126
+ - features/step_definitions/persistent_http_connections_steps.rb
127
+ - features/step_definitions/support/env.rb
128
+ - features/step_definitions/support/web_request.rb
129
+ - features/support/env.rb
130
+ - spec/lib/httparty/persistent/connection_adapter_spec.rb
131
+ - spec/lib/httparty/persistent_spec.rb
132
+ - spec/spec_helper.rb
133
+ - spec/support/matchers.rb
134
+ homepage: https://github.com/soupmatt/persistent_httparty
135
+ licenses: []
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ segments:
147
+ - 0
148
+ hash: -710231127126955515
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ! '>='
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ segments:
156
+ - 0
157
+ hash: -710231127126955515
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 1.8.23
161
+ signing_key:
162
+ specification_version: 3
163
+ summary: Persistent HTTP connections for HTTParty
164
+ test_files:
165
+ - features/persistent_http_connections.feature
166
+ - features/step_definitions/persistent_http_connections_steps.rb
167
+ - features/step_definitions/support/env.rb
168
+ - features/step_definitions/support/web_request.rb
169
+ - features/support/env.rb
170
+ - spec/lib/httparty/persistent/connection_adapter_spec.rb
171
+ - spec/lib/httparty/persistent_spec.rb
172
+ - spec/spec_helper.rb
173
+ - spec/support/matchers.rb