Skip to Content
Course content

44: Practice Exercise: Building a Simple Todo List Gem

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

Where do the files actually go so this works as a gem?

If you've only written scripts so far, the directory structure of a gem can feel like overkill. But there's a reason for it. Ruby expects a specific layout so that when someone runs require 'your_gem_name', it knows exactly where to look. For our todo list, you want a lib/ directory. Inside that, you create a folder named after your gem, and inside that, a file also named after your gem.

It looks like this: lib/todo_manager/todo_manager.rb. I know it feels redundant to name everything the same thing, but trust me, it prevents naming collisions when your gem is installed alongside a hundred others. You'll also need a .gemspec file in the root. That's the "ID card" for your gem—it tells Ruby the version, the author, and which files are included. Don't forget to actually require your logic files inside that main todo_manager.rb entry point, otherwise, the user will load the gem and find... absolutely nothing.

Should I put the CLI logic and the Todo logic in the same class?

Definitely not. This is where a lot of people trip up. If you mix your puts statements and gets.chomp calls inside your core logic, you've just made your gem impossible to use as a library. What if someone wants to use your TodoManager inside a Rails app or a Discord bot later? They can't, because your code will keep trying to ask for terminal input.

Keep a clean split. Create a Task class and a List class for the data, and a separate CLI class for the interface. Here is how I'd sketch that separation:

# lib/todo_manager/list.rb
class TodoManager::List
  def initialize
    @items = []
  end

  def add(task_name)
    @items << { name: task_name, done: false }
  end
end

# lib/todo_manager/cli.rb
class TodoManager::CLI
  def run
    puts "Welcome to your Todo List!"
    # All the puts/gets logic goes here, calling the List class
  end
end

How do I make it an actual command I can run from my terminal?

You don't want to type ruby -Ilib bin/todo every time you want to add a task. To make it a real command, you need a bin/ directory. Create a file in there—let's call it todo (no extension)—and start it with a "shebang" line. This tells your shell to use Ruby to execute the file.

Your bin/todo file should look like this:

#!/usr/bin/env ruby

require 'todo_manager'
TodoManager::CLI.new.run

One pro tip: make that file executable by running chmod +x bin/todo in your terminal. Now, as long as you're in the project root, you can just run ./bin/todo and it feels like a real piece of software.

What's the best way to save the list to a file without overcomplicating it?

Since this is a simple gem, don't bother with a database like SQLite. It's too much overhead for a practice exercise. Just use JSON. It's built into Ruby, it's human-readable, and it maps perfectly to the arrays and hashes you're already using for your tasks.

I usually suggest creating a storage.rb module or just adding a save and load method to your List class. Use File.write and JSON.dump to push your data to a hidden file in the user's home directory (like ~/.todo_list.json). This ensures that the list persists even after the program exits, which is the whole point of a todo app, right?




📋 Practical Task

Build the Persistent Todo CLI Gem

Your task is to build a functional Ruby gem called todo_manager. Your gem must meet the following technical requirements:

  • Structure: Follow the standard gem layout (lib/todo_manager/ and a bin/ directory).
  • Separation of Concerns: Create a List class to handle adding/removing tasks and a CLI class to handle user input and output.
  • Persistence: Implement a save/load system using the json standard library so that tasks are remembered between sessions.
  • Executable: Create a bin/todo file with the correct shebang that launches the CLI.
  • Functionality: The user should be able to:
    1. Add a new task.
    2. List all current tasks.
    3. Mark a task as completed.
    4. Exit the program.

Test your gem by running it via ./bin/todo and verifying that your tasks are still there after you restart the application.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.