hot_tub 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
6
+ nbproject/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in http_hot_tub.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ platform :ruby do
8
+ gem 'eventmachine'
9
+ gem 'em-http-request', '~> 1.0', :require => 'em-http'
10
+ gem 'em-synchrony', '~> 1.0', :require => ['em-synchrony', 'em-synchrony/em-http']
11
+ gem "excon"
12
+ end
13
+ platform :jruby do
14
+ gem 'jruby-openssl'
15
+ gem 'jruby-httpclient'
16
+ end
17
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Joshua T. Mckinney
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # HotTub
2
+ A simple thread-safe pooling gem to use with your preferred http library that support
3
+ keep-alive.
4
+
5
+ ## Installation
6
+
7
+ HotTub is available through [Rubygems](https://rubygems.org/gems/hot_tub) and can be installed via:
8
+
9
+ $ gem install hot_tub
10
+
11
+ ## Setup
12
+ class MyClass
13
+ @@pool = HotTub::Session.new({:size => 2 :client => HotTub::Client::EmSynchronyClient.new('https://google.com'), :never_block => true})
14
+
15
+ def self.fetch_results(query)
16
+ @@pool.get(:query => query) # keepalive has be defaulted to true in the client
17
+ end
18
+ end
19
+
20
+ MyClass.fetch_results({:foo => "goo"})
21
+
22
+ ## Contributing to HotTub
23
+
24
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
25
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
26
+ * Fork the project
27
+ * Start a feature/bugfix branch
28
+ * Commit and push until you are happy with your contribution
29
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
30
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "hot_tub/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "hot_tub"
7
+ s.version = HotTub::VERSION
8
+ s.authors = ["Joshua Mckinney"]
9
+ s.email = ["joshmckin@gmail.com"]
10
+ s.homepage = "https://github.com/JoshMcKin/hot_tub"
11
+ s.summary = %q{A very simple ruby pool gem}
12
+ s.description = %q{A very simple ruby pool gem}
13
+
14
+ s.rubyforge_project = "hot_tub"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.add_development_dependency "rspec"
21
+ end
@@ -0,0 +1,41 @@
1
+ require "hot_tub"
2
+ require 'thread'
3
+ module HotTub
4
+ # Super class for all HotTub clients
5
+ # provides the 4 required methods to ensure compatibility
6
+ class Client
7
+
8
+ def client
9
+ @client
10
+ end
11
+
12
+ def method_missing(method, *args, &blk)
13
+ @client.send(method,*args,&blk)
14
+ end
15
+
16
+ def temporary?
17
+ @temporary == true
18
+ end
19
+
20
+ def mark_temporary
21
+ @temporary = true
22
+ end
23
+
24
+ # Override this method to perform the necessary action for ensure a client
25
+ # is clean for use.
26
+ def sanitize_hot_tub_client
27
+ @client
28
+ end
29
+
30
+ def close_hot_tub_client
31
+ @client
32
+ end
33
+
34
+ class << self
35
+ def mutex
36
+ Mutex.new
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,45 @@
1
+ require "hot_tub"
2
+ require "hot_tub/clients/client"
3
+ module HotTub
4
+ class EmSynchronyClient < HotTub::Client
5
+
6
+ def initialize(url,options={})
7
+ @url = url
8
+ @options = {:inactivity_timeout => 0}.merge(options)
9
+ @client = EM::HttpRequest.new(url,options)
10
+ end
11
+
12
+ def sanitize_hot_tub_client
13
+ if @client.conn && @client.conn.error?
14
+ HotTub.logger.info "Sanitizing connection : #{EventMachine::report_connection_error_status(@client.conn.instance_variable_get(:@signature))}"
15
+ @client.conn.close_connection
16
+ @client.instance_variable_set(:@deferred, true)
17
+ end
18
+ @client
19
+ end
20
+
21
+ def close_hot_tub_client
22
+ @client.conn.close_connection if @client.conn
23
+ end
24
+
25
+ # Default keepalive true for HTTP requests
26
+ [:get,:head,:delete,:put,:post].each do |m|
27
+ define_method m do |options={},&blk|
28
+ options ={} if options.nil?
29
+ options[:keepalive] = true
30
+ @client.send(m,options,&blk)
31
+ end
32
+ end
33
+
34
+ def dup
35
+ self.class.new(@url,@options)
36
+ end
37
+
38
+ class << self
39
+ # Use a fiber safe mutex
40
+ def mutex
41
+ EM::Synchrony::Thread::Mutex.new
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,27 @@
1
+ require "hot_tub"
2
+ require "hot_tub/clients/client"
3
+ require "excon"
4
+ module HotTub
5
+ class ExconClient < HotTub::Client
6
+
7
+ def initialize(url,options={})
8
+ @url = url
9
+ #make sure we turn on keep-alive
10
+ @options = {:headers => {"Connection" => "keep-alive"}}.merge(options)
11
+ @client = Excon.new(url,options)
12
+ end
13
+
14
+ # pretty sure Excon handles this internally
15
+ def sanitize_hot_tub_client
16
+ @client
17
+ end
18
+
19
+ def close_hot_tub_client
20
+ @client.socket.close
21
+ end
22
+
23
+ def dup
24
+ self.class.new(@url,@options)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ require "hot_tub"
2
+ require "hot_tub/clients/client"
3
+ require 'uri'
4
+ require 'http_client'
5
+ module HotTub
6
+
7
+ class HttpClientClient < HotTub::Client
8
+
9
+ def initialize(options={})
10
+ options[:default_host] = options[:url] if (options[:url] && options[:default_host].nil?)
11
+ @options = options
12
+ @client = HTTP::Client.new(options)
13
+ end
14
+
15
+ # pretty sure HttpClient handles this internally
16
+ def sanitize_hot_tub_client
17
+ @client
18
+ end
19
+
20
+ def close_hot_tub_client
21
+ @client.shutdown
22
+ end
23
+
24
+ def dup
25
+ self.class.new(@options)
26
+ end
27
+ end
28
+ end
29
+
30
+ module HTTP
31
+ class Client
32
+ def read_response(request)
33
+ r = execute(request)
34
+ r.body
35
+ r
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,127 @@
1
+ module HotTub
2
+ class Session
3
+ AVAILABLE_CLIENTS = ["NetHttpClient","EmSynchronyClient"]
4
+
5
+ # OPTIONS
6
+ # * :size - number of connections for each pool
7
+ # * :inactivity_timeout - number of seconds to wait before disconnecting,
8
+ # setting to 0 means the connection will not be closed
9
+ # * :pool_timeout - the amount of seconds to block waiting for an availble connection,
10
+ # because this is blocking it should be an extremely short amount of
11
+ # time default to 0.5 seconds, if you need more consider enlarging your pool
12
+ # instead of raising this number
13
+ # :never_block - if set to true, a connection will always be returned, but
14
+ # these extra connections are not added to the pool when the request is completed
15
+ def initialize(options={})
16
+ @options = {
17
+ :size => 5,
18
+ :never_block => false,
19
+ :blocking_timeout => 0.5,
20
+ :client_options => {}
21
+ }.merge(options)
22
+ @pool = []
23
+ @pool_data = {:current_size => 0}
24
+ @client = @options[:client]
25
+ @mutex = (@client.respond_to?(:mutex) ? @client.mutex : Client.mutex)
26
+ end
27
+
28
+ def pool
29
+ @mutex.synchronize do
30
+ add if add?
31
+ @pool
32
+ end
33
+ end
34
+
35
+ # Fetches an avaible client from the pool.
36
+ # Hot tubs are not always clean... Make sure we have a good connection. The client
37
+ # should respond to sanitize_hot_tub_connection, which checks to make sure
38
+ # the connection is still viable, and resets if not
39
+ def fetch
40
+ client = nil
41
+ alarm = (Time.now + @options[:blocking_timeout])
42
+ # block until we get an available connection or Timeout::Error
43
+ while client.nil?
44
+ raise_alarm if alarm <= Time.now
45
+
46
+ client = pool.shift
47
+ if client.nil? && (@options[:never_block])
48
+ HotTub.logger.info "Adding never_block client for #{@client.class.name}, will not be returned to pool."
49
+ client = new_client
50
+ client.mark_temporary
51
+ end
52
+ end
53
+ client.sanitize_hot_tub_client
54
+ client
55
+ end
56
+
57
+ # return a client to the pool
58
+ def return_client(client)
59
+ if client.temporary?
60
+ client.close_hot_tub_client # Too hot in the hot tub...
61
+ else
62
+ @pool << client
63
+ end
64
+ @pool
65
+ end
66
+
67
+ # Run the block on the retrieved connection. Good for ensure the same client
68
+ # is used for mulitple requests. For HTTP requests make sure you request has
69
+ # keep-alive properly set for your client
70
+ # EX:
71
+ # @pool = HotTub.new("https://some_web_site.com")
72
+ # results = []
73
+ # @pool.run do |client|
74
+ # results.push (client.get(:query => {:foo => "bar"}, :keepalive => true))
75
+ # results.push (client.get(:query => {:bar => "foo"}, :keepalive => true)) # reuse client
76
+ # end
77
+ #
78
+ def run(&block)
79
+ client = fetch
80
+ if block_given?
81
+ block.call(client)
82
+ else
83
+ raise ArgumentError, 'Run requires a block.'
84
+ end
85
+ ensure
86
+ return_client(client) if client
87
+ end
88
+
89
+
90
+ # Let pool instance respond to client methods. For HTTP request make sure you
91
+ # requests has keep-alive properly set for your client
92
+ # EX:
93
+ # @pool = HotTub.new("https://some_web_site.com")
94
+ # r1 = @pool.get(:query => {:foo => "bar"}, :keepalive => true)
95
+ # r2 = @pool.get(:query => {:bar => "foo"}, :keepalive => true) # uses a different client
96
+ #
97
+ def method_missing(method, *args, &blk)
98
+ client = fetch
99
+ client.send(method,*args,&blk)
100
+ ensure
101
+ return_client(client) if client
102
+ end
103
+
104
+ private
105
+
106
+ def add?
107
+ (@pool.empty? && (@pool_data[:current_size] < @options[:size]))
108
+ end
109
+
110
+ def add
111
+ HotTub.logger.info "Adding HotTub client: #{@client.class.name} to pool"
112
+ @pool_data[:current_size] += 1
113
+ @pool << new_client
114
+ @pool
115
+ end
116
+
117
+ def new_client
118
+ @client.dup
119
+ end
120
+
121
+ def raise_alarm
122
+ message = "Could not fetch a free client in time. Consider increasing your pool size for #{@client.class.name}."
123
+ HotTub.logger.error message
124
+ raise Timeout::Error, message
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,3 @@
1
+ module HotTub
2
+ VERSION = "0.0.1"
3
+ end
data/lib/hot_tub.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'thread'
2
+ require 'timeout'
3
+ require 'logger'
4
+ require "hot_tub/version"
5
+ require "hot_tub/session"
6
+ require "hot_tub/clients/client"
7
+ require "hot_tub/clients/em_synchrony_client"
8
+ require "hot_tub/clients/excon_client"
9
+ require "hot_tub/clients/http_client_client" if RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
10
+
11
+ module HotTub
12
+ @@logger = Logger.new(STDOUT)
13
+ def self.logger
14
+ @@logger
15
+ end
16
+
17
+ def self.logger=logger
18
+ @@logger = logger
19
+ end
20
+ end
21
+
@@ -0,0 +1,47 @@
1
+ unless RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
2
+ require "em-synchrony"
3
+ require "em-synchrony/em-http"
4
+ require "em-synchrony/fiber_iterator"
5
+ require "hot_tub/clients/em_synchrony_client"
6
+ require 'test_helper_methods'
7
+ include TestHelperMethods
8
+
9
+ describe HotTub::EmSynchronyClient do
10
+ before(:each) do
11
+ @url = "https://www.google.com"
12
+ end
13
+
14
+ it "Keep alive should work" do
15
+ EM.synchrony do
16
+ keep_alive_test HotTub::EmSynchronyClient.new(@url) do |connection|
17
+ connection.get.response_header.status
18
+ end
19
+ EM.stop
20
+ end
21
+ end
22
+ # context 'integration test with parallel requests' do
23
+ # # 10 parallel requests
24
+ # it "should work" do
25
+ # @connection_pool = HotTub::Session.new(:client_options =>
26
+ # {:url => "https://www.google.com"},
27
+ # :never_block => true,
28
+ # :client => HotTub::EmSynchronyClient)
29
+ # EM.synchrony do
30
+ # concurrency = 10
31
+ # options = (0..19).to_a
32
+ # results = []
33
+ #
34
+ # EM::Synchrony::FiberIterator.new(options, concurrency).each do |count|
35
+ # resp = @connection_pool.get
36
+ # results.push resp.response_header.status
37
+ # end
38
+ #
39
+ # results.length.should eql(20)
40
+ # results.include?(200).should be_true
41
+ # @connection_pool.instance_variable_get(:@pool).length.should eql(@connection_pool.instance_variable_get(:@options)[:size])
42
+ # EM.stop
43
+ # end
44
+ # end
45
+ # end
46
+ end
47
+ end
@@ -0,0 +1,18 @@
1
+ unless (RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE))
2
+ require "hot_tub/clients/excon_client"
3
+ require 'test_helper_methods'
4
+ require 'excon'
5
+ include TestHelperMethods
6
+ describe HotTub::ExconClient do
7
+ before(:each) do
8
+ @url = "https://www.google.com"
9
+ end
10
+
11
+ it "Keep alive should work" do
12
+ keep_alive_test HotTub::ExconClient.new(@url) do |client|
13
+ client.get.status
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,16 @@
1
+ if RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
2
+ require "hot_tub/clients/http_client_client"
3
+ require 'test_helper_methods'
4
+ include TestHelperMethods
5
+ describe HotTub::HttpClientClient do
6
+ before(:each) do
7
+ @url = "https://www.google.com"
8
+ end
9
+ it "Keep alive should work" do
10
+ keep_alive_test HotTub::HttpClientClient.new(:url => @url) do |client|
11
+ c = client.get('')
12
+ c.status_code
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+ class MocClient < HotTub::Client
3
+ def initialize(url,options={})
4
+ end
5
+
6
+ def get
7
+ sleep(0.05)
8
+ end
9
+ end
10
+
11
+ describe HotTub::Session do
12
+ before(:each) do
13
+ @url = "http://www.testurl123.com/"
14
+ @tub = HotTub::Session.new(:client => MocClient.new(@url))
15
+ end
16
+
17
+ context 'default configuration' do
18
+ it "should have @pool_size of 5" do
19
+ @tub.instance_variable_get(:@options)[:size].should eql(5)
20
+ end
21
+
22
+ it "should have @pool_timeout of 0" do
23
+ @tub.instance_variable_get(:@options)[:blocking_timeout].should eql(0.5)
24
+ end
25
+ end
26
+
27
+ describe '#add_connection?' do
28
+ it "should be true if @pool_data[:length] is less than desired pool size and
29
+ the pool is empty?"do
30
+ @tub.instance_variable_set(:@pool_data,{:current_size => 1})
31
+ @tub.send(:add?).should be_true
32
+ end
33
+
34
+ it "should be false pool has reached pool_size" do
35
+ @tub.instance_variable_set(:@pool_data,{:current_size => 5})
36
+ @tub.instance_variable_set(:@pool,
37
+ ["connection","connection","connection","connection","connection"])
38
+ @tub.send(:add?).should be_false
39
+ end
40
+ end
41
+
42
+ describe '#add_connection' do
43
+ it "should add connections for supplied url"do
44
+ @tub.send(:add)
45
+ @tub.instance_variable_get(:@pool).should_not be_nil
46
+ end
47
+ end
48
+
49
+ describe '#fetch_connection' do
50
+ it "should raise Timeout::Error if an available is not found in time"do
51
+ @tub.stub(:pool).and_return([])
52
+ lambda { @tub.fetch}.should raise_error(Timeout::Error)
53
+ end
54
+
55
+ it "should not raise Timeout::Error if an available is not found in time"do
56
+ @tub.instance_variable_get(:@options)[:never_block] = true
57
+ @tub.stub(:pool).and_return([])
58
+ lambda { @tub.fetch}.should_not raise_error(Timeout::Error)
59
+ end
60
+
61
+ it "should return an instance of the driver" do
62
+ @tub.fetch.should be_instance_of(MocClient)
63
+ end
64
+ end
65
+
66
+ describe '#run' do
67
+ it "should fetch a connection and run the supplied block" do
68
+ @fetched = nil
69
+
70
+ @tub.run do |connection|
71
+ @fetched = connection.class.name
72
+ end
73
+ @fetched.should eql("MocClient")
74
+ end
75
+
76
+ it "should return the connection after use" do
77
+ @tub.run do |connection|
78
+ @connection = connection
79
+ @fetched = @tub.instance_variable_get(:@pool).include?(connection)
80
+ end
81
+ @fetched.should be_false # not in pool because its doing work
82
+
83
+ # returned to pool after work was done
84
+ @tub.instance_variable_get(:@pool).include?(@connection).should be_true
85
+ end
86
+ end
87
+
88
+ context 'thread safety' do
89
+ it "should work" do
90
+ threads = []
91
+ 20.times.each do
92
+ threads << Thread.new do
93
+ @tub.run{|connection| connection.get}
94
+ end
95
+ end
96
+
97
+ sleep(0.5)
98
+
99
+ threads.each do |t|
100
+ t.join
101
+ end
102
+
103
+ @tub.instance_variable_get(:@pool).length.should eql(5)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,12 @@
1
+ require 'hot_tub'
2
+ require 'rspec'
3
+ require 'bundler/setup'
4
+ require 'logger'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ #Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+ HotTub.logger.level = Logger::ERROR
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,34 @@
1
+ module TestHelperMethods
2
+ def keep_alive_test(client,&get_status_blk)
3
+ responses = []
4
+ start = Time.now
5
+
6
+ 50.times.each do
7
+ c = client.dup # want a new client everytime
8
+ responses.push get_status_blk.call(c)
9
+ end
10
+
11
+ normal = Time.now - start
12
+
13
+ # we want a whole new pool to make sure we don't cheat
14
+ connection_pool = HotTub::Session.new(
15
+ :client => client)
16
+
17
+ start = Time.now
18
+ 50.times.each do
19
+ #responses.push get_status_blk.call(connection_pool.get)
20
+ connection_pool.run do |c|
21
+ responses.push get_status_blk.call(c)
22
+ end
23
+
24
+ end
25
+
26
+ keep_alive = Time.now - start
27
+
28
+
29
+ puts "#{client.class.name} keep-alive: #{keep_alive}; normal: #{normal};"
30
+ #puts responses.join(", ")
31
+ (keep_alive < normal).should be_true
32
+ end
33
+
34
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hot_tub
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Joshua Mckinney
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-16 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ version_requirements: &2056 !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ none: false
22
+ requirement: *2056
23
+ prerelease: false
24
+ type: :development
25
+ description: A very simple ruby pool gem
26
+ email:
27
+ - joshmckin@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - .rspec
34
+ - Gemfile
35
+ - LICENSE.txt
36
+ - README.md
37
+ - Rakefile
38
+ - http_hot_tub.gemspec
39
+ - lib/hot_tub.rb
40
+ - lib/hot_tub/clients/client.rb
41
+ - lib/hot_tub/clients/em_synchrony_client.rb
42
+ - lib/hot_tub/clients/excon_client.rb
43
+ - lib/hot_tub/clients/http_client_client.rb
44
+ - lib/hot_tub/session.rb
45
+ - lib/hot_tub/version.rb
46
+ - spec/em_synchrony_client_spec.rb
47
+ - spec/excon_client_spec.rb
48
+ - spec/http_client_client_spec.rb
49
+ - spec/session_spec.rb
50
+ - spec/spec_helper.rb
51
+ - spec/test_helper_methods.rb
52
+ homepage: https://github.com/JoshMcKin/hot_tub
53
+ licenses: []
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ none: false
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ none: false
70
+ requirements: []
71
+ rubyforge_project: hot_tub
72
+ rubygems_version: 1.8.15
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: A very simple ruby pool gem
76
+ test_files:
77
+ - spec/em_synchrony_client_spec.rb
78
+ - spec/excon_client_spec.rb
79
+ - spec/http_client_client_spec.rb
80
+ - spec/session_spec.rb
81
+ - spec/spec_helper.rb
82
+ - spec/test_helper_methods.rb
83
+ ...