My first Ruby CLI gem! travel_inspiration

Lonely Planet’s Travel Inspirations

In brainstorming for the theme of my Ruby CLI gem, I toyed with a few ideas. I love Travel / Food / Arduino projects, and all three themes have plenty of resources available for scraping. So I asked myself:

What problem will you like to solve?

I decided I will tackle the problem: Where should I explore next?

For some people, they have a bucketlist of places they want to visit before they die. But for others who don’t have a list in mind, how will they decide which countries they want to visit next?

For my parents, I noticed they often plan trips based on a theme. For architecture and history, they decided to visit the UK and France. For sakura blossom viewing, they planed a Spring trip to Japan. And when they wanted a more active trip, they visited my sisters in Seattle and South California.

Going through Lonely Planet’s website, I noticed there’s a Travel Inspiration section on the homepage!

  1. There are 12 themes
  2. Upon selecting a theme, the theme page displays tips, blogs, videos, related tours, and 6 recommended destinations.
  3. Selecting a destination, the country page displays top experiences, survival guide filled with useful information and related articles!

I decided to name my Ruby CLI Gem: travel_inspiration

Walkthrough of my code

My Ruby Gem provides:

  1. A CLI with an interface that users can interact with,
  2. Scrape data from Lonely Planet’s website,
  3. Provide three level of data! First, the CLI displays 12 travel themes. User will select a theme, and the top 6 destinations will be shown (second level). User can then select a destination to view more information, and the CLI will display the Best Time to Visit, country introduction summary, and more details about the country (third level).
  4. The Ruby Gem is a travel themed application,
  5. Following the Single Responsibility Principle (SRP), I split my code up to four files:
  • cli.rb : code handling CLI display logic and user input
  • themes.rb : code scraping first level of data, displaying 12 themes for user selection
  • destinations.rb : code scrpaing second level of data, displaying 6 destinations for user selection
  • country.rb : code scrpaing third level of data, displaying country information and seasonality data.

You can also view my Github repo code here! I alsopublished my gem to RubyGems, you can find the travel_inspiration gem here!


How do I create a Ruby Gem?

First step to creating a Ruby gem is, well, learning how to create a Ruby Gem! A quick Google search returns over 1million results!

In Avi’s video walkthrough, he mentioned we can use Bundler to create a gem. Following the Bundler guide, I created a Ruby gem:

bundle gem travel_inspiration

This command pre-populated a basic directory with:

- bin
- lib
.gitignore
travel-inspiration.gemspec
Gemfile
License.txt
Rakefile
README.md

What files are pre-populated by bundler gem?

/bin directory

In the bin directory, there are two files: console , setup

Following the pre-populated comments in the console file, I uncommented the line require “pry” as I will be using Pry during development.

/lib directory

In the lib directory, there is a sub directory /travel_inspiration

I’m treating this as my app folder, where all ruby files will go. The bundler gem has also created version.rb file where I will note version changes of each gem release.

module TravelInspiration
VERSION = “0.1.3”
end

Another pre-populated file was travel_inspiration.rb . For a smaller, single-purpose app, you can put all code in this file.

However, I know I will be following the Single Responsibility Principle and creating different .rb files for each of my content scraper and the CLI logic from my planning. So I simply used this file to define all dependencies:

#OpenURI is a wrapper for Net::HTTP, Net::HTTPS and Net::FTP
#Nokogiri parses and searches XML/HTML
require ‘open-uri’ 
require ‘nokogiri’
#all other file dependencies
require “travel_inspiration/version” 
require “travel_inspiration/themes”
require “travel_inspiration/cli”

Other pre-populated files

  • README.md this is the file that will display on my Github repo by default! In this file, I have included a quick introduction to the gem, how users can download the gem, how developers can check out the repo, contribution guidelines and license information!
  • .gitignore defines paths to ignore in git versions
  • Gemfile define a project gems dependency, deals with dependencies version and lock versions to use the same environment across developers & deployments.

When using a Gemfile, bundler is resolving each gem’s gemspec and gather them all to find out what conflicts might occurs and what which most up to date version of a gem is compatible among all your dependencies.

Taking a closer look, I noted Gemfile includes gemspec as path to other gem and library dependencies.

source “https://rubygems.org"
# Specify your gem’s dependencies in travel_inspiration.gemspec
gemspec
  • travel_inspiration .gemspec is the way to define a ruby gem, publish it on rubygems.org and install them with the gem command.

In this file, there’s many pre-populated fields and comments. This is where I defined attributes of my gem such has the gem name, authors details, gem summary & description, executables, require paths and dependency versioning.

travel_inspiration.gemspec

It was important to note that there are two types of dependencies: runtime and development.

Development dependencies are useful for other developers who wants to make modifications to your gem, if your gem is open to contributions. RubyGems guide notes:

When you specify development dependencies, another developer can run gem install — dev your_gem and RubyGems will grab both sets of dependencies (runtime and development). Typical development dependencies include test frameworks and build systems.

I have included bundler, rake, and pry as development dependencies.

Runtime dependencies are what your gem needs to work for end users who installs your gem!

I have included Nokogiri and colorize as development dependencies.

Colorize is a Ruby gem developed by Michał Kalbarczyk, an engineer based on Poland. It is a:

