A CLI in both
Ruby with OptionParser
The tool greets a name a number of times. Both versions take the same two flags, give the same defaults, and print one line per greeting.
require "optparse"
options = { count: 1, name: "guest" }
OptionParser.new do |parser|
parser.on("-c", "--count N", Integer, "How many greetings") { |value| options[:count] = value }
parser.on("-n", "--name NAME", "Who to greet") { |value| options[:name] = value }
end.parse!
options[:count].times { puts "Hello, #{options[:name]}" }
Python with argparse
import argparse parser = argparse.ArgumentParser() parser.add_argument("-c", "--count", type=int, default=1) parser.add_argument("-n", "--name", default="guest") args = parser.parse_args() for _ in range(args.count): print(f"Hello, {args.name}")
The same run in both
ruby greet.rb -c 3 -n Ada
Hello, Ada
Hello, Ada
Hello, Ada
python3 greet.py -c 3 -n Ada
Hello, Ada
Hello, Ada
Hello, Ada
Without flags both fall back to one greeting for a guest:
ruby greet.rb
Hello, guest
python3 greet.py
Hello, guest
An unknown flag shows the second difference, the exit code:
ruby greet.rb --bad
greet.rb:7:in '<main>': invalid option: --bad (OptionParser::InvalidOption)
python3 greet.py --bad
usage: greet.py [-h] [-c COUNT] [-n NAME]
greet.py: error: unrecognized arguments: --bad
Ruby exits 1 with a Ruby exception on stderr; Python exits 2 with a usage line and its own error message. A pipeline sees both as failures, but the 2 against 1 matters when a script branches on the status.
What differs
- The declaration. Both describe a flag once with a short and a long form. OptionParser takes the type as a positional argument (Integer); argparse takes it as a keyword (type=int).
- Where the values land. OptionParser calls back into a hash the program already owns. argparse builds an object with one attribute per flag, so args.count appears without a declaration.
- Help. Both print usage and flag descriptions for -h/--help; the text and the layout differ, and neither is a contract a script should parse.
- Exit codes. A bad flag is a Ruby exception (1) and a Python argument error (2), as above.
For a larger tool both languages have subcommand libraries (Ruby has optparse plus a dispatch hash, Python has argparse subparsers). The shape of the code is the same either way: parse once, validate, then call one method.
Next: Minitest against pytest
Article author: Arthur Isaev