DEV Community

Norman R.
Norman R.

Posted on

My First Program

After nearly a week and a half of trial an error I finally finished my first CLI project. It definitely took longer then expected mainly due to my lack of asking for help which I will make a priority for the next project. Completion of the this project really helped me change from a guy who just memorized a few ruby commands to a guy who can formulate ideas and convert them to "sloppy" but coherent code. I still have a long way to go and after struggling for weeks wondering if I actually "know" anything, I have gain a little more confidence.

As a former truck driver one of the many annoying situations in the long haul industry is finding suitable parking for an 18 wheeler. I decided to make a Command Line Interface (CLI) program to help find trucks stops in different states.

I first started building a flow chart that wasn't very well thought out and quickly ended up just "coding as I go". I decided to build the web scraper using nokogiri first which was my biggest challenge. It took a while to find a suitable web site to scrape since one of the project requirements was to use a 2 level web scrape. I settled on Allstays.com which had all the locations of truck stops in the US and Canada. The site was full of good info but I struggled finding the right CSS selectors. The site had the states on one page and the city and address on another. The state names and links were placed in "a" classes which made it easy to get to the next page. I wanted end users to be able to type in a state name and have the program match that with the data from the website. I used an "each do" loop to iterate through the css elements as follows.

scrape.each do |element|
      @state_name = element.children.text
      @state_url = element['href'].insert(0, 'https:')
      if user_input == @state_name
        second_scrape(@state_url, user_input)
        return
      end
Enter fullscreen mode Exit fullscreen mode

If the user search match the css elements text then it would use the url as an argument for the second scrape method. If your wondering what

(0, 'https:')
Enter fullscreen mode Exit fullscreen mode


is well that is because the url was missing the "https" causing nokogiri to crash. Adding that to the beginning of each url the block iterated through allowed nokogiri to scrape the next web page successfully. Scraping data on the second page of the site was difficult as the truck stop address, name, and city were not together in a class like before. At first I tried to code an iterator using "each" but ended up with unwanted text from the site. After figuring out the text elements pattern I noticed the truck stop address was located every two elements apart and the city name was located one element apart. The name of the truck stop was in a separate "a" class. I wanted to iterate through all 3 elements at once and store them in newly created "Truckstop" class instances but the program kept crashing when I used an "each do" loop. I'm sure a better engineer could have done this in one or two lines of code but I decided to make a custom iterator using a "while" loop. The loop could iterate through all 3 elements at once even though they had different patterns.

name_counter = -1
    address_counter = -2
    city_counter = -1
    while city_counter < city_scrape.length - 6
      city_counter += 1
      name_counter += 1
      address_counter += 2
      truckstop_city = city_scrape[city_counter].text
      truckstop_name = name_scrape[name_counter].text
      truckstop_address = address_scrape[address_counter].text
      truckstop = Truckstop.new(truckstop_name, truckstop_city, truckstop_address)
Enter fullscreen mode Exit fullscreen mode

The code isn't very sexy but it does the trick. The " - 6" above would stop the "address" iterator from overshooting the "city_scrape" array which would lead to an error as well as unwanted text found at the back of the array. I would have preferred to just store the values in a hash but the project required me to use class instances. One thing I never figured out was how to take a string value and use it to dynamically name a new instance class. The project deadline was approaching and being behind I skipped that task. If any one reading this know how to do that using meta programming or a command please reply below. Here is the Github link to the project. Thank you. Link

Top comments (0)