Random Port Generator — Ruby source
Generate one or many random TCP/UDP port numbers across registered, ephemeral, or the full range - optionally unique. Runs entirely in your browser with crypto-grade randomness.
This is the Ruby implementation — the same logic the interactive tool runs, in a shareable, citable form.
# random-port-generator — Ruby port: random/dynamic port picker.
#
# Display port of the CosmoDev Random Port Generator tool — same contract as
# cli/random-port-generator/random-port-generator.go (the live Go twin) and
# src/lib/random-port.ts (the canonical TypeScript implementation). Pure +
# deterministic via an injectable rng (the TS test surface); invalid custom
# ranges raise ArgumentError, mirroring the TS throw. The default rng is
# SecureRandom.random_number — a CSPRNG standing in for the TS
# crypto.getRandomValues default.
require 'securerandom'
module RandomPort
RANGES = {
'any' => [1, 65_535],
'registered' => [1024, 49_151], # the TS default (registered ports)
'ephemeral' => [49_152, 65_535] # dynamic/client ports
}.freeze
DEFAULT_RNG = -> { SecureRandom.random_number } # CSPRNG float in [0, 1)
module_function
# resolveRange() in the TS lib.
def resolve_range(range: 'registered', min: nil, max: nil)
return [(min || 1), (max || 65_535)] if range == 'custom'
RANGES.fetch(range)
end
# assertRange() in the TS lib — the TS Error becomes an exception.
def assert_range(lo, hi)
unless lo.is_a?(Integer) && hi.is_a?(Integer) && lo >= 0 && lo <= 65_535 && hi <= 65_535 && lo <= hi
raise ArgumentError, "Invalid port range #{lo}-#{hi}"
end
end
# randomPort() in the TS lib — Port() in the Go twin.
def random_port(range: 'registered', min: nil, max: nil, rng: DEFAULT_RNG)
lo, hi = resolve_range(range: range, min: min, max: max)
assert_range(lo, hi)
lo + (rng.call * (hi - lo + 1)).floor
end
# randomPorts() in the TS lib — Ports() in the Go twin.
def random_ports(count: 1, unique: false, range: 'registered', min: nil, max: nil, rng: DEFAULT_RNG)
count = [1, count].max
lo, hi = resolve_range(range: range, min: min, max: max)
assert_range(lo, hi)
unless unique
return Array.new(count) { random_port(range: range, min: min, max: max, rng: rng) }
end
# Fisher-Yates partial shuffle over the range to pick n unique ports.
capacity = hi - lo + 1
n = [count, capacity].min
pool = (lo..hi).to_a
n.times do |i|
j = i + (rng.call * (capacity - i)).floor
pool[i], pool[j] = pool[j], pool[i]
end
pool.first(n)
end
end
# Vectors from src/lib/random-port.test.ts — a fixed rng pins every draw.
zero = -> { 0.0 }
half = -> { 0.5 }
puts "registered+0 = #{RandomPort.random_port(rng: zero)}" # 1024
puts "any+0 = #{RandomPort.random_port(range: 'any', rng: zero)}" # 1
puts "ephemeral+0 = #{RandomPort.random_port(range: 'ephemeral', rng: zero)}" # 49152
puts "custom 8000-8000 @0.9 = #{RandomPort.random_port(range: 'custom', min: 8000, max: 8000, rng: -> { 0.9 })}" # 8000
puts "resolveRange(custom defaults) = #{RandomPort.resolve_range(range: 'custom').join('..')}" # 1..65535
puts "count=5 @0 = #{RandomPort.random_ports(count: 5, rng: zero).inspect}" # [1024 x5]
puts "unique 3 in 1..10 @0.5 = #{RandomPort.random_ports(count: 3, unique: true, range: 'custom', min: 1, max: 10, rng: half).inspect}" # [6, 1, 7]
puts "unique 5 in 1..3 @0.5 = #{RandomPort.random_ports(count: 5, unique: true, range: 'custom', min: 1, max: 3, rng: half).inspect}" # capped at capacity 3
p = RandomPort.random_port # default CSPRNG, registered range
puts "default rng registered = #{p} (in 1024..49151)"
[
[{ range: 'custom', min: 70_000 }, 'min above 65535'],
[{ range: 'custom', min: 100, max: 50 }, 'min above max']
].each do |opts, why|
RandomPort.random_port(**opts)
rescue ArgumentError => e
puts "invalid: #{e.message} (#{why})"
end
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →