What to Watch? - CLI Data Gem Project

Posted by camneu37 on January 9, 2018

Well I’ve made it through the first section of Flatiron School’s Online Web Developer Track and have completed my first portfolio project! For this first project, I built an application that provides a Command Line Interface (CLI) to an external data source, with the CLI being composed of an Object Oriented Ruby application. I decided to build an application which would allow users to view in list form all Showtime series available for for streaming on Showtime Anytime, and then view further information (summary of the show, number of seasons/episodes available) on the series’ of their choosing. I chose this topic because (like most people these days) I love finding new series to binge watch. I particularly like Showtime’s content (Shameless and Ray Donovan are my faves), but I really hate all the incessant scrolling and clicking. I could spend an hour just scrolling around and clicking into different show details, trying to find a new show to watch. So I decided, why not build a program that would allow me to get this information more quickly?

I started my project a bit differently than I had seen done in the video walkthrough lectures. In the video, the instructor started by building out their application, using hard coded data to get the program working, and waited to actually scrape the site once the rest of the program was working. I decided for my project, I would work on the scraper portion first. As I dove deeper into inspecting Showtime’s site, I found it was a bit more difficult than I had originally expected, so I wanted to make sure I could actually pull out the data I needed before writing the program that would depend on that data. I figured this would save me some time and effort in case I had to scrap the Showtime series idea and choose a new site all together.

Thus the first bit of code I wrote for my program was defining a Scraper class. I then defined two instance methods within that class - one for getting the full list of Showtime series and one for getting series specific details from each of the series’ pages on the site. I utilized the open-uri and nokogiri gems to open, scrape from, and parse the html data from the Showtime website. I utilized Pry a lot here as well, so that I could test out different selectors and figure out which would allow me to pull the text I required - series name, link to series’ page, series summary, and series’ episode details. Once I determined the appropriate selectors, I was able finish writing my methods. The ‘scrape_series_list’ method would return an array containing hashes for all series from the site, each hash containing that series’ name and the link to that series’ page on the Showtime site. The ‘scrape_show_details’ method would return a hash containing the series’ summary and the series’ episode information. I would utilize the returns of these methods in my Shows class, when creating new objects and adding attributes to those objects.

Once I had all the code working properly in Scraper class, I moved on to coding my Shows class. This class would be responsible for creating separate instances for each Showtime series and also for keeping track of all series instances created. I started out by defining the class and adding some attribute accessors, for attributes I knew would need getter and setter methods. I then set up the ‘initialize’ method that would ensure every new object was added to the class variable ‘@@all’ whenever a new object was creating, allowing the Shows class to keep track of all instances of itself. I made a class method ‘series_list’ to expose the return data of the Scraper instance method ‘scrape_series_list’ so that it could be more easily called and utilized within the Shows class. I then created a class method ‘create_from_list’, which iterated through that data and created a new Shows instance for each series in the array. I quickly discovered in testing this method that duplicates of shows would be created if for some reason the series was present twice on the Showtime site (this occurred for a handful of series because there’s a top section of featured series and then below is the all series section, and unfortunately I could not figure out a selector to get only the ‘all series’ section when scraping). To prevent duplicate objects from being created, I defined a ‘find_by_name’ class method which would iterate through the Shows instances saved in the ‘@@all’ class variable (i.e. it would iterate through all objects that had already been created), and return the object with a name attribute equalling the string passed passed in as an argument to the method. I then utilized this ‘find_by_name’ method within the ‘create_from_list’ method, using it in a conditional statement, so that a new object would only be created if there was not another existing object having the same name. The ‘create_from_list’ method also utilized a separate class method ‘add_show_attributes’, which called on the Scraper instance method ‘scrape_show_details’ and set the show summary and episode details equal to the Shows instance variables ‘@about’ and ‘@episodes’. I defined a few other class methods within the Shows class - an ‘all’ method to expose the data saved in the class variable ‘@@all’ (i.e. all Shows objects), a ‘list_shows’ method that accepted an argument of some parameters for which shows the user would like to view (i.e. shows starting with the letters A-F) and then printed the list of shows meeting those parameters with their corresponding number within the full list (which would be used for selecting and viewing further details of a particular show), and a ‘sorted_shows’ method which just sorted the full show list (i.e. all Shows instances) alphabetically and assigned numbers to each show in ascending order.

