pooled_curb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 07a94a2385231e6e44493d622a452b8254438ca1
4
+ data.tar.gz: ab901074e21f2c8538beaa7393c50adbe997adc1
5
+ SHA512:
6
+ metadata.gz: 1a72f0f29ec9546a96d5bdc940e2742c08eff8655e9b2977d9a33cf7ef5267bd895700d100789c7ce142bfc5b6439748eacec187db2c8fbd42ee0a7ed5342e1f
7
+ data.tar.gz: 99e288f8ab485c8da3d5d89dbb086aaa57eda51e189c24d5a930d3cb83d78587b4baa3a321948c041f9b332ced35d2a6e3d92b7c715fb7bb6887a896997b8459
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+
13
+ ## Environment normalisation:
14
+ /.bundle/
15
+ /lib/bundler/man/
16
+
17
+ # Ignore bundler config.
18
+ /.bundle
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
@@ -0,0 +1 @@
1
+ pooled-curb
@@ -0,0 +1 @@
1
+ 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+
@@ -0,0 +1,46 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pooled_curb (1.0.0)
5
+ activesupport (~> 4.1.6)
6
+ connection_pool (~> 2.0)
7
+ curb (~> 0.8)
8
+ yajl-ruby (~> 1.2)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ activesupport (4.1.6)
14
+ i18n (~> 0.6, >= 0.6.9)
15
+ json (~> 1.7, >= 1.7.7)
16
+ minitest (~> 5.1)
17
+ thread_safe (~> 0.1)
18
+ tzinfo (~> 1.1)
19
+ connection_pool (2.0.0)
20
+ curb (0.8.6)
21
+ diff-lcs (1.2.5)
22
+ i18n (0.7.0.beta1)
23
+ json (1.8.1)
24
+ minitest (5.4.2)
25
+ rake (10.1.0)
26
+ rspec (2.14.1)
27
+ rspec-core (~> 2.14.0)
28
+ rspec-expectations (~> 2.14.0)
29
+ rspec-mocks (~> 2.14.0)
30
+ rspec-core (2.14.8)
31
+ rspec-expectations (2.14.5)
32
+ diff-lcs (>= 1.1.3, < 2.0)
33
+ rspec-mocks (2.14.6)
34
+ thread_safe (0.3.4)
35
+ tzinfo (1.2.2)
36
+ thread_safe (~> 0.1)
37
+ yajl-ruby (1.2.1)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ json (~> 1.8)
44
+ pooled_curb!
45
+ rake (~> 10.1)
46
+ rspec (~> 2.14)
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 500px, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,60 @@
1
+ Pooled CuRb gem
2
+ ===============
3
+
4
+ A simple wrapper around curb to provide a pool of reusable HTTP connections.
5
+
6
+ Usage
7
+ -----
8
+
9
+ ```
10
+ gem install pooled_curb
11
+ ```
12
+
13
+ Or, with bundler
14
+
15
+ ```ruby
16
+ gem 'pooled_curb'
17
+ ```
18
+
19
+ Then use it:
20
+
21
+ ```ruby
22
+ require 'pooled_curb'
23
+
24
+ PooledCurb.with_client do |curb|
25
+ ...
26
+ end
27
+ ```
28
+
29
+ Alternatively, issue HTTP commands directly:
30
+
31
+ ```ruby
32
+ require 'pooled_curb'
33
+
34
+ response = PooledCurb.get("http://example.com")
35
+ ```
36
+
37
+ License
38
+ -------
39
+
40
+ The MIT License (MIT)
41
+
42
+ Copyright (c) 2014 500px, Inc.
43
+
44
+ Permission is hereby granted, free of charge, to any person obtaining a copy
45
+ of this software and associated documentation files (the "Software"), to deal
46
+ in the Software without restriction, including without limitation the rights
47
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
48
+ copies of the Software, and to permit persons to whom the Software is
49
+ furnished to do so, subject to the following conditions:
50
+
51
+ The above copyright notice and this permission notice shall be included in
52
+ all copies or substantial portions of the Software.
53
+
54
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
55
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
56
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
57
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
58
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
59
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
60
+ THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
@@ -0,0 +1,175 @@
1
+ require 'active_support'
2
+ require 'connection_pool'
3
+ require 'curb'
4
+
5
+ # A Curb-based client which uses the "connection_pool" gem to allow high-performance
6
+ # HTTP requests in a multi-threaded environment.
7
+ #
8
+ # Based on: https://github.com/karmi/tire-contrib/blob/master/lib/tire/http/clients/pooled_curb.rb
9
+ #
10
+ # Example:
11
+ # --------
12
+ #
13
+ # require 'pooled_curb'
14
+ #
15
+
16
+ class PooledCurb
17
+ cattr_accessor :pool_size, :pool_timeout, :read_timeout, :write_timeout
18
+
19
+ POOL_SIZE = 1
20
+ POOL_TIMEOUT = 5.0 # seconds
21
+ READ_TIMEOUT = 5 # seconds
22
+ WRITE_TIMEOUT = 30 # seconds
23
+
24
+ RETRIES = 3
25
+ RETRY_WAIT = 0.2 # second
26
+
27
+ def self.configure
28
+ yield self
29
+
30
+ self
31
+ end
32
+
33
+ def self.reset!
34
+ [:pool_size, :pool_timeout, :read_timeout, :write_timeout].each do |attr_name|
35
+ send("#{attr_name}=", nil)
36
+ end
37
+
38
+ @pool = nil
39
+
40
+ self
41
+ end
42
+
43
+ def self.connection_pool
44
+ @pool ||= ConnectionPool.new(connection_pool_opts) do |config|
45
+ Curl::Easy.new
46
+ end
47
+ end
48
+
49
+ def self.disconnect!
50
+ if @pool
51
+ @pool.shutdown { |client| client.close }
52
+ @pool = nil
53
+ end
54
+ end
55
+
56
+ def self.with_client(&block)
57
+ connection_pool.with(&block)
58
+ end
59
+
60
+ def self.head(url, headers: {})
61
+ perform_with_retry(:head, url, timeout: _read_timeout, headers: headers)
62
+ end
63
+
64
+ def self.get(url, headers: {})
65
+ perform_with_retry(:get, url, timeout: _read_timeout, headers: headers)
66
+ end
67
+
68
+ def self.post(url, data, headers: {})
69
+ perform_with_retry(:post, url, post_body: to_post_data(data), timeout: _write_timeout, headers: headers)
70
+ end
71
+
72
+ def self.multipart_form_post(url, data, headers: {})
73
+ perform_with_retry(:post, url, post_body: to_post_data(data), multipart_form_post: true, timeout: _write_timeout, headers: headers)
74
+ end
75
+
76
+ def self.put(url, data, headers: {})
77
+
78
+ perform_with_retry(:put, url, post_body: data, timeout: _write_timeout, headers: headers)
79
+ end
80
+
81
+ def self.delete(url, headers: {})
82
+ perform_with_retry(:delete, url, timeout: _write_timeout, headers: headers)
83
+ end
84
+
85
+ class Response
86
+ attr_reader :body, :status, :header_str
87
+
88
+ def initialize(status, header_str, body)
89
+ @body = body
90
+ @header_str = header_str
91
+ @status = status
92
+ end
93
+
94
+ def success?
95
+ status <= 299
96
+ end
97
+
98
+ def failure?
99
+ status >= 400
100
+ end
101
+
102
+ def headers
103
+ return @headers if @headers
104
+
105
+ headers = @header_str.split("\r\n")
106
+ headers.shift # Remove HTTP response line ("200 OK")
107
+ headers = headers.reject { |h| h.nil? || h.empty? }.map { |h| h.split(/:/, 2).map(&:strip) }
108
+ @headers = Hash[headers]
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def self.perform_with_retry(verb, url, headers: {}, **opts)
115
+ RETRIES.times do |tries|
116
+ Kernel.sleep(RETRY_WAIT) if tries > 0
117
+ can_retry = tries + 1 < RETRIES
118
+
119
+ begin
120
+ response = perform(verb, url, headers: headers, **opts)
121
+ final_response = response.success? || (response.failure? && response.status < 500)
122
+
123
+ return response if !can_retry || final_response
124
+ rescue Curl::Err::CurlError => ex
125
+ raise unless can_retry
126
+ end
127
+ end
128
+ end
129
+
130
+ def self.to_post_data(data)
131
+ if data.kind_of?(Hash)
132
+ data.map { |k, v| Curl::PostField.content(k, v) }
133
+ else
134
+ data
135
+ end
136
+ end
137
+
138
+ def self.perform(verb, url, **opts)
139
+ with_client do |client|
140
+ client.url = url
141
+
142
+ opts.each do |attr, value|
143
+ client.send("#{attr}=", value)
144
+ end
145
+
146
+ begin
147
+ if [:put, :post].include?(verb)
148
+ client.send("http_#{verb}", opts[:post_body])
149
+ else
150
+ client.send("http_#{verb}")
151
+ end
152
+ response = Response.new(client.response_code, client.header_str, client.body_str)
153
+ rescue Curl::Err::CurlError
154
+ # Reset the connection to prevent sending requests to the same broken handler
155
+ client.close
156
+ raise
157
+ end
158
+ end
159
+ end
160
+
161
+ def self._read_timeout
162
+ read_timeout || READ_TIMEOUT
163
+ end
164
+
165
+ def self._write_timeout
166
+ write_timeout || WRITE_TIMEOUT
167
+ end
168
+
169
+ def self.connection_pool_opts
170
+ {
171
+ size: pool_size || POOL_SIZE,
172
+ timeout: pool_timeout || POOL_TIMEOUT
173
+ }
174
+ end
175
+ end
@@ -0,0 +1,4 @@
1
+ class PooledCurb
2
+ VERSION = "1.0.0"
3
+ end
4
+
@@ -0,0 +1,28 @@
1
+ require "./lib/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "pooled_curb"
5
+ s.version = PooledCurb::VERSION
6
+ s.date = "2014-10-24"
7
+ s.summary = "Pooled curb"
8
+ s.description = "Simple wrapper around curb to provide a pool of reusable connections"
9
+ s.authors = ['Arseniy Ivanov', 'Chris Micacchi', 'Vova Drizhepolov']
10
+ s.email = ["arseniy@500px.com", "chris@500px.com", "vova@500px.com"]
11
+
12
+ s.files = `git ls-files`.split("\n")
13
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
+ s.require_paths = ["lib"]
16
+
17
+ s.homepage = "https://github.com/500px/pooled-curb"
18
+ s.license = "MIT"
19
+
20
+ s.add_dependency "activesupport", "~> 4.1"
21
+ s.add_dependency "yajl-ruby", "~> 1.2"
22
+ s.add_dependency "curb", "~> 0.8"
23
+ s.add_dependency "connection_pool", "~> 2.0"
24
+
25
+ s.add_development_dependency "json", "~> 1.8"
26
+ s.add_development_dependency "rspec", "~> 2.14"
27
+ s.add_development_dependency 'rake', "~> 10.1"
28
+ end
@@ -0,0 +1,221 @@
1
+ require 'spec_helper'
2
+
3
+ describe PooledCurb do
4
+ subject { PooledCurb }
5
+ before(:each) { subject.reset! }
6
+
7
+ describe '.configure' do
8
+ let(:expected_value) { rand(1..1000) }
9
+
10
+ def do_configure(attr_name, value)
11
+ subject.configure do |client|
12
+ client.send("#{attr_name}=", value)
13
+ end
14
+ end
15
+
16
+ %w(pool_size pool_timeout read_timeout write_timeout).each do |attr_name|
17
+ it "sets #{attr_name}" do
18
+ do_configure(attr_name, expected_value)
19
+ expect(subject.send(attr_name)).to eq(expected_value)
20
+ end
21
+ end
22
+ end
23
+
24
+ describe '.connection_pool' do
25
+ it "returns an instance of ConnectionPool" do
26
+ expect(subject.connection_pool).to be_a(ConnectionPool)
27
+ end
28
+ end
29
+
30
+ describe '.get' do
31
+ let(:curl) { Curl::Easy.new }
32
+
33
+ before :each do
34
+ curl.stub(:http_get)
35
+ curl.stub(:http_post)
36
+ subject.stub(:with_client).and_yield(curl)
37
+ end
38
+
39
+ it "configures default timeout on request" do
40
+ expect(curl).to receive("timeout=").with(PooledCurb::READ_TIMEOUT)
41
+ subject.get("stub")
42
+ end
43
+
44
+ it "configures custom timeout on request" do
45
+ expected_timeout = rand(0..1000)
46
+
47
+ subject.configure { |client| client.read_timeout = expected_timeout }
48
+ expect(curl).to receive("timeout=").with(expected_timeout)
49
+
50
+ subject.get("stub")
51
+ end
52
+
53
+ it "sets the URL of the request" do
54
+ expected_url = "http://stub#{rand(0..1000)}"
55
+
56
+ expect(curl).to receive("url=").with(expected_url)
57
+ subject.get(expected_url)
58
+ end
59
+
60
+ it "makes a GET request" do
61
+ expect(curl).to receive(:http_get).once
62
+ subject.get("stub")
63
+ end
64
+
65
+ context "returning response" do
66
+ let(:response_json) { {"value" => "stub-response-#{rand(0..100_000)}"} }
67
+ let(:stub_response) { response_json.to_json }
68
+ let(:header_str) { "HTTP/1.1 200 OK\r\nContent-Length: 19\r\nContent-Type: text/plain; charset=utf-8\r\nDate: Mon, 02 Jun 2014 14:04:16 GMT\r\n\r\n" }
69
+
70
+ before :each do
71
+ curl.stub(:body_str).and_return(stub_response)
72
+ curl.stub(:header_str).and_return(header_str)
73
+
74
+ curl.stub(:response_code).and_return(200)
75
+ end
76
+
77
+ it "returns a Response object" do
78
+ expect(subject.get("stub")).to be_a(PooledCurb::Response)
79
+ end
80
+
81
+ it "sets body_str on Response object" do
82
+ expect(subject.get("stub").body).to be(stub_response)
83
+ end
84
+
85
+ it "sets status on Response object" do
86
+ expect(subject.get("stub").status).to be(200)
87
+ end
88
+
89
+ it "sets header_str on Response object" do
90
+ expect(subject.get("stub").header_str).to be(header_str)
91
+ end
92
+
93
+ context "with the headers" do
94
+ it "parses into a hash" do
95
+ expect(subject.get("stub").headers).to be_a(Hash)
96
+ end
97
+
98
+ it "parses all the headers" do
99
+ expect(subject.get("stub").headers.size).to eq(3)
100
+ end
101
+
102
+ it "splits on the first colon only" do
103
+ expect(subject.get("stub").headers["Content-Length"]).to eq("19")
104
+ expect(subject.get("stub").headers["Content-Type"]).to eq("text/plain; charset=utf-8")
105
+ expect(subject.get("stub").headers["Date"]).to eq("Mon, 02 Jun 2014 14:04:16 GMT")
106
+ end
107
+ end
108
+
109
+ it "sets status on Response object" do
110
+ expect(subject.get("stub").status).to be(200)
111
+ end
112
+ end
113
+
114
+ context "request takes too long" do
115
+ let(:expected_error) { Curl::Err::TimeoutError.new }
116
+
117
+ before :each do
118
+ curl.stub(:http_get).and_raise(expected_error)
119
+ end
120
+
121
+ it "raises the exception" do
122
+ expect {
123
+ subject.get("stub")
124
+ }.to raise_error(expected_error)
125
+ end
126
+ end
127
+ end
128
+
129
+
130
+ describe '.post' do
131
+ let(:curl) { Curl::Easy.new }
132
+
133
+ before :each do
134
+ curl.stub(:http_post)
135
+ subject.stub(:with_client).and_yield(curl)
136
+ end
137
+
138
+ it "configures default timeout on request" do
139
+ expect(curl).to receive("timeout=").with(PooledCurb::WRITE_TIMEOUT)
140
+ subject.post("stub", {})
141
+ end
142
+
143
+ it "configures custom timeout on request" do
144
+ expected_timeout = rand(0..1000)
145
+
146
+ subject.configure { |client| client.write_timeout = expected_timeout }
147
+ expect(curl).to receive("timeout=").with(expected_timeout)
148
+
149
+ subject.post("stub", {})
150
+ end
151
+
152
+ it "sets the URL of the request" do
153
+ expected_url = "http://stub#{rand(0..1000)}"
154
+
155
+ expect(curl).to receive("url=").with(expected_url)
156
+ subject.post(expected_url, {})
157
+ end
158
+
159
+ it "sets the POST body" do
160
+ expect(curl).to receive(:http_post).once do |args|
161
+ expect(args.first.name).to eq(:x)
162
+ expect(args.first.content).to eq(27)
163
+ end
164
+ subject.post("stub", {x: 27})
165
+ end
166
+
167
+ it "makes a POST request" do
168
+ expect(curl).to receive(:http_post).once
169
+ subject.post("stub", {})
170
+ end
171
+
172
+ context "returning response" do
173
+ let(:response_json) { {"value" => "stub-response-#{rand(0..100_000)}"} }
174
+ let(:stub_response) { response_json.to_json }
175
+ let(:header_str) { "HTTP/1.1 200 OK\r\nContent-Length: 19\r\nContent-Type: text/plain; charset=utf-8\r\nDate: Mon, 02 Jun 2014 14:04:16 GMT\r\n\r\n" }
176
+
177
+ before :each do
178
+ curl.stub(:body_str).and_return(stub_response)
179
+ curl.stub(:header_str).and_return(header_str)
180
+
181
+ curl.stub(:response_code).and_return(200)
182
+ end
183
+
184
+ it "returns a Response object" do
185
+ expect(subject.post("stub", {})).to be_a(PooledCurb::Response)
186
+ end
187
+
188
+ it "sets body_str on Response object" do
189
+ expect(subject.post("stub", {}).body).to be(stub_response)
190
+ end
191
+
192
+ it "sets status on Response object" do
193
+ expect(subject.post("stub", {}).status).to be(200)
194
+ end
195
+
196
+ it "sets header_str on Response object" do
197
+ expect(subject.post("stub", {}).header_str).to be(header_str)
198
+ end
199
+
200
+ context "with the headers" do
201
+ it "parses into a hash" do
202
+ expect(subject.post("stub", {}).headers).to be_a(Hash)
203
+ end
204
+
205
+ it "parses all the headers" do
206
+ expect(subject.post("stub", {}).headers.size).to eq(3)
207
+ end
208
+
209
+ it "splits on the first colon only" do
210
+ expect(subject.post("stub", {}).headers["Content-Length"]).to eq("19")
211
+ expect(subject.post("stub", {}).headers["Content-Type"]).to eq("text/plain; charset=utf-8")
212
+ expect(subject.post("stub", {}).headers["Date"]).to eq("Mon, 02 Jun 2014 14:04:16 GMT")
213
+ end
214
+ end
215
+
216
+ it "sets status on Response object" do
217
+ expect(subject.post("stub", {}).status).to be(200)
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,9 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'json'
5
+ require 'pooled_curb'
6
+
7
+ RSpec.configure do |config|
8
+ # some (optional) config here
9
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pooled_curb
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Arseniy Ivanov
8
+ - Chris Micacchi
9
+ - Vova Drizhepolov
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2014-10-24 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '4.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '4.1'
29
+ - !ruby/object:Gem::Dependency
30
+ name: yajl-ruby
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.2'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.2'
43
+ - !ruby/object:Gem::Dependency
44
+ name: curb
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.8'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '0.8'
57
+ - !ruby/object:Gem::Dependency
58
+ name: connection_pool
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '2.0'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '2.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: json
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '1.8'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '1.8'
85
+ - !ruby/object:Gem::Dependency
86
+ name: rspec
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '2.14'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '2.14'
99
+ - !ruby/object:Gem::Dependency
100
+ name: rake
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '10.1'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '10.1'
113
+ description: Simple wrapper around curb to provide a pool of reusable connections
114
+ email:
115
+ - arseniy@500px.com
116
+ - chris@500px.com
117
+ - vova@500px.com
118
+ executables: []
119
+ extensions: []
120
+ extra_rdoc_files: []
121
+ files:
122
+ - ".gitignore"
123
+ - ".rspec"
124
+ - ".ruby-gemset"
125
+ - ".ruby-version"
126
+ - Gemfile
127
+ - Gemfile.lock
128
+ - LICENSE
129
+ - README.md
130
+ - Rakefile
131
+ - lib/pooled_curb.rb
132
+ - lib/version.rb
133
+ - pooled_curb.gemspec
134
+ - spec/pooled_curb_spec.rb
135
+ - spec/spec_helper.rb
136
+ homepage: https://github.com/500px/pooled-curb
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.2.2
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: Pooled curb
160
+ test_files:
161
+ - spec/pooled_curb_spec.rb
162
+ - spec/spec_helper.rb