Harry Potter and the CLI Project

About the Project

For the past month as a student in Flatiron’s Software Engineering program, I have been studying Ruby. We’ve covered a lot of material, but the most useful was how to create CLIs and utilize APIs to enhance their capability. For our final project in the Ruby module, we were tasked with creating an application using Ruby that would access external data. Additionally, we were required to have our data go one level deep — meaning that the user must make a choice and then be given details based on the choice they made.

Project Planning

The most important part of any project is planning. It can be overwhelming to try to begin creating something without a plan in place, and a project like this is no exception. The first thing I had to accomplish was to decide what I wanted my project to do and what API was accessible to help me achieve that. I decided to create a program that gives the user a list of Harry Potter characters (provided from said API) and, upon selection, would tell the user the name of the actor who played them in the films. I was able to find an API to help me do just that — HP-API. Now that I had the general idea of the functionality I wanted to accomplish, I had to figure out what smaller steps were needed to get me there. I decided I wanted my project to look something like this:

Once I was done planning, it was time to roll up my sleeves and begin building.

Setting Up the Environment and Gemfile

Before getting into anything else, I needed to set up my environment and Gemfile. In the Gemfile, I listed the gems I would be using (most importantly rest-client and JSON, for handling the API data) and ran bundle install.

a list of gems utilized in my application

Next, I set up my environment.

In my environment.RB file, I required the gems and files that would be necessary to make the program work.

You’ll notice the first section resembles the Gemfile — any gem we’re using needs to be in the Gemfile as well as required in the environment file. I also used require_relative to load in the files I’ll be using to make the program run. By requiring these in the environment file (which will be loaded into our run file — more on that later!) we are telling the program that these files and gems must be referenced throughout the program.

Accessing Data Using an API

You can’t build a program centered around data from an API without loading that data in! I’ll be honest — this was by far the most difficult part of the project for me. I really struggled with it but with a little help from my cohort lead, I realized I was much closer than I realized and was simply overthinking it. It was so rewarding once I got it to work!

I used rest-client and JSON to gain access to and parse the data from the API.

I used rest-client, a gem created specifically for situations like this, to grab the data from the API. However it’s not the easiest to work with in the state that that data comes in, so I used JSON to parse it to make it easier to work with.

Organizing the Data

The good news is that HP-API contains a ton of data — houses, patronuses, you name it. The bad news is, I don’t need to use all of that data! Which means I need to clarify in my program which types of data I want to use. To do this, I fired up the file that contained my Characters class.

In this class, I tell the program to store the character name and actor data of each character and store it in a class variable, @@all.

As you can see in the code above, I told the program that it needs to store the character name and actor information for each character in a class variable called @@all.

Doing Something With the Data

Once I had my environment and data set up, I was very excited because it was time to do something with it! Creating the CLI was the easy and fun part for me. It allows for a lot more creativity than any other part of the project and starts to build something I can see in action. Using the steps of functionality I listed while project planning, I was able to create a CLI that represented my vision.

  • Greet the user

First things first, I have to create a class! I named mine Cli to keep things simple. Greeting the user was pretty simple — I used puts to greet the user and explain what the program would do. I also added an empty puts on either end so that there would be an empty line above and below for the sake of aesthetics. I repeated this pattern often throughout my code so it all looks consistent.

  • Load the data

Next is making sure the data we spent so much time loading in actually makes it into the program! This will all be happening behind the scenes, so the user won’t see anything. However, this can sometimes take a few seconds. I don't want the user to think there’s an error while it loads, so I added a message to let them know to standby while it is loading. Once it has loaded, we want to move on to the next step of providing a menu of options to the user. That will be the next code written, and it will be named “choose_menu”, so we will call that method here and the program will know to move on. And of course, we end our “begin” method!

  • Provide a numbered list of characters via the API and ask the user which they would like to select

This step will tackle two methods — one we will call “choose_menu”, and one we will call “list_characters”. The purpose of the choose_menu method will be to display a message instructing a user to select a character and move on to the list_characters method, which will list the character options available as provided by our API.

In list_characters, we set up the program to get the data and index + 1. It’s important to add one to the index since indexes start at zero, but we want our list to start at one. It then lists all of the indexes, a period, and the character’s name — all of which has been grabbed from our handy dandy API.

  • Get the input from the user

Next, we need to allow the user to make a selection from the list. After defining a method called “get_input”, I used puts to display a message instructing them to enter the number of the character they want to choose and set a variable named “input” equal to “gets.chomp”, which gives the ability for the user to enter information.

From there, we need to process that information so that our program knows how to proceed. I did this using a method I named enter_choice. Within this method, I used if, elsif, and else statements to direct the program in the right direction depending on the user’s choice.

Ideally, the user will choose a selection that is a valid number. If it is a valid number (meaning, the number the user entered is between 1 and the last number of the list), we want to subtract one from the number to get the index. After that, we will then go through all of our instances in the Characters class to get the one with the index we want. From there, we will go to a to-be-created class called results(character). All of this code will be housed in the if part of our conditional.

Next, we move on to our “elsif” statement, stating that if our user enters the word “exit”, the program will reroute to a method for exiting we will create later called “exit_program” that will end the program.

And finally, we need to address the possibility that a user may not enter something that matches either. If this happens, we want to provide an error message and ask them if they’d like to try again. We’ll reference this in our “else” statement, and then create a method called “error” that provides that method, gets the user's answer, and either exits the program or reroutes back to our choose_menu method to provide the list and start again.

As you will see, the previously mentioned “exit_program” method is starting to pop up quite a bit — which means we need to actually define that method! This method is a super simple one — it simply displays the text “Goodbye!” and exits the program. While I could have just used “exit” in any instance where I wanted the program to end, I wanted to create reusable code to keep things efficient, easy, and not repeat my code unnecessarily.

  • Return the character and actor information

Once I completed the previous step, it was time to finally provide the user with an answer! Taking the character selected from the previous step, we will bring that into a new results method. I wanted every result to read as “[Character’s name] is played by [Actor’s name].” So to do that, I used puts and interpolated the character and actor names where appropriate.

  • Ask the user if they would like to continue or exit

I didn’t want the user to only have one opportunity to use the program so it was important to me that after returning the result, the program asks the user if they’d like to select another character.

  • Repeat the process if the user would like to continue

We use a similar if/elsif/else code as the one we previously used to either direct the user back to the list of characters to choose from or exit the program, depending on their decision.

Side Note: You may notice .red, .green, and .neon after a few instances of puts. I wanted to add some color to my project to make it more aesthetically pleasing and easier to read, so I used a gem called “color_text” (which you’ll see in the Gemfile!) to colorize certain instances of text.

Making It Runnable

Now that we’ve loaded in the data and written the CLI portion, there is one small but very important step left — giving it the ability to run! I created an extensionless executable file called run in my bin folder and filled in the following code:

On line 1 I’m stating which language it should be read in since there is no extension to do so. On line 3, I’m stating that the environment file we built before is required to run this file. And finally, on line 5 I’m telling it to run by creating a new instance of the class Cli, and running the “begin” method we defined within it.

Making It Run

And at long last, the program can run! Below you’ll see an example of what running it looks like:

Software Engineering Student @ Flatiron School by day, TV/Film Script Analyst by night. NYC via FSU.