Ruby Command Line Interface Gems

Tweet

Computer Monitor

So you’ve written this amazing library, but something is missing… a command line interface! Ruby is perfect for this type of thing. While there is the basic ARGV approach, there are some great RubyGems that help with creating command line interfaces (CLIs) with ease. In this article, we’ll take a look at several Ruby CLI gems.

Prerequisites

  • Ruby (at least 1.9.3, but greater than 2.0.0 recommended)
  • In this article, I mention various RubyGems. It’s expected that you can run $ gem install GEMNAME without much trouble.

Tips

  • Most Ruby projects have a bin directory in which the CLI binary is stored. (yes, I know it’s not technically a real “binary file”)
  • If you end up using this code verbatim, remember to make your files executable.
  • You’re probably going to need a Ruby shebang for your binary file #!/usr/bin/env ruby.
  • Try to keep the code in your binary file as terse as possible. Not only will this help you later on with bug fixing, but it will also help collaborators.
  • The CLI gems below have a code block where you pass the code to be executed when the command is run. Try to keep this down to just a method call. Possibly passing the options from the command line into an options hash in your method, where the options are actually parsed.

Built-in ARGV

ARGV is a built-in array in Ruby for parsing command line arguments. For example, ARGV[0] returns the first argument passed to the Ruby script. For dirt-simple argument parsing, you could try something like this:

puts "Hello #{ARGV[0]}!"
$ ./my-app world
Hello world!

A very simple example, but quite powerful. The only issue with this type of argument reading is error handling. Specifically when a required argument is not given. This is easily fixed though using simple Ruby (see Array docs and ARGV docs). I won’t get too in depth with ARGV, but you can see the possibilities. Just remember that ARGV is just an array, so you should treat as such when reading arguments and options.

command = ARGV[0]
name = ARGV[1]

name = "world" unless name

case command
when "hello"
  puts "Hello #{name}!"
when "goodbye"
  puts "Goodbye #{name}!"
end

# And so on...

Thor

You’ve probably heard of Thor before. If you look at the RubyGems stats, you’ll see Thor at the top. It’s also my personal favorite due to its extensibility. Here’s some basic syntax:

The CLI is wrapped in a class inheriting from the Thor superclass.

require "thor"

class MyApp < Thor

end

Add a command by adding a method to the class.

require "thor"

class MyApp < Thor
  def hello
      puts "Hello world!"
  end
end

At the bottom of the file (typically), call MyApp#start to allow it to start the CLI.

# ... (MyApp class code above)

MyApp.start

$ ./my-app hello
Hello world!

We can add arguments to this by simply adding arguments to the method.

require "thor"

class MyApp < Thor
  desc "hello", "Say hello"
  def hello(name)
    puts "Hello #{name}"
  end
end
$ ./my-app hello universe
Hello universe!

Or we could do the same things with an option.

require "thor"

class MyApp < Thor
  desc: "Say Hello"
  method_option :name, :aliases => "-n", :desc => "Specify a name"
  def hello
    puts "Hello #{options[:name]}"
  end
end

You can specify the type of the option that you have by adding to method_option. The documentation for that can be found here. For example, in the sample code above, we create an option name, that is aliased to -n, and has the description "Specify a name".

You may have noticed that we’ve been using desc a few times. This is another cool feature of Thor, it is self-documenting. If you were to run my-app help hello, then you would see a little bit of documentation about it.

$ ./my-app help hello
Usage:
  my-app hello

Options:
  -n, [--name=NAME]  # Specify a name

Say hello

This is a really cool feature of a lot of the CLI gems that I’ve researched. Thor does it using the desc option, but as you’ll see, other gems use different documenting options.

All in all, Thor is a great tool, I believe it powers the Rails command line interface as well. The self-documentation built in is a nice touch, and I’ve only touched the surface of all of its features. You should really check out its wiki if you want to see full documentation. There’s also a very nice tutorial here on SitePoint.

Commander

Commander is a full-fledged suite for writing CLI applications. I’ve written a CLI in it, and Jekyll also uses it. It has a neat DSL and, like Thor, is self documenting. It also has really nice error handling, and is so easy, you can create a command line app in under 15 seconds.

To create a new CLI go into the bin/ directory of your app (or anywhere else you need it), and run $ commander init my-app (syntax: $ commander init FILENAME).

You’ll get some prompts for information:

$ commander init my-app
Machine name of program: my-app
Describe your program: Does so many things.
List the commands you wish to create: hello goodbye # Notice no commas
Initialized template in my-app

This creates the file my-app which has the contents:

#!/usr/bin/env ruby

require 'rubygems'
require 'commander/import'

program :version, '0.0.1'
program :description, 'Does oh so many things.'

command :hello do |c|
  c.syntax = 'my-app hello [options]'
  c.summary = ''
  c.description = ''
  c.example 'description', 'command example'
  c.option '--some-switch', 'Some switch that does something'
  c.action do |args, options|
    # Do something or c.when_called My-app::Commands::Hello
  end
end

command :goodbye do |c|
  c.syntax = 'my-app goodbye [options]'
  c.summary = ''
  c.description = ''
  c.example 'description', 'command example'
  c.option '--some-switch', 'Some switch that does something'
  c.action do |args, options|
    # Do something or c.when_called My-app::Commands::Goodbye
  end
