Thor is a Ruby command-line parser that makes it easy to build powerful command-line applications. Terminalwire uses Thor to define commands, parse arguments, and generate help output for your terminal applications.
This chapter covers the basics of working with Thor to build your command-line interface.
Defining commands
Commands are defined in your terminal classes by using the desc method followed by a regular Ruby method. The method name becomes the command name that users type in their terminal.
class MainTerminal < ApplicationTerminal desc "hello", "Say hello" def hello puts "Hello, World!" end end
Users can now run bin/my-app hello to execute this command.
Commands with arguments
Commands can accept arguments by adding parameters to the method. Required arguments are defined as method parameters, and Thor will automatically parse them from the command line.
class MainTerminal < ApplicationTerminal desc "hello NAME", "Say hello to NAME" def hello(name) puts "Hello, #{name}!" end end
Users run this with bin/my-app hello Alice, which outputs “Hello, Alice!”.
Multiple arguments
You can define multiple arguments by adding more method parameters:
class MainTerminal < ApplicationTerminal desc "greet GREETING NAME", "Greet NAME with a custom GREETING" def greet(greeting, name) puts "#{greeting}, #{name}!" end end
Users run this with bin/my-app greet "Good morning" Alice.
Optional arguments
Optional arguments can be defined by providing default values:
class MainTerminal < ApplicationTerminal desc "hello [NAME]", "Say hello to NAME (or World if not provided)" def hello(name = "World") puts "Hello, #{name}!" end end
Users can run bin/my-app hello or bin/my-app hello Alice.
Option flags
Options, also called flags, allow users to pass additional configuration to commands. They’re defined using the option method and accessed via the options hash.
class MainTerminal < ApplicationTerminal desc "hello NAME", "Say hello to NAME" option :enthusiastic, type: :boolean, aliases: "-e", desc: "Add emphasis" def hello(name) greeting = "Hello, #{name}" greeting += "!" if options[:enthusiastic] puts greeting end end
Users can run:
bin/my-app hello Alice
Hello, Alice
bin/my-app hello Alice --enthusiastic
Hello, Alice!
bin/my-app hello Alice -e
Hello, Alice!
Option types
Thor supports several option types:
class MainTerminal < ApplicationTerminal desc "configure", "Configure the application" option :port, type: :numeric, default: 3000, desc: "Port number" option :host, type: :string, default: "localhost", desc: "Host name" option :verbose, type: :boolean, aliases: "-v", desc: "Verbose output" option :environment, type: :string, enum: ["development", "production"], desc: "Environment" def configure puts "Host: #{options[:host]}" puts "Port: #{options[:port]}" puts "Environment: #{options[:environment]}" if options[:environment] puts "Verbose mode enabled" if options[:verbose] end end
Required options
Options can be marked as required:
class MainTerminal < ApplicationTerminal desc "deploy", "Deploy the application" option :environment, type: :string, required: true, desc: "Target environment" def deploy puts "Deploying to #{options[:environment]}..." end end
Thor will show an error if users forget to provide required options.
Subcommands
For larger applications, you can organize commands into subcommands by creating separate Thor classes and registering them:
# ./app/terminals/ticket_terminal.rb class TicketTerminal < ApplicationTerminal desc "list", "List all support tickets" def list puts "Fetching your support tickets..." end desc "create SUBJECT", "Create a new support ticket" def create(subject) puts "Creating ticket: #{subject}" end desc "show ID", "Show details for a ticket" def show(id) puts "Showing ticket ##{id}..." end end # ./app/terminals/main_terminal.rb class MainTerminal < ApplicationTerminal desc "ticket SUBCOMMAND", "Manage support tickets" subcommand "ticket", TicketTerminal end
Users can run:
bin/my-app ticket list
Fetching your support tickets...
bin/my-app ticket create "Need help with API"
Creating ticket: Need help with API
bin/my-app ticket show 123
Showing ticket #123...
Help output
Thor automatically generates help output for your commands. Users can view help by running:
Show all commands
bin/my-app help
Show help for a specific command
bin/my-app help hello
Customizing the basename
The basename is what appears in help output as your command name. You can customize it in your ApplicationTerminal:
class ApplicationTerminal < Thor include Terminalwire::Thor def self.basename = "my-app" end
Long descriptions
You can add longer descriptions to commands using the long_desc method:
class MainTerminal < ApplicationTerminal desc "deploy", "Deploy the application" long_desc <<-LONGDESC Deploy the application to the specified environment. This command will: * Build the application * Run tests * Deploy to the target environment * Run post-deployment checks Example: $ my-app deploy --environment production LONGDESC option :environment, type: :string, required: true def deploy puts "Deploying to #{options[:environment]}..." end end
Class-level configuration
Thor provides several class-level methods for configuring your terminal:
class MainTerminal < ApplicationTerminal # Set the namespace for subcommands namespace :main # Define a default command when none is provided def self.default_task = "help" # Hide commands from help output no_commands do def helper_method # This won't appear as a command end end end
Best practices
When building your Thor commands, follow these best practices:
- Keep command names short and memorable - Users will type these frequently
- Use clear, descriptive help text - Good help text makes your CLI self-documenting
- Validate input early - Check arguments and options at the start of your methods
- Provide helpful error messages - Tell users what went wrong and how to fix it
- Use options for configuration - Reserve arguments for primary data like names and IDs
- Group related commands into subcommands - This keeps your main command list manageable
Next steps
Now that you understand how to work with command-line parsers, you can build powerful command-line interfaces. The next chapter covers Sessions, which allow you to maintain state between commands—essential for implementing authentication and user-specific functionality.