One challenge I ran into while coding the Shows class, was a bit of confusion on my end with the file load order. I originally tried calling the Shows methods (some of which collaborated with methods for the Scraper class) within the Shows class file, which resulted in Uninitialized Constant Errors. I could see all the methods and classes were defined within my class files and I had required all the class files in my environment file, so I was a bit confused as to what the problem was. After doing a bit of research online and spending more time combing through each file in my program to see where and how I had required each file, I discovered the problem. I needed to call the methods within my bin file rather than within a class file, as I had not required relative class files within each class file. My bin file had the necessary line to require the relative what_to_watch.rb file, which contained the code requiring the relative environment file, which contained the code requiring all of the individual class files. Once I started calling all my methods from the bin file, everything was working as it should! This was one of the biggest pain points I encountered during the course of the project, and it was very rewarding to figure it out all on my own (well, with the help of google, but without asking an instructor).

Once I had tested and had all the methods working properly in the Shows class, I began work on my CLI class, in which I defined the methods that related to the user’s interaction with the program. I started out by defining a ‘start’ method, and at first built it out to include everything I’d want the program to do. Once I had an idea of how I wanted my program to run, I did a lot of refactoring and moved code from the ‘start’ method out into individual methods, as appropriate. I created a ‘show_list_info’ method which printed the main instructions for the program - presenting the user with the different options of viewing the list of series, whether they wanted to see series starting with A-I, J-R, etc. I then created a method for showing the selected series list, based on the input from the user. This method, the ‘show_list’ method, would take in an argument of the user’s input and, based on that input, print a list of series. This method utilized the Shows class method ‘list_shows’ to print the lists. I added a loop at the start of this method, to handle any invalid input. I defined the method so that until the input matched one of 5 conditions, an ‘invalid entry’ message would print and the user would be prompted to re-enter a valid selection. I then defined a ‘ask_more’ method which would print a message to the user asking them how they would like to proceed, and prompting the user for further input. I then defined a method called ‘do_more’ that took in that input as an argument and then, based on that input, either showed the details of a specific series, showed the user the original instructions again, or, in the case the user entered ‘exit’, thanked the user for using the program and terminated the program. Similar to the ‘list_shows’ method, I started this method with a loop that would cause an ‘invalid entry’ message to appear while prompting the user to re-enter their input, until their input met specific conditions. I actually made a separate method called ‘invalid’, which printed the invalid entry message, so that I could simplify the other methods and just call ‘invalid’ rather than have the same lines of code appearing multiple times in my program. The last method defined in my CLI class was a ‘show_series_details’ method, which took in an argument of a Shows object and then printed out the details of that object. The last refactoring I did in this class was in regards to the ‘start’ method. I added a loop in the method after the first time the ‘show_list’ method is called which causes the program to continuously call the ‘ask_for_more’ method, get user input, and then calls the ‘do_more’ method which utilizes that input, until the user input’s ‘exit’, at which point the program will terminate. This allows the user to continuously go back and forth within the program, between selecting parameters for the list of series they’d like to view, selecting a specific series for which to view more details, and back to selecting new list parameters, until they are done using the program.

With that, I had finished building my first application from scratch! I found the project to be very enjoyable to work on, even when I encountered some challenges. It was really fun to work through the frustrations and end up with a working program, one which I’ll actually use. I’ve already started brainstorming ways I can build out the program in the future to make it better and more useful, and I’m really excited to move on in the coursework and gain more skills that will help me to do so!