Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
118: Practice Exercise: Building a CLI Tool with Thor
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
Scaffoldthat inherits fromThor. - Implement a command called
newthat takes one required argument:PROJECT_NAME. - Add a
method_optionto thenewcommand 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
startcall so it can be run from the terminal.
Example Usage:
ruby scaffold.rb new MyAwesomeApp --type=rails
Output: Creating a rails project named MyAwesomeApp...
There are no comments for now.