Skip to Content
Course content

118: Practice Exercise: Building a CLI Tool with Thor

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Think of building a CLI (Command Line Interface) tool like setting up a professional kitchen's order rail. When a ticket comes in, it's not just a random scribble; it's a structured request. "Burger" is the command. "No onions" or "Medium-rare" are the options. The chef doesn't have to guess what the customer wants because the system ensures the request follows a specific format before it ever hits the grill.

In Ruby, Thor acts as that order rail. Instead of you manually parsing ARGV—which, let's be honest, is a nightmare once you have more than two arguments—Thor allows you to map command-line inputs directly to Ruby methods. If a user types my_tool setup --env=production, Thor sees "setup" as the method to call and --env as a parameter to pass into that method. It handles the validation, the help menus, and the type casting for you.

Turning Methods into Commands

The magic of Thor is that any public method defined inside a class inheriting from Thor becomes a CLI command. I've found that the biggest mistake people make is overthinking the structure. Just write a method, describe it using the desc keyword, and you're halfway there.

require 'thor'

class DevTool < Thor
  desc "greet NAME", "Say hello to a specific developer"
  def greet(name)
    puts "Hey #{name}, time to squash some bugs!"
  end
end

DevTool.start(ARGV)

In this snippet, desc does two things: it tells Thor that the greet method is a command, and it provides the documentation that appears when a user types help. If you run this as ruby dev_tool.rb greet Alice, it works perfectly. If you forget the name, Thor will automatically complain that the argument is missing. You didn't have to write a single if ARGV[1].nil? check.

Adding Nuance with Options

Arguments are great for required data, but options (flags) are where CLI tools become powerful. Imagine you're building a tool to clear cache files. Most of the time, you just want them gone. But occasionally, you want a "dry run" to see what would be deleted without actually doing it.

We use method_option for this. It's a declarative way to tell Thor, "This specific method accepts an optional flag."

class DevTool < Thor
  desc "clear_logs", "Cleans up the log directory"
  method_option :dry_run, type: :boolean, default: false, desc: "Show what would be deleted"
  def clear_logs
    if options[:dry_run]
      puts "Dry run: I would have deleted 500MB of logs."
    else
      puts "Logs purged successfully!"
    end
  end
end

Notice how options is a hash available inside the method. I prefer using type: :boolean here because it allows the user to simply type --dry-run without needing to provide a value like true or false. It keeps the interface clean and intuitive.

The Secret Sauce: The Entry Point

One thing that often trips people up is how to actually execute the tool. Calling DevTool.start(ARGV) is the trigger. It takes the array of strings from the shell and maps them to your class logic. When you're ready to move this from a script to a real gem, you'll put that .start call inside a binary file in your bin/ directory. This is what allows users to just type dev_tool clear_logs instead of ruby dev_tool.rb clear_logs.




📋 Practical Task

Exercise: Build a 'Project Scaffolder' CLI

Your goal is to create a CLI tool called Scaffold using the Thor gem. This tool will simulate the creation of a new project directory structure.

Requirements:

  • Create a class Scaffold that inherits from Thor.
  • Implement a command called new that takes one required argument: PROJECT_NAME.
  • Add a method_option to the new command called --type. This should be a string, with a default value of "ruby".
  • The output of the command should be a printed message: "Creating a [type] project named [PROJECT_NAME]..."
  • Ensure the script ends with the correct start call so it can be run from the terminal.

Example Usage:
ruby scaffold.rb new MyAwesomeApp --type=rails
Output: Creating a rails project named MyAwesomeApp...

Rating
0 0

There are no comments for now.

to be the first to leave a comment.