Skip to content

Ruby Mini Projects — Build Real Applications Using Ruby Concepts

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Ruby Mini Projects. We cover key concepts, practical examples, and best practices to help you master this topic.

Ruby mini projects applying classes, metaprogramming, threads, and gems to build a CLI task manager, web scraper, file watcher, and API client.

What You'll Learn

  • Building a CLI task manager
  • Creating a web scraper
  • Building a file watcher
  • Creating an API client gem

Why It Matters

Projects solidify your learning. These projects mirror real-world tools used at companies like GitHub (Ruby-based), Shopify, and DodaTech.

Real-World Use

CLI tools for DevOps, web scrapers for data collection, file watchers for CI/CD, API clients for Microservices.

flowchart LR
    A["Projects"] --> B["CLI Task Manager"]
    A --> C["Web Scraper"]
    A --> D["File Watcher"]
    A --> E["API Client Gem"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

CLI Task Manager

#!/usr/bin/env ruby
require "json"
require "fileutils"

class TaskManager
  FILE = "tasks.json"

  def initialize
    @tasks = load_tasks
  end

  def run(args)
    case args.first
    when "add"
      add(args.drop(1).join(" "))
    when "list"
      list
    when "done"
      done(args[1].to_i)
    when "delete"
      delete(args[1].to_i)
    else
      puts "Commands: add, list, done, delete"
    end
  end

  private

  def load_tasks
    File.exist?(FILE) ? JSON.parse(File.read(FILE)) : []
  rescue
    []
  end

  def save
    File.write(FILE, JSON.pretty_generate(@tasks))
  end

  def add(description)
    @tasks << { id: @tasks.size + 1, description: description, done: false }
    save
    puts "Added: #{description}"
  end

  def list
    @tasks.each do |task|
      status = task["done"] ? "[x]" : "[ ]"
      puts "#{task['id']}. #{status} #{task['description']}"
    end
  end

  def done(id)
    task = @tasks.find { |t| t["id"] == id }
    task["done"] = true if task
    save
  end

  def delete(id)
    @tasks.reject! { |t| t["id"] == id }
    save
  end
end

TaskManager.new.run(ARGV)

Web Scraper

require "net/http"
require "nokogiri"

class Scraper
  def initialize(url)
    @url = url
  end

  def fetch
    uri = URI(@url)
    response = Net::HTTP.get(uri)
    doc = Nokogiri::HTML(response)
    doc.css("h1").each { |h| puts h.text }
  end
end

Scraper.new("https://example.com").fetch

File Watcher

require "fileutils"

class FileWatcher
  def initialize(directory)
    @dir = directory
    @files = {}
  end

  def watch
    loop do
      Dir.glob("#{@dir}/*").each do |file|
        mtime = File.mtime(file)
        if @files[file] && @files[file] != mtime
          puts "Modified: #{file}"
        elsif !@files[file]
          puts "New: #{file}"
        end
        @files[file] = mtime
      end
      sleep 1
    end
  end
end

FileWatcher.new(".").watch

API Client Gem Structure

# lib/weather_client.rb
require "net/http"
require "json"

class WeatherClient
  BASE_URL = "https://api.weather.gov"

  def initialize
    @headers = { "User-Agent" => "WeatherClient/1.0" }
  end

  def forecast(lat, lon)
    points = get("/points/#{lat},#{lon}")
    forecast_url = points.dig("properties", "forecast")
    get(forecast_url)
  end

  private

  def get(path)
    uri = URI(path.start_with?("http") ? path : "#{BASE_URL}#{path}")
    response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
      http.get(uri, @headers)
    end
    JSON.parse(response.body)
  end
end

client = WeatherClient.new
puts client.forecast(39.7456, -97.0892)

Common Mistakes

1. No Error Handling

Always wrap API calls and file operations in begin/rescue blocks.

2. Hardcoded Paths

Use File.expand_path or relative paths. Never hardcode absolute paths.

3. No Input Validation

Check ARGV length before accessing elements. Validate user input.

4. Forgetting to Save

File-based apps must save after every mutation. Write tests to verify.

5. Blocking the Main Thread

Use threads or Ractors for long-running operations in watchers.

Practice Questions

1. How does the task manager persist data? Via JSON file. Tasks are loaded on init and saved after every mutation.

2. How does the file watcher detect changes? Compares File.mtime timestamps. Polls every second.

3. How does the API client handle HTTPS? Uses Net::HTTP with use_ssl: true for HTTPS connections.

4. What gem does the scraper use? Nokogiri for HTML Parsing, Net::HTTP for fetching pages.

Challenge: Add a --due DATE option to the task manager tasks and list tasks due today.

Solution
def add(description, due_date = nil)
  @tasks << {
    id: @tasks.size + 1,
    description: description,
    done: false,
    due: due_date
  }
  save
end

def due_today
  today = Date.today
  @tasks.select { |t| t["due"] == today.to_s }
end

FAQ

{{< faq question="How do I distribute my CLI tool?" >}} Package it as a gem with an executable. Use spec.executables in your gemspec. Users install with gem install your_tool. {{< /faq >}}

{{< faq question="How do I add color to CLI output?" >}} Use the colorize gem or ANSI escape codes. puts "\e[31mRed text\e[0m" for red text. {{< /faq >}}

{{< faq question="What's the best way to handle command-line arguments?" >}} Use the OptionParser standard library or the thor gem for more complex CLIs. {{< /faq >}}

{{< faq question="How do I make HTTP requests?" >}} Use the standard net/http library or the httparty and faraday gems for more features. {{< /faq >}}

{{< faq question="Should I use threads or Ractors for concurrent scraping?" >}} Threads for I/O-bound scraping (waiting for responses). Ractors for CPU-bound parsing. Use a thread pool to limit connections. {{< /faq >}}

Try It Yourself

Run the task manager:

ruby task_manager.rb add "Learn Ruby"
ruby task_manager.rb list
ruby task_manager.rb done 1

Expected output:

Added: Learn Ruby
1. [ ] Learn Ruby
1. [x] Learn Ruby

What's Next

Now that you've built projects, explore the Ruby ecosystem and community.

Topic Description Link
Ruby Ecosystem Community and tools {{< ref "46-ecosystem" >}}
Ruby Deployment Deploying Ruby apps {{< ref "48-deployment" >}}
Go Mini Projects Compare Go projects Go

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro