My first Ruby CLI gem! travel_inspiration
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!
- There are 12 themes
- Upon selecting a theme, the theme page displays tips, blogs, videos, related tours, and 6 recommended destinations.
- 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
My Ruby Gem provides:
- A CLI with an interface that users can interact with,
- Scrape data from Lonely Planet’s website,
- 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).
- The Ruby Gem is a travel themed application,
- 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/HTMLrequire ‘open-uri’
require ‘nokogiri’#all other file dependenciesrequire “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.
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:
- #self.list_theme_names
RETURN the result from scraping, listing 12 themes - #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.
Destinations.rb
The purpose of this file is to: SCRAPE each THEME website for TOP DESTINATIONS.
This is achieved through two class methods:
- #self.list_destination_names(theme_name)
RETURN the result from scraping, listing 6 destinations - #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
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:
- #initialize
Create new instance of Country - #country_info
* Return both seasonality and country information - #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
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:
- #start
INITIATE gem - #list_themes
- Welcomes user and explain the purpose of this gem.
- Prompt user to select a TRAVEL THEME
- Display 12 THEMES (from themes.rb) - #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 - #list_destinations(theme_name)
- Display 6 COUNTRIES (from destinations.rb)
- Prompt user to select a COUNTRY - #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 - #country_details(chosen_country)
- Display selected COUNTRY INFORMATION (from country.rb)
- Prompt user to select another COUNTRY or EXIT to end the program - #goodbye
Display message to indicate end of CLI Gem
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.
- Create a RubyGem account
- Update my Gem’s version lib/version.rb to 0.1.0 , following semantic versioning patten.
- 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 rubyrequire “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!