Writing Awesome CLI Tools in Ruby: Part II
This is Part II of my series on writing awesome CLI (command line interface) tools using ruby (Part I). In the first part I described how to create your project layout, add an executable binary, and get started. In this next part I will cover:
- How to structure your code to be usable as both a tool and a library
- Building your CLI frontend to your library.
The Library
Your library lives within lib/
. Break up the library into modules and classes. In this example we have a single module, wunder
.
Classes
All of your work should be done from classes within your gem. These classes will be configured and executed by the CLI, but can also be reused within other scripts and programs by requiring them.
Be sure to design your classes to be configurable, and to return values rather than simply printing the output. It also helps if any IO is configurable with defaults to stdin/stdout.
You notice that the Wunder::Wunderground
class takes a configuration object, provided by the CLI class (or potentially any other object that wants to take advantage of its features).
The command line interface
Within the library, I have a class called “cli” (lib/wunder/cli.rb
), which is my command line interface. The command line interface uses the fantastic Thor gem. An example of a thor interface might look like:
[code language=”ruby”]
class CLI < Thor
desc ‘echo ARG’, ‘Echo. Takes an arg, prints it.’
def echo(arg)
puts arg
end
end
[/code]
As you can see right away: it is as simple as extending Thor
and defining public methods. The methods are executable as sub-commands when this script is run. Arguments are simply method arguments.
Options are a bit different though…
[code language=”ruby”]
class CLI < Thor
desc ‘take_option’, ‘Option taking class’
option :opt, required: false, default: nil, aliases: [‘o’], desc: ‘An option.’
def take_option
puts "My option: #{options[:opt]}"
end
end
[/code]
The above method can be passed an option (‘opt’) either through --opt=value
or -o value
.
The CLI portion gets invoked from our ‘binary’ in bin/wunder
, which calls Wunder::CLI.start(ARGV)
to kick it off.
The methods within the CLI class should configure objects, call methods, and format/print the results.
Conclusion
This post covered how to structure and build your library and commandline interface. Hopefully this will help you create useful and effective tools!
Links/references
- Writing Awesome CLI Tools using Ruby: Part I
- Wunder-rb – the example CLI tool used in this series
- Markdown formatted version of this series (possibly better looking)