Ruby is often thought of as a web language, due to the popularity of web frameworks like Ruby on Rails. Building websites and web backends using Ruby is certainly easy and the ecosystem is huge, making it ideal. But Ruby can of course be used for other purposes. In this article, we’d like to show you how to use a Twitter API and an example of how you can build a simple CLI tool for your terminal. This should be very simple for you to extend with other APIs!

How to get Twitter API Data

For this article, we’ll be using an API by PeerReach, which provides some interesting information about Twitter users, like the subjects they’re associated with, which groups they belong to, and which ranks they have in these groups. To get started with this API, head to rapidapi.com, and create an account if you haven’t already. Once you’re done, you’ll need to subscribe to the PeerReach API here. You can just press the “Subscribe to Test” button to begin.

Once you’ve signed up, we can play around with it. First, try it out straight from your browser. In the middle column, you can see the endpoint and the parameters you can pass (only the username in this case). Enter any username (perhaps yours?) and click on the “Test Endpoint” button on the top.

On the right of the page, you’ll see the API’s reply nicely formatted. Another way we can play around with the API is using curl. You most likely have this installed already on your computer. You’ll just need to copy your API key from RapidAPI’s site (you can easily copy it from one of the code snippets on the right). Then, you’d just run this command on your terminal:

curl -H "x-rapidapi-key: YOUR_API_KEY" 'https://peerreach-peerreach-subscription.p.rapidapi.com/user/lookup.json?screen_name=codinghorror'

The URL we’re querying can also be grabbed from one of the snippets by the way. Run this and you should quickly get the same response you saw when testing on the website.

The third way of testing is just copying the snippets provided for you! Look on the right and you’ll find a list of different languages and libraries to use. Let’s grab the “Ruby (Net::HTTP)” one:

You can straight up copy and paste this into a new twitter.rb file anywhere in your system, then run it using ruby from your terminal with ruby twitter.rb. You should see something like this:

$ ruby test.rb                                                                    
{"screen_name":"codinghorror","user_id":"5637652","lastupdate":"2020-07-31 15:20:03","followers":"275846","friends":"223","country":null,"gender":"Male","interests":[],"profiles":["webtech","blogger","games","humor","developer"],"peergroups":[{"topic":"developer","region":"ww","score":"4618","rank":"8"},{"topic":"webtech","region":"ww","score":"9990","rank":"308"},{"topic":"blogger","region":"ww","score":"2351","rank":"1822"},{"topic":"games","region":"ww","score":"3450","rank":"4047"}],"subjects":[{"name":"uber","subject_id":"603641","score":"2","assign_date":"2015-06-18 01:07:33"},{"name":"dnc","subject_id":"885429","score":"1","assign_date":"2016-11-16 01:01:10"},{"name":"nintendo","subject_id":"22330","score":"1","assign_date":"2016-11-12 01:01:15"},{"name":"products","subject_id":"722189","score":"1","assign_date":"2016-04-15 01:01:18"},{"name":"halloween","subject_id":"601068","score":"1","assign_date":"2015-09-22 01:01:17"},{"name":"storage","subject_id":"1016169","score":"1","assign_date":"2015-08-13 01:07:07"},{"name":"nimoy","subject_id":"1434142","score":"1","assign_date":"2015-02-28 01:17:19"},{"name":"windows","subject_id":"601162","score":"1","assign_date":"2014-10-02 01:02:50"},{"name":"windows10","subject_id":"1182952","score":"1","assign_date":"2014-10-02 01:02:49"},{"name":"watson","subject_id":"635196","score":"1","assign_date":"2014-09-23 01:02:55"},{"name":"minecraft","subject_id":"22088","score":"1","assign_date":"2014-09-10 01:03:11"},{"name":"potato","subject_id":"708353","score":"1","assign_date":"2014-07-08 01:02:59"},{"name":"swift","subject_id":"602033","score":"1","assign_date":"2014-06-03 01:09:37"},{"name":"wall","subject_id":"600118","score":"1","assign_date":"2014-05-20 01:08:22"},{"name":"bridge","subject_id":"657510","score":"1","assign_date":"2014-05-01 01:08:06"},{"name":"fools","subject_id":"704967","score":"1","assign_date":"2014-04-01 01:07:49"},{"name":"horror","subject_id":"21302","score":"1","assign_date":"2014-03-01 01:06:26"},{"name":"norway","subject_id":"22353","score":"1","assign_date":"2014-02-19 01:07:48"},{"name":"oregon","subject_id":"22426","score":"1","assign_date":"2014-02-16 01:05:55"},{"name":"mac","subject_id":"602285","score":"1","assign_date":"2014-01-26 01:05:11"}]}