Ruby gem for colorizing text using ANSI escape sequences. Extends String class or add a ColorizedString with methods to set text color, background color and text effects.

I included this gem in my CLI logic, highlighting questions requesting user input / input / returned results. This gave me flexibility to design the presentation view of my CLI.


Time to create my Ruby CLI gem

To start, I will need to scrape the THEME data from the Lonely Planet’s website. I will create a new file for each purpose.

Themes.rb

The purpose of this file is to: SCRAPE Lonely Planet homepage’s Travel Inspiration THEMES.

This is achieved through three class methods:

  1. #self.list_theme_names
     RETURN the result from scraping, listing 12 themes
  2. #self.scrape_themes
    - Access Lonely planet homepage through open-uri
    - Parse webpage to XML using Nokogiri
    - Select the corresponding themes carousel slides
    - Loop through themes, creating a new instance per theme assigning name and theme URL
    - Push each theme into an array: themes_list
    - Return themes_list array

3. #self.url_for_theme_name(theme_name)
 Create the URL for each theme, used to scrape the top 6 destinations in destinations.rb

In hindsight, I didn’t need the #self.url_for_theme_name(theme_name) method since I have already stored theme URLs in each instance for step 2.

themes.rb

Destinations.rb

The purpose of this file is to: SCRAPE each THEME website for TOP DESTINATIONS.

This is achieved through two class methods:

  1. #self.list_destination_names(theme_name)
     RETURN the result from scraping, listing 6 destinations
  2. #self.scrape_destinations(theme_name)
    - Access selected THEME webpage through open-uri
    - Parse webpage to XML using Nokogiri
    - Select the TOP DESTINATIONS section
    - Loop through destinations, creating a new instance per destination assigning name, continent and destination URL
    - Push each theme into an array: list
    - Return list array
destinations.rb

Country.rb

The purpose of this file is to: SCRAPE each COUNTRY website for seasonality information and country information.

This is achieved through five instance methods:

  1. #initialize
     Create new instance of Country
  2. #country_info
     * Return both seasonality and country information
  3. #url
     Create the URL for each country

In hindsight, I didn’t need the this method since I have already stored country URLs in each instance in destinations.rb

4. #scrape_info
 * Access selected COUNTRY webpage through open-uri
 * Parse webpage to XML using Nokogiri
 * Select the COUNTRY INTRODUCTION section
 * Assign summary quote and country information to each instance

5. #scrape_season
 * Access selected COUNTRY webpage through open-uri
 * Parse webpage to XML using Nokogiri
 * Select the SURVIVAL GUIDE — WEATHER section
 * Assign high season, low season, shoulder season to each instance

country.rb

CLI.rb

Finally, all the logic are tied together in cli.rb

The purpose of this file is to: DEFINE the logic of what is displayed in the CLI based on user input

This is achieved through seven instance methods:

  1. #start
     INITIATE gem
  2. #list_themes
    - Welcomes user and explain the purpose of this gem.
    - Prompt user to select a TRAVEL THEME
    - Display 12 THEMES (from themes.rb)
  3. #select_theme(theme_arr)
    - User will select a theme with number 1 -12
    - Control flow based on user input: 
    - IF the input is valid, the corresponding 6 TOP DESTINATIONS will be displayed 
    - IF the input is valid, prompt user to select a valid theme
  4. #list_destinations(theme_name)
    - Display 6 COUNTRIES (from destinations.rb)
    - Prompt user to select a COUNTRY
  5. #select_country(country_arr)
    - User will select a theme with number 1–6
    - Control flow based on user input: 
    - IF the input is valid, the corresponding COUNTRY INFORMATION will be displayed 
    - IF the input is valid, prompt user to select a valid theme
  6. #country_details(chosen_country)
    - Display selected COUNTRY INFORMATION (from country.rb)
    - Prompt user to select another COUNTRY or EXIT to end the program
  7. #goodbye
     Display message to indicate end of CLI Gem
cli.rb

How to publish a gem on RubyGems.org

Now that I have completed my Ruby CLI gem, it is time to publish it! A quick Google search led me to RubyGems Guides to publish a gem.

  1. Create a RubyGem account
  2. Update my Gem’s version lib/version.rb to 0.1.0 , following semantic versioning patten.
  3. Use gem command and enter my RubyGems account credentials:
gem push travel-inspiration-0.1.0.gem

Voila! I have published my gem successfully!

However, after publishing my gem, I realized a user who downloads my gem doesn’t have a quick way to start the gem. This led me to create a new file in /bin that will create a new instance of the CLI gem

bin/inspire_me

The purpose of this file is to: ENABLE user to start the CLI with a simple command:

inspire_me

This is achieved by calling the method start, which creates a new instance of CLI class and display the first user prompt.

#!/usr/bin/env ruby
require “bundler/setup”
require “travel_inspiration”
TravelInspiration::CLI.new.start

travel_inspiration.gemspec

It was also important to update my gemspec file to specify the /bin directory of my inspire_me executable file!

spec.bindir = “bin”
spec.executables = [“inspire_me”]
spec.require_paths = [“lib”]

Travel_inspiration Ruby Gem!

Finally, my CLI gem is ready to use! Go on, try it for yourself 😃

You can install the travel_inspiration gem by typing the following prompt in terminal:

gem install travel_inspiration

Get inspired for your next trip!