DEV Community

Unpublished Post. This URL is public but secret, so share at your own discretion.

Creating My First CLI Gem Part 2: Getting the Command Line Interface Up and Running

Last time, I got the gem file structure set up and created an executable file that I could run in my terminal. The next step was to set up the command line interface.

See also:

Instead of putting the CLI code directly in the bin/find-recipe, I wanted to create a class file that would act as the CLI controller and that could be called from the executable file through FindRecipe::CLI.new.call.

For the CLI class and all the other program files, I think it’s better to put them under the lib/ folder. The lib folder already contained a couple items automatically generated by Bundler: a find-recipe.rb file and another folder, find_recipe/, which contained a version.rb file that is used to keep track of, well, the version.

The find-recipe.rb file contained some code already:

# lib/find_recipe.rb

require "find_recipe/version"

module FindRecipe
  # Your code goes here...
end
Enter fullscreen mode Exit fullscreen mode

While I could write my program code inside the FindRecipe module, for now I’m going to use the file as a way to load my environment and separate my classes into files under the lib/find_recipe/ folder (later, I think I’ll create a config folder with an environment.rb file).

To finally get started with coding the CLI, I created a cli.rb file inside lib/find_recipe/. Now the basic file structure looks like this:

find_recipe
  |- bin/
  | |- find-recipe
  | |- console
  | |- setup
  |- lib/
  | |- find_recipe.rb 
  | |- find_recipe/
  | | |- version.rb
  | | |- cli.rb
  |- spec/
  |- Gemfile
  |- find_recipe.gemspec
  |- README.md
Enter fullscreen mode Exit fullscreen mode
  • bin/ contains the executable files, notably the find-recipe file I created which launches my program (console and setup file were automatically generated by Bundler)
  • lib/ contains my program logic, like the CLI logic and later any other classes I’ll need (probably something that creates recipe objects and a scraper class that gets the data used by the recipe objects)
  • spec/ is the appropriate place to put test files

The CLI class

Before writing any code for the scraper or a recipe class, first I wanted to get my user interface working. When the program launches, it would call the CLI class through the FindRecipe::CLI.new.call method.

Since my plan is to offer two ways to get a list of recipes (trending recipes or through a search keyword), I decided to break up the CLI into several methods that would handle each task:

  1. The search options
  2. Looking at trending recipes
  3. Using a keyword to see recipes
  4. Exiting

To concentrate on just getting the CLI logic to work properly, for now, I’m using dummy data that represents recipe objects that will be scraped by the scraper class later on.

# This Class acts as the CLI Controller
class FindRecipe::CLI

  def call
    search_options
  end

  def search_options
    puts ""
    puts "How do you want to get started?"
    puts ""
    puts "1. See trending recipes"
    puts "2. Search for a recipe"
    puts ""
    puts "Enter 1 or 2, or exit"

    input = gets.strip.downcase

    if input == "1"
      trending_recipes
    elsif input == "2"
      search_recipe
    elsif input == "exit"
      exit
    else
      puts "Not sure what you mean..."
      search_options
    end
  end

  def trending_recipes
    puts "1. Khaman dhokla"
    puts "2. Low carb Courgette fritters"
    puts "3. Bacon, Leek and Potato Soup"
    puts ""

    puts "Choose a recipe number or type 'back'"

    input = gets.strip.downcase
    if input.to_i > 0 && input.to_i <= 3
      get_recipe_from_number( input.to_i )
    elsif input == "back"
      search_options
    else
     puts "Not sure what you mean..."
     puts ""
     trending_recipes
    end

    input = nil

    while input != "exit"
      puts "Do you want to see the list again, restart, or exit?"
      puts "Enter list, restart, or exit"  
      input = gets.strip.downcase

      if input == "list"
        trending_recipes
      elsif input == "restart"
        search_options
      end
    end

    exit
  end

  def get_recipe_from_number(number)
    puts "Recipe #{number}"
  end

  def search_recipe
    puts "What is the dish or ingredient you want to search for?"

    input = gets.strip.downcase

    puts "#{input} Recipes:"

    exit
  end

  def exit
    puts "See you next time!"
  end

end
Enter fullscreen mode Exit fullscreen mode

This seems to be working properly for now, so my next steps will be creating a recipe class so the CLI will use real recipes objects instead of dummy data and a scraper class that will get data from a live site!

P.S. You can see the git repository for this project here.

Top comments (0)