The format is obviously not formatted since it comes compressed from the server. We can fix that with a few changes:

require 'uri'
require 'net/http'
require 'openssl'
require 'json'

url = URI("https://peerreach-peerreach-subscription.p.rapidapi.com/user/lookup.json?screen_name=codinghorror")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Get.new(url)
request["x-rapidapi-host"] = 'peerreach-peerreach-subscription.p.rapidapi.com'
request["x-rapidapi-key"] = '6b9ae6ac49mshd298c37aadd8671p1b1cc2jsn2a002937834e'

response = http.request(request)
json = JSON.parse(response.read_body)

puts JSON.pretty_generate(json)

Notice the highlighted lines. We require the JSON library, parse the response, and then use JSON.pretty_generate to generate a pretty print of the object.

Example: Twitter API using Ruby and Thor

How To Create A CLI Tool for Twitter with Ruby

We’re going to be building a CLI utility using the mentioned API and Ruby. Since we’re using Ruby, you’ll of course need that. You most likely already have it installed, otherwise, refer to the official documentation to get started. We’ll also be using bundler and other gems. Install bundler by running gem install bundler (which, again, you might already have installed).

Since we’re going to be building a CLI tool that depends on an API, we’re also going to need access to that.

There, you can just select the basic plan, which is free up to 2400 requests a day (more than enough for this article). You might be required to enter your credit card information, but you won’t be charged for this.

Starting Small

Let’s start by building the basic skeleton for our CLI app. We could perfectly well build the whole app from scratch, but this would be like building an entire website using Ruby from scratch (why not use Ruby on Rails?). Thus, we’ll be using a gem called Thor, which is built precisely for this purpose: to build CLI programs. It provides all the basic tools to get input, options, and other goodies. Since we’re also going to need another gem, let’s just use bundler to manage them. Create a new directory called twitter-cli, then run bundle init in there. This will create a basic Gemfile for you. Edit that file and make sure it looks like this:

source 'https://rubygems.org'

gem 'excon'
gem 'thor'

Run bundle install to install both our gems. We already explained what Thor is all about, but what about Excon? Excon is just a simple HTTP library, which will simplify the use of our API a bunch.

Next, create a new cli.rb file in the same directory, and add this:

require 'bundler/setup'

Bundler.require(:default)

class Twitter < Thor
  def self.exit_on_failure?
    true
  end

  desc 'username', 'Fetch data from a Twitter user'
  def username(username)
    puts "Hi #{username}"
  end
end

Twitter.start

The first two lines leverage our Gemfile to load our gems. This way, if we were to add more and more gems, we only need to edit our Gemfile and run bundle install again, and that’s it. Next, we create a new class which inherits from Thor. This gives our class CLI capabilities. The first method we define might be a bit confusing. Thor’s default behavior is to return exit code zero when it doesn’t find an option or method. This is not really what we’d want, as an error should return a non-zero exit code. This fixes that. Below that, we define a new method called username, which takes one argument and just prints a string. Above it, we define some short documentation for it. Finally, we “start” our CLI program.

We can now run it to see what happens. If you go to your terminal and just run ruby cli.rb you should get this:

$ ruby cli.rb                  
Commands:
  cli.rb help [COMMAND]  # Describe available commands or one specific command
  cli.rb username        # Fetch data from a Twitter user

Pretty cool huh? We got all of this documentation for free. You can even try ruby cli.rb help username to get:

$ ruby cli.rb help username    
Usage:
  cli.rb username

Fetch data from a Twitter user

