RbRuby · Lesson 10 of 11

Mini Project: CLI Password Generator

Let's build a command-line password generator. It'll use modules, classes, string manipulation, and argument parsing — a real taste of Ruby in action.

Ruby
#!/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}"
Bash
ruby password_gen.rb           # 16 chars with symbols
ruby password_gen.rb 24        # 24 chars with symbols
ruby password_gen.rb 20 --no-symbols  # 20 chars, alphanumeric only
◆ Note
Next steps: add a `--count 5` flag to generate multiple passwords, use `optparse` from the stdlib for proper argument parsing, or add a clipboard copy feature with the `clipboard` gem.