There's a famous scene in Jurassic Park, where Lex sits down at a computer she's never used before, recognises it's UNIX, and quickly figures out how to use the system to lock the door.
It's a great scene, but is it realistic? Can we make interfaces so good that they're discoverable and familiar to people who've never used them before, with UNIX as the common ground?
Maybe we can, if we follow the conventions and obey the rules.
Conventions are about what the user expects, but are not enforced by the technology we use. For example, most websites have a logo in the top left corner which links to the homepage.
Rules govern how software interacts with other software. By playing well with other software we can lean on a well known common interface (like a web browser, or a shell) and provide a more familiar experience.
The Greeter
library provides one class, with one method. We
are going to write a command line interface for it.
irb > Greeter.greeting "George" => "Hello George" irb > Greeter.greeting "!!!" Greeter::BadNameError: "!!!" is not a valid name
We begin with a simple implementation: 4e1f5c8
$ greet George Hello George
This implementation handles errors badly. If we give bad input, we see a Ruby stack trace as output.
$ greet ... /code/greeter/lib/greeter.rb:18:in `greeting': "..." is not a valid name (Greeter::BadNameError) from /code/greeter/lib/greeter.rb:7:in `greeting' from /code/greeter/lib/greeter/cli.rb:10:in `run' from /code/greeter/bin/greet:5:in `<main>'
We improve the implementation by adding error handling: 7c779c1
$ greet ... greet: Error: "..." is not a valid name
Running the command with no input produces an error, which goes against the UNIX convention of providing a usage message.
$ greet greet: Error: nil is not a valid name
We improve the implementation by adding a usage message: 9813de9
$ greet usage: greet name
The command does not play well with other commands, for example sending the output to a file is fine for good input but gives unexpected results for bad input:
$ greet George > out.txt $ cat out.txt Hello George $ greet > out.txt $ cat out.txt usage: greet name $
We improve the implementation by using the correct output streams: 0a74009
$ greet > out.txt usage: greet name $ cat out.txt $
The command does not play well with conditional execution.
$ greet George && echo Welcome Hello George Welcome $ greet && echo Welcome usage: greet name Welcome $
We improve the implementation by using non-zero exit statuses: c0b2ec2
$ greet && echo Welcome usage: greet name $ echo $? 64
The command is not documentated.
We improve the implementation by adding a manual page: 3fe6108
Distributing man pages with Rubygems is difficult, because gems are intended to be for libraries not applications.