Again, all of this for free. If you now run ruby cli.rb username rapidapi you should get the string we defined:

$ ruby cli.rb username rapidapi
Hi rapidapi

Fetch some data

Now we’re ready to fetch some actual data. At RapidAPI, you should be able to see your API key. Take note of this, as you’ll need it now. You might not be able to directly copy it from the center column, but you can quickly grab it from the snippets on the right:

Add some extra code above your class in cli.rb:

require 'json'

def fetch(username)
  encoded_username = URI.encode_www_form_component(username)

  response =
    Excon
    .get(
      'https://peerreach-peerreach-subscription.p.rapidapi.com/user/lookup.json?screen_name=' + encoded_username,
      headers: {
        'x-rapidapi-key' => 'YOUR_API_KEY'
      }
    )

  JSON.parse(response.body)
end

This is just a simple method that fetches the endpoint you see on RapidAPI (check that snippet above) and converts the response from JSON to a Ruby Hash. If you’re planning on sharing this (or any other) utility, make sure you don’t hard-code your API key in there!

The rest of the work is pretty simple. We need to modify our original username method to fetch the user’s data and display it:

def username(username)
  data = fetch(username)

  puts "Screen name: #{data['screen_name']} (twitter.com/#{data['screen_name']})"
  puts "Last Update: #{data['lastupdate']}"
  puts "Followers: #{data['followers']}"
  puts "Following: #{data['friends']}"

  puts

  puts "#{data['profiles'].count} profiles"
  data['profiles'].each do |profile|
    puts " - #{profile}"
  end

  puts

  puts "#{data['peergroups'].count} peer groups"
  data['peergroups'].each do |peergroup|
    puts " ##{peergroup['rank']} #{peergroup['topic']}"
  end

  puts

  puts "#{data['subjects'].count} subjects"
  data['subjects'].each do |subject|
    assign_year = Date.parse(subject['assign_date']).strftime('%Y')
    puts " #{subject['name']} assigned in #{assign_year}"
  end
end

That is a lot of puts. No way around it, as we’re displaying a bunch of information at the same time (although there are some options, like using templates with ERB). You can get some help understanding the API’s response format using RapidAPI’s tester. Now, you should be able to run ruby cli.rb username codinghorror (or whatever username you like!) and see something like this:

$ ruby cli.rb username codinghorror
Screen name: codinghorror (twitter.com/codinghorror)
Last Update: 2020-07-31 15:20:03
Followers: 275846
Following: 223

5 profiles
 - webtech
 - blogger
 - games
 - humor
 - developer

4 peer groups
 #8 developer
 #304 webtech
 #1822 blogger
 #4038 games

20 subjects
 uber assigned in 2015
 dnc assigned in 2016
 nintendo assigned in 2016
 products assigned in 2016
 halloween assigned in 2015
 storage assigned in 2015
 nimoy assigned in 2015

[...]

Feel free to adjust this output to your liking. What we’d like to suggest is to align the output so that it’s easier to read. See how the peer groups and subjects are difficult to see due to the alignment? This is a simple fix. All we need to do for the peer groups, for example, calculate the maximum length of the “rank”, so that we can pad the others. Let’s do that:

puts "#{data['peergroups'].count} peer groups"

length = data['peergroups'].map { |d| d['rank'] }.map(&:length).max

data['peergroups'].each do |peergroup|
  puts " ##{peergroup['rank'].ljust(length)} #{peergroup['topic']}"
end

Notice the highlighted lines. We’ve added a calculation of the maximum length of the rank attribute, then we use the ljust method on a string, which pads the string to the length we specify. Running our command again yields this result:

4 peer groups
 #8    developer
 #304  webtech
 #1822 blogger
 #4038 games

Much better! Let’s do the same for the subjects:

puts "#{data['subjects'].count} subjects"

length = data['subjects'].map { |d| d['name'] }.map(&:length).max

data['subjects'].each do |subject|
  assign_year = Date.parse(subject['assign_date']).strftime('%Y')
  puts " #{subject['name'].ljust(length)} assigned in #{assign_year}"
end

