#!/usr/bin/env ruby
# password_gen.rb — usage: ruby password_gen.rb [length] [--no-symbols]
module CharSets
LOWER = ('a'..'z').to_a
UPPER = ('A'..'Z').to_a
DIGITS = ('0'..'9').to_a
SYMBOLS = '!@#$%^&*()-_=+[]{}|;:,.<>?'.chars
ALL = LOWER + UPPER + DIGITS + SYMBOLS
SAFE = LOWER + UPPER + DIGITS
end
class PasswordGenerator
DEFAULT_LENGTH = 16
def initialize(length: DEFAULT_LENGTH, symbols: true)
@length = length
@symbols = symbols
end
def generate
charset = @symbols ? CharSets::ALL : CharSets::SAFE
# Ensure at least one character from each required set
required = [
CharSets::LOWER.sample,
CharSets::UPPER.sample,
CharSets::DIGITS.sample,
]
required << CharSets::SYMBOLS.sample if @symbols
# Fill the rest randomly
rest = (@length - required.length).times.map { charset.sample }
# Shuffle so the required chars aren't always at the front
(required + rest).shuffle.join
end
def strength(password)
score = 0
score += 1 if password.match?(/[a-z]/)
score += 1 if password.match?(/[A-Z]/)
score += 1 if password.match?(/d/)
score += 1 if password.match?(/[^a-zA-Zd]/)
score += 1 if password.length >= 16
case score
when 5 then "Strong 💪"
when 3..4 then "Moderate 🔶"
else "Weak ⚠️"
end
end
end
# Parse arguments
length = ARGV[0]&.to_i || 16
symbols = !ARGV.include?("--no-symbols")
gen = PasswordGenerator.new(length: length, symbols: symbols)
pw = gen.generate
puts "Generated password:"
puts pw
puts "Strength: #{gen.strength(pw)}"
puts "Length: #{pw.length}"