Ruby Command Line Interface Gems

Share this article

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.

Frequently Asked Questions (FAQs) about Ruby Command Line Interface Gems

What are the key differences between dry-rb/dry-cli and other Ruby CLI gems?

Dry-rb/dry-cli is a powerful command line interface gem that stands out for its simplicity and flexibility. Unlike other gems, it allows for easy parsing of command line options, supports subcommands, and provides a robust mechanism for error handling. It also supports i18n out of the box, which is not a common feature in other CLI gems.

How do I choose the best Ruby CLI gem for my project?

The choice of a Ruby CLI gem depends on the specific needs of your project. Consider factors such as the complexity of the command line interface you need to build, the level of customization you require, and the learning curve of the gem. Reading reviews and comparing features of different gems can also be helpful.

Can I build a Ruby CLI gem from scratch?

Yes, it is possible to build a Ruby CLI gem from scratch. This can be a great learning experience and can give you a high level of control over the functionality of your gem. However, it can also be time-consuming and may require a good understanding of Ruby and command line interfaces.

What is cli-builder and how does it compare to other gems?

Cli-builder is a Ruby gem that simplifies the process of building command line interfaces. It provides a DSL for defining commands and options, and automatically generates help messages. Compared to other gems, cli-builder is relatively new and may not have as many features or as much community support.

How can I improve the performance of my Ruby CLI gem?

There are several ways to improve the performance of your Ruby CLI gem. These include optimizing your code, reducing the number of system calls, and using faster algorithms or data structures where possible. Profiling your gem can help identify bottlenecks and areas for improvement.

How do I handle errors in my Ruby CLI gem?

Error handling is an important aspect of building a robust CLI gem. You can use Ruby’s built-in exception handling mechanisms, or use features provided by your chosen gem. For example, dry-rb/dry-cli provides a mechanism for defining custom error messages.

Can I use Ruby CLI gems in non-Ruby projects?

While Ruby CLI gems are designed to be used in Ruby projects, it is possible to use them in non-Ruby projects by invoking the Ruby interpreter from your project. However, this may not be the most efficient or convenient solution, and there may be better options depending on your project’s language and requirements.

How do I test my Ruby CLI gem?

Testing is crucial to ensure the reliability of your CLI gem. You can use Ruby’s built-in testing frameworks, or third-party tools like RSpec. Your tests should cover all major functionality of your gem, and should simulate a variety of command line inputs.

How do I distribute my Ruby CLI gem?

Once you’ve built and tested your Ruby CLI gem, you can distribute it through RubyGems, the standard package manager for Ruby. You’ll need to create a gemspec file that describes your gem, and then you can publish it to the RubyGems repository.

How do I keep my Ruby CLI gem up to date?

Keeping your Ruby CLI gem up to date involves regularly checking for and implementing updates to the gem’s dependencies, as well as updating your gem’s code to use the latest Ruby features and best practices. You should also listen to feedback from users of your gem, as they may identify bugs or suggest new features.

Jesse HerrickJesse Herrick
View Author

Jesse Herrick is an avid Ruby developer who specializes in web development. He is a back-end developer at Littlelines and loves programming. You can read his personal blog at: https://jesse.codes.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week