end

If you go immediately into your terminal, you now have a CLI that does nothing.

$ ./my-app --help
  NAME:

    my-app

  DESCRIPTION:

    Does oh so many things.

  COMMANDS:

    goodbye                             
    hello                               
    help                 Display global or [command] help documentation.

  GLOBAL OPTIONS:

    -h, --help 
        Display help documentation

    -v, --version

Next, we’ll edit this file so that it has the following functionality:

$ ./my-app hello world
Hello world!

$ ./my-app goodbye -n universe
Goodbye universe!

For the first command, hello, we take an argument as a name. The second command, goodbye, takes an option for a name.

Here we go!

#!/usr/bin/env ruby

require 'rubygems'
require 'commander/import'

program :version, '0.0.1'
program :description, 'Does oh so many things.'

command :hello do |c|
  c.syntax = 'my-app hello NAME [options]'
  c.summary = 'Says hello'
  c.description = c.summary # Because we're lazy
  c.example 'Says hello to "world"', 'my-app hello world'
  c.action do |args, options|
    name = args.shift # or args.first or args[0] if you wish
    puts "Hello #{name}!"
    # Or in a real app, you would call a method and pass command line arguments to it.
  end
end

command :goodbye do |c|
  c.syntax = 'my-app goodbye NAME [options]'
  c.summary = 'Says goodbye'
  c.description = c.summary
  c.example 'Say goodbye to world', 'my-app goodbye -n world'
  c.example 'Say goodbye to world', 'my-app goodbye --name world'
  c.option '-n', '--name NAME', String, 'Specify a name' # Option aliasing
  c.action do |args, options|
    puts "Hello #{options.name}!"
  end
end

By default, options are booleans. So when we add the String type to it, it allows for the option to be called with an argument. You could also define an option like so:

# ...
  c.option '--option', 'A boolean by default'
# ...

Commander also has HighLine built in so you can do cool things like this:

ask_for_array "Commands: " # => Commands: hello goodbye
# => ["hello", "goodbye"]
say "Hello!" # => Hello!
ask "Password: " { |char| char.echo = "*" } # => Password: ***********
# => "supersecret"

And of course you can do countless things with Commander, as with Thor. You can
check those out
here.

Slop

Slop is another DSL for creating CLI apps in Ruby. You probably already have it from some other gem’s dependencies (just like thor and, probably, commander). It seems to be very popular, so let’s jump in!

Slop is very minimal and doesn’t do much for you unless you ask it to. Let’s start by adding a version command. This will be -v and --version.

require "slop"

Slop.parse do
  on "-v", "--version" do
    puts "my-app v0.0.1"
  end
end

Let’s create the same app we had before, but with slop.

require "slop"

opts = Slop.parse do
  on "-v", "--version" do
    puts "my-app v0.0.2"
  end

  command 'hello' do
    run do |opts, args|
      puts "Hello #{args.shift}"
    end
  end

  command 'goodbye' do
    on 'n=', 'name='

    run do |opts, args|
      puts "Goodbye #{opts[:name]}"
    end
  end
end

This is extremely minimal. The CLI doesn’t even care if the option’s argument is blank! But, that’s the point of Slop: simple option parsing. It’s one of the things I like most about Slop. If Commander is Rails, then Slop is Sinatra.

I would suggest Slop for simple CLIs and Commander for complex ones. You should really check out its README to see the full list of features and options.

Conclusion

There are a lot of great gems out there for building command line interfaces. Which one you choose is entirely up to your style. There’s Ruby-like (Thor), minimal (Slop), and full on suite (Commander) CLI frameworks at your disposal. In the end, it’s up to you on what type of library you want to use for building a CLI, if any (Homebrew uses the ARGV option!). There are also so many others that I did not mention in this article, but I hope this gives you a basic overview of some great choices.

Free JavaScript: Novice to Ninja Sample

Get a free 32-page chapter of JavaScript: Novice to Ninja and receive updates on exclusive offers from SitePoint.

  • http://davidcel.is/ David Celis

    I recently discovered Escort and I’ve found it the nicest to work with so far, I think. I had issues with Thor’s self documentation being totally inconsistent when using subcommands, so I had to make a switch. Unfortunately, Thor is pretty much the only choice I could find for making a generator, so when I made a CLI that needed a generator, I ended up including both Thor and Escort :

  • JesseHerrick

    I debated between Commander and GLI, however, I eventually decided to use Commander because I have used it before in my own gems. I didn’t cover both od these gems because they are so similar in style.

  • JesseHerrick

    Interesting concept. I’ve never had the need to output command line options to that extent. I would hope that someone wouldn’t have the need to shell out that often in their code.

  • http://mayurrokade.com/ Mayur Rokade

    The template u get from commander using the init command is really awesome. Does thor n slop have that?

  • http://danieldosen.org Daniel Dosen

    Wow – I can’t believe I didn’t know what Thor was… Awesome!

  • JesseHerrick

    Well, there’s always MacRuby and ScriptingBridge…

  • Dennis

    Really surprised the article and comments haven’t mentioned the following yet. You don’t want to mess around with `ARGV` yourself and sometimes using a CLI gem is overkill. For those cases the built-in OptionParser library (`require “optparse”`) is perfect.