More or less the same as before. Run your command now. You should see a much nicer and easier to read output.

JSON as Output

Being a CLI utility, we can’t avoid the power user features. What if we just need the JSON output? Sure, we could directly call the API using curl or something like that, but we can also add an option to our program easily, using Thor. Right before our username method, add this:

desc 'username', 'Fetch data from a Twitter user'
method_option :json, desc: 'Return JSON'
def username(username)

The method_option allows you to add modifiers to the command. These are, obviously, optional, as the name suggests, and they magically appear in the documentation:

Usage:
  cli.rb username

Options:
  [--json=JSON]  # Return JSON

Fetch data from a Twitter user

Pretty nice for such little effort. Now we need to use this modifier in our code, let’s add some extra lines:

def username(username)
  data = fetch(username)

  if options[:json]
    print data.to_json
    return
  end

# ...

We’ll fetch the data as normal, then check if the json modifier is set. If it is, then just print the data as JSON immediately, then return to exit the program. You can try this by running ruby cli.rb username codinghorror --json. You’ll get the straight JSON response, which you could potentially pipe into utilities like jq.

Handling Edge Cases

What if the username we search for doesn’t exist? Right now we’ll just get an error because our code doesn’t handle this. Let’s do that. The API returns an empty array [] when the user isn’t found. Let’s handle this case in our method:

if data.empty?
  puts 'User not found'
  exit 1
end

puts "Screen name: #{data['screen_name']} (twitter.com/#{data['screen_name']})"
puts "Last Update: #{data['lastupdate']}"
puts "Followers: #{data['followers']}"
puts "Following: #{data['friends']}"

If the data variable is empty, we’ll just output a message and exit with code 1 (as a program should in an error!). Note that the output should better go to the STDERR output device if it’s an error, but we’ll leave that as an exercise to the reader.

Extra Credit

Right now we need to call ruby cli.rb to even start our program. Ideally, we should just be able to call twitter-cli straight from our terminal. We can achieve this very simply. First, we need to rename our program. Just rename the file directly, removing the .rb extension and all. Then, add a header to our file to tell the OS how to run it:

#!/usr/bin/env ruby
require 'bundler/setup'

Bundler.require(:default)

# ...

Lastly, we need to make the file executable: chmod +x twitter-cli. Now, we can just call ./twitter-cli to invoke our program. If you want to avoid the ./ prefix, you’d need to put the working directory in your PATH environment variable. You can do this for your current terminal like so:

export PATH="$PATH:`pwd`"

Now you can run the command twitter-cli directly!

Conclusion

We hope this article gave you a useful introduction to using Ruby for CLI programs. Ruby is a powerful language and it can allow you to write any type of program. Its ecosystem also makes it very easy for you to get started. We’d like to recommend trying out the paint gem to output colors and bold text. Use this as an exercise to make the output of your program even easier to read! You can also extend your program using more APIs from RapidAPI, and you don’t necessarily need to use Twitter APIs. You can perhaps combine multiple different ones (using a single API key) to make a very useful

5 / 5 ( 1 vote )
Ian Murray

Ian Murray is a Backend and Frontend Systems Engineer. He specializes in Web and iOS development with an emphasis on Ruby on Rails. GitHub | LinkedIn

Share
Published by

Recent Posts

How to use WordPress with React (WordPress React API Tutorial)

WordPress WordPress claims to be, "the world’s most popular website builder" based on the statistic…

1 week ago

How to use an E-mail API with JavaScript

Introduction This tutorial will show you how to use JavaScript to call an API to…

1 week ago

How to Document your API

Documentation is an essential part of any API, and this is what we're going to…

2 weeks ago

How to use the Google Play Store API to Search App Details

Modern mobile phones are replacing many different devices and services. And the bigger part of…

2 weeks ago

The 10 Chrome Flags That Can Transform Your Browsing Experience

Being one of the most popular web browsers out there, Google Chrome has a reputation…

3 weeks ago

How to use the Google Translate API with Ruby on Rails

Google, mostly known for its search engine and extremely popular email service, Gmail, also provides…

3 weeks ago