Alpine-Pro /// My First Ruby CLI Gem

Posted by Eryk Rachwal on February 19, 2020

Alpine-Pro, my first Ruby project. The purpose of it is to populate a list of potential expeditions the user may be interested in and give a description for the chosen option. Looking back on it, it was only fitting my gem was about mountains. Because this was one heck of a journey! An uphill climb, with many setbacks, doubts and struggles. However, just like reaching the summit of a mountain, I persevered and pushed on. I can honestly say that this one single project has taught me more about being a software engineer than anything else so far. It won’t be easy, it’ll be very frustrating and confusing at times, but man, is that feeling when you figure out your broken code and finally see your hard work come together worth it.

I began my gem project by creating three classes and cooresponding files. A CLI/Controller class, a Scraper class, and an Expedition class. My next step was to create a basic outline of how I wanted my CLI to look. The purpose of this was for me to be able to just focus on my code and have one less thing to worry about. In addition, there is a sacred rule I live by: “Plan your work and work your plan.” After this, I began working on my Scraper class. I wanted to make sure the website I chose was “scrapable”. My original website was not, so I had to find a new one. This is why I chose to focus on my scraper first (and it payed off)! Here is my Scraper Class:

———————————————————————————————————————————–

class AlpinePro::Scraper

def self.scrape_expeditions index_page = Nokogiri::HTML(open(“https://www.adventureconsultants.com/expeditions/”))

array_of_expeditions = index_page.css("div.slide__content")
array_of_expeditions.each_with_index do |expedition_card|
name = expedition_card.css("span.slide__title.uppercase").text
path = expedition_card.css("a").attr("href").value
AlpinePro::Expedition.new(name, path)
 end    end 

def self.scrape_description(expedition) description_page = Nokogiri::HTML(open(“https://www.adventureconsultants.com”+ expedition)) description_page.search(“div#body-content”).text.strip
end

end

**———————————————————————————————————————————– **

I first had to scrape to get the expedition names I was looking for from the original url. I also had to scrape a path so that it can be initialized with the expedition name in my Expedition class. This had to be done because the description I wanted to scrape was on a separate page belonging to each individual Expedition.

Here is my Expedition Class where the name and path are initialized:

**———————————————————————————————————————————– **

class AlpinePro::Expedition

attr_accessor :name, :path, :description

@@all = []

def initialize(name, path) @name = name @path = path save end

def self.all @@all end

def save @@all « self end

end

**———————————————————————————————————————————– **

Once I had these two classes going, it was time to put my CLI together and make everything work together. I created a “start method” to welcome the user and called on some methods to run them in order. In this method, I also called on my scraper, to scrape the expedition names. Then in my next method, the “list_expeditions method”, I made sure each individual expedition name was assigned an index (starting at 1) so that it can be interacted with, as the user is technically interacting with the index and not the name. In the next method, “get_expedition” the user is asked for an input to the cooresponding list of expeditions. This method also recognizes if a proper command was input. Finally, there is the “want_description method” which is the “second layer” to the program. It double checks with the user, making sure that they made the correct input. Once confirmed, it prints the description by caling “expedition.description”.

Here is the CLI:

**———————————————————————————————————————————– **

class AlpinePro::CLI

def start 
  puts ""
  puts "Welcome to Alpine-Pro!"
  puts ""
  puts "Here are expeditions you should check out:"
  puts ""
  AlpinePro::Scraper.scrape_expeditions
  list_expeditions
  get_expedition
end 


def list_expeditions
  AlpinePro::Expedition.all.each.with_index(1) do |expedition, index|
    puts "#{index}. #{expedition.name}"
  end 
end 

 
def get_expedition
  puts ""
  puts "////////////////////////////////////////////////////////////////"
  puts ""
  puts "Select an Expedition from 1-109"
  input = gets.strip
  index = input.to_i - 1 
  if index.between?(0,108)
    expedition = AlpinePro::Expedition.all[index]
  puts ""
  puts "///  You Chose: #{expedition.name}  ///"
  want_description(expedition)
  elsif input == "exit"
  else 
    puts "Sorry I didn't understand that command"
    get_expedition
  end 
end 

  
def want_description(expedition)
  
  expedition.description = AlpinePro::Scraper.scrape_description(expedition.path)
  puts ""
  puts "Do You Want To Continue (Y/N)"
  input = gets.strip.upcase
  until ["Y","N","YES","NO"].include?(input)
    puts "Please Type Y or N"
    input = gets.strip.upcase
  end 
  if input == "Y"
    puts ""
    puts "/////////  Your Next Adventure: #{expedition.name}  /////////"
    puts ""
    puts expedition.description
  else 
    puts "you ended"
  end 
end 

end

**———————————————————————————————————————————– **

All in all, this first project was one of the most rewarding experiences in my life. (And I have had many). As tough as some parts were (scraping), I acquired many traits that will, with hard work, round me into the full-stack software engineer I dream of becoming. Persistance, Attention to detail, and a love for creating something from scratch.