diff --git a/.rubocop.yml b/.rubocop.yml
index 0152dd8..2dfede8 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -2,6 +2,8 @@ AllCops:
DisplayCopNames: true
TargetRubyVersion: 2.3.3
+Style/ClassAndModuleChildren:
+ Enabled: false
Naming/FileName:
Enabled: false
Metrics/MethodLength:
diff --git a/lib/random-port.rb b/lib/random-port.rb
index 1684615..28551b0 100644
--- a/lib/random-port.rb
+++ b/lib/random-port.rb
@@ -22,4 +22,12 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
+# The module for all classes.
+#
+# Author:: Yegor Bugayenko (yegor256@gmail.com)
+# Copyright:: Copyright (c) 2018 Yegor Bugayenko
+# License:: MIT
+module RandomPort
+end
+
require 'random-port/pool'
diff --git a/lib/random-port/pool.rb b/lib/random-port/pool.rb
index 023a3f4..4d4d283 100644
--- a/lib/random-port/pool.rb
+++ b/lib/random-port/pool.rb
@@ -24,121 +24,120 @@
require 'socket'
require 'monitor'
+require_relative '../random-port'
-module RandomPort
- # Pool of TPC ports.
- #
- # Use it like this:
- #
- # RandomPort::Pool.new.acquire do |port|
- # # Use the TCP port. It will be returned back
- # # to the pool afterwards.
- # end
- #
- # You can specify the maximum amount of ports to acquire, using +limit+.
- # If more acquiring requests will arrive, an exception will be raised.
- #
- # The class is thread-safe, by default. You can configure it to be
- # not-thread-safe, using optional sync argument of the constructor.
- #
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
- # Copyright:: Copyright (c) 2018 Yegor Bugayenko
- # License:: MIT
- class Pool
- # If can't acquire by time out.
- class Timeout < StandardError; end
+# Pool of TPC ports.
+#
+# Use it like this:
+#
+# RandomPort::Pool.new.acquire do |port|
+# # Use the TCP port. It will be returned back
+# # to the pool afterwards.
+# end
+#
+# You can specify the maximum amount of ports to acquire, using +limit+.
+# If more acquiring requests will arrive, an exception will be raised.
+#
+# The class is thread-safe, by default. You can configure it to be
+# not-thread-safe, using optional sync argument of the constructor.
+#
+# Author:: Yegor Bugayenko (yegor256@gmail.com)
+# Copyright:: Copyright (c) 2018 Yegor Bugayenko
+# License:: MIT
+class RandomPort::Pool
+ # If can't acquire by time out.
+ class Timeout < StandardError; end
- attr_reader :limit
+ attr_reader :limit
- # Ctor.
- def initialize(sync: false, limit: 65_536)
- @ports = []
- @sync = sync
- @monitor = Monitor.new
- @limit = limit
- end
+ # Ctor.
+ def initialize(sync: false, limit: 65_536)
+ @ports = []
+ @sync = sync
+ @monitor = Monitor.new
+ @limit = limit
+ end
- # Application wide pool of ports
- SINGLETON = Pool.new
+ # Application wide pool of ports
+ SINGLETON = RandomPort::Pool.new
- # How many ports acquired now?
- def count
- @ports.count
- end
- alias size count
+ # How many ports acquired now?
+ def count
+ @ports.count
+ end
+ alias size count
- # Is it empty?
- def empty?
- @ports.empty?
- end
+ # Is it empty?
+ def empty?
+ @ports.empty?
+ end
- # Acquire a new random TCP port.
- #
- # You can specify the number of ports to acquire. If it's more than one,
- # an array will be returned.
- #
- # You can specify the amount of seconds to wait until a new port
- # is available.
- def acquire(total = 1, timeout: 4)
- start = Time.now
- loop do
- if Time.now > start + timeout
- raise Timeout, "Can't find a place in the pool of #{@limit} ports \
+ # Acquire a new random TCP port.
+ #
+ # You can specify the number of ports to acquire. If it's more than one,
+ # an array will be returned.
+ #
+ # You can specify the amount of seconds to wait until a new port
+ # is available.
+ def acquire(total = 1, timeout: 4)
+ start = Time.now
+ loop do
+ if Time.now > start + timeout
+ raise Timeout, "Can't find a place in the pool of #{@limit} ports \
for #{total} port(s), in #{format('%.02f', Time.now - start)}s"
- end
- opts = safe do
- next if @ports.count + total > @limit
- opts = Array.new(0, total)
- begin
- (0..(total - 1)).each do |i|
- opts[i] = i.zero? ? take : take(opts[i - 1] + 1)
- end
- rescue Errno::EADDRINUSE, SocketError
- next
- end
- next if opts.any? { |p| @ports.include?(p) }
- d = total * (total - 1) / 2
- next unless opts.inject(&:+) - total * opts.min == d
- @ports += opts
- opts
- end
- next if opts.nil?
- opts = opts[0] if total == 1
- return opts unless block_given?
+ end
+ opts = safe do
+ next if @ports.count + total > @limit
+ opts = Array.new(0, total)
begin
- return yield opts
- ensure
- release(opts)
+ (0..(total - 1)).each do |i|
+ opts[i] = i.zero? ? take : take(opts[i - 1] + 1)
+ end
+ rescue Errno::EADDRINUSE, SocketError
+ next
end
+ next if opts.any? { |p| @ports.include?(p) }
+ d = total * (total - 1) / 2
+ next unless opts.inject(&:+) - total * opts.min == d
+ @ports += opts
+ opts
+ end
+ next if opts.nil?
+ opts = opts[0] if total == 1
+ return opts unless block_given?
+ begin
+ return yield opts
+ ensure
+ release(opts)
end
end
+ end
- # Return it/them back to the pool.
- def release(port)
- safe do
- if port.is_a?(Array)
- port.each { |p| @ports.delete(p) }
- else
- @ports.delete(port)
- end
+ # Return it/them back to the pool.
+ def release(port)
+ safe do
+ if port.is_a?(Array)
+ port.each { |p| @ports.delete(p) }
+ else
+ @ports.delete(port)
end
end
+ end
- private
+ private
- def take(opt = 0)
- server = TCPServer.new('127.0.0.1', opt)
- p = server.addr[1]
- server.close
- p
- end
+ def take(opt = 0)
+ server = TCPServer.new('127.0.0.1', opt)
+ p = server.addr[1]
+ server.close
+ p
+ end
- def safe
- if @sync
- @monitor.synchronize { yield }
- else
- yield
- end
+ def safe
+ if @sync
+ @monitor.synchronize { yield }
+ else
+ yield
end
end
end
diff --git a/test/test_pool.rb b/test/test_pool.rb
index 0b88e96..c689cc9 100644
--- a/test/test_pool.rb
+++ b/test/test_pool.rb
@@ -29,82 +29,90 @@
# Author:: Yegor Bugayenko (yegor256@gmail.com)
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
# License:: MIT
-module RandomPort
- class TestAmount < Minitest::Test
- def test_acquires_and_releases
- pool = Pool.new
- port = pool.acquire
- assert(!port.nil?)
- assert(port.positive?)
- pool.release(port)
- end
+class RandomPort::TestPool < Minitest::Test
+ def test_acquires_and_releases
+ pool = RandomPort::Pool.new
+ port = pool.acquire
+ server = TCPServer.new(port)
+ server.close
+ assert(!port.nil?)
+ assert(port.positive?)
+ pool.release(port)
+ end
- def test_acquires_and_releases_three_ports
- pool = Pool.new(limit: 3)
- assert_equal(0, pool.size)
- ports = pool.acquire(3, timeout: 16)
- assert_equal(3, pool.size)
- assert_equal(3, ports.count)
- pool.release(ports)
- assert_equal(0, pool.size)
+ def test_acquires_and_releases_three_ports
+ pool = RandomPort::Pool.new(limit: 3)
+ assert_equal(0, pool.size)
+ ports = pool.acquire(3, timeout: 16)
+ ports.each do |p|
+ server = TCPServer.new(p)
+ server.close
end
+ assert_equal(3, pool.size)
+ assert_equal(3, ports.count)
+ pool.release(ports)
+ assert_equal(0, pool.size)
+ end
- def test_acquires_and_releases_three_ports_in_block
- pool = Pool.new(limit: 3)
- assert_equal(0, pool.size)
- pool.acquire(3, timeout: 16) do |ports|
- assert(ports.is_a?(Array))
- assert_equal(3, ports.count)
- assert_equal(3, pool.size)
+ def test_acquires_and_releases_three_ports_in_block
+ pool = RandomPort::Pool.new(limit: 3)
+ assert_equal(0, pool.size)
+ pool.acquire(3, timeout: 16) do |ports|
+ assert(ports.is_a?(Array))
+ assert_equal(3, ports.count)
+ assert_equal(3, pool.size)
+ ports.each do |p|
+ server = TCPServer.new(p)
+ server.close
end
- assert_equal(0, pool.size)
end
+ assert_equal(0, pool.size)
+ end
- def test_acquires_and_releases_in_block
- result = Pool.new.acquire do |port|
- assert(!port.nil?)
- assert(port.positive?)
- 123
- end
- assert_equal(123, result)
+ def test_acquires_and_releases_in_block
+ result = RandomPort::Pool.new.acquire do |port|
+ assert(!port.nil?)
+ assert(port.positive?)
+ 123
end
+ assert_equal(123, result)
+ end
- def test_acquires_and_releases_safely
- pool = Pool.new
- assert_raises do
- pool.acquire do
- raise 'Itended'
- end
+ def test_acquires_and_releases_safely
+ pool = RandomPort::Pool.new
+ assert_raises do
+ pool.acquire do
+ raise 'Itended'
end
- assert(pool.count.zero?)
end
+ assert(pool.count.zero?)
+ end
- def test_acquires_and_releases_from_singleton
- Pool::SINGLETON.acquire do |port|
- assert(!port.nil?)
- assert(port.positive?)
- end
+ def test_acquires_and_releases_from_singleton
+ RandomPort::Pool::SINGLETON.acquire do |port|
+ assert(!port.nil?)
+ assert(port.positive?)
end
+ end
- def test_acquires_unique_numbers
- total = 25
- numbers = (0..total - 1).map { Pool::SINGLETON.acquire }
- assert_equal(total, numbers.uniq.count)
- end
+ def test_acquires_unique_numbers
+ total = 25
+ numbers = (0..total - 1).map { RandomPort::Pool::SINGLETON.acquire }
+ assert_equal(total, numbers.uniq.count)
+ end
- def test_raises_when_too_many
- pool = Pool.new(limit: 1)
- pool.acquire
- assert_raises Pool::Timeout do
- pool.acquire(timeout: 0.1)
- end
+ def test_raises_when_too_many
+ pool = RandomPort::Pool.new(limit: 1)
+ pool.acquire
+ assert_raises RandomPort::Pool::Timeout do
+ pool.acquire(timeout: 0.1)
end
+ end
- def test_acquires_unique_numbers_in_no_sync_mode
- total = 25
- pool = Pool.new(sync: false)
- numbers = (0..total - 1).map { pool.acquire }
- assert_equal(total, numbers.uniq.count)
- end
+ def test_acquires_unique_numbers_in_no_sync_mode
+ total = 25
+ pool = RandomPort::Pool.new(sync: false)
+ numbers = (0..total - 1).map { pool.acquire }
+ assert_equal(total, numbers.uniq.count)
end
end