Run — Refactor — Break. Repeat.

Claire Bontempo
5 min readNov 16, 2020

Write a program, make it run. Yay! It works! Tweak it *holds breath*…and it breaks. Spend hours trying to make it work again. This very quickly became my coding ritual while creating my first CLI (command-line interface) application to access external data utilizing an API (application programming interface). You may view a walkthrough of my project here as I go into greater detail about the process itself below.

The “making it run” part.

For several minutes I sat looking at the skeleton of my project. Hurray! I git cloned a repository that came from my very own Git Hub account. Once the initial triumph wore off, the overwhelming sense of “So, where do I even start?” crept in. My vacant VS code looked back at me, with its prepped files labeled ‘api.rb’, ‘cli.rb’ and ‘something.rb’ waiting expectantly in the wings. Fortunately, we have an abundance of resources available to give us a nice little push as we teeter-totter onward with our coding training wheels. I reviewed our lessons and scoped a couple of sample projects.

I decided I couldn’t get anywhere without data, so I’d tackle my API url first. I wanted users to be able to input three different ingredients, and get back recipes containing those ingredients in return. The inspiration was having very little time as a student, and equally limited groceries at home. So, I wanted an application to do the searching for me, after telling it what I had available in my pantry and fridge. With some playing around on the Edamam API website I discovered how the API url looked when it contained three ingredients. After some consideration, it seemed the best way to validate the user’s ingredients, and return the search results if so, would be to have my API take in an initial argument of ingredient_input — a sorted array of ingredients the user inputs — and interpolate the elements of this array directly into the url. This way, I would simply have to check that the url returned something. If it returned an empty hash or nil then the ingredients were invalid (e.g. not food items, or spelled incorrectly).

class Apidef initialize(ingredient_input)@ingredient_input = ingredient_inputurl = "https://api.edamam.com/search?q=#. {ingredient_input[0]}%2C+#{ingredient_input[1]}%2C+#{ingredient_input[2]}&app_id=#{ENV["API_ID"]}&app_key=#{ENV["API_KEY"]}"
uri = URI(url)
response = Net::HTTP.get(uri)
recipes = JSON.parse(response)
@recipe_hits = recipes["hits"][1..5]
end

Well, that’s groovy. But what does initializing a new Api object create? The first part of the #initialize method sets the variable url equal to the API url. Next, the built-in Ruby class Net::HTTP gets the data and the built-in JSON module has a #parse method that converts the data into a much more agreeable format for Ruby: a hash or array.

Okay, that’s groovy and all. But when I instantiate a new Api object — what does that create exactly? *Enter our friend Pry*

Pry is a gem that allows, as the name suggests, you to pry into your code and stop it mid-method, wherever you have placed a binding.pry. Now, Pry and I are still getting to know each other, she can be a finicky friend, but we certainly learned a lot about each other during this project.

Oh, the thrill when I hit binding.pry in my #fetch_api method and typed api to see what exactly Api.new(ingredient_input) had created.

class Cli    def fetch_api
api = Api.new(ingredient_input)
binding.pry
api.create_recipe
end
end

It was the most beautiful sight — an array called “hits” that contained nested hashes (and more arrays). Inside the hit array, the recipe key’s value equalled one-hundred hashes, each a different recipe. I decided that one-hundred was a little much, which is why I gave my instance variable @recipe_hits a range of five. Thanks to pry I was about to discern how to navigate within the recipe key. Now I had all of the information I needed to create my Recipe objects! In the Cli class, I wrote an instance method #create_recipe to iterate through each instance of an Api object which contains five recipe hashes generated from the three ingredients initially inputted by the user. Each iteration instantiates a new Recipe object, with arguments that correlate to a key-value pair in the “recipe” hash. My API contained a LOT of information, from dietary concerns to very detailed nutritional content. To simplify things for the user, Recipe objects would be instantiated with five arguments. (I created a fifth argument, ingredient_input, to have the option to add more functionality later that allowed the user to review previous searches and search results.)

def create_recipe
@recipe_hits.each { |hits| Recipe.new(hits[“recipe”][“label”], hits[“recipe”][“ingredientLines”], hits[“recipe”][“source”], hits[“recipe”][“url”], self.ingredient_input) }
end

So now my application worked! It correctly fetched data, and generated new objects. The “easy” part was formatting this data so that it was pleasant and legible for the user. Thankfully, we’ve had a lot of practicing using each_with_index and other devices to sort through hashes and arrays. The final details of my project were spent doing a LOT of tweaking.

The “breaking” it part.

It’s funny to play around with a program you’ve made, and be SO proud of your creation, to then have someone else run it and IMMEDIATELY find a flaw. For example, right at the beginning — my friend didn’t have a clue how to enter the ingredients. It hadn’t even occurred to me to provide explicit directions.

Although this issue had a simple solution, it demonstrated the value of collaboration and receiving an outside perspective.

A more complicated problem was how to avoid hitting my API key limit. I was only allowed 5 hits a minute. Well, when I initially wrote my code the valid_ingredients method hit API three separate times, just to validate the user’s input. This meant my “Search again, with three more ingredients” function only worked if the user initiated a new search over a minute after the previous one. This one took hours to troubleshoot and it was right after I had declared myself “finished” — HA. Finally, thanks to some guidance from my cohort leader suggesting the use of an instance variable, I fixed it. (This is when @recipe_hits was born.) And thus, I decided I had done enough refactoring and it was time to wrap it up.

Throughout this whole process my brain constantly flooded itself with an assortment of ideas on how to add more functionality. While exciting, I had to constantly remind myself this was my very first project to avoid feeling overwhelmed at all the possibilities. Especially as this particular project was supposed to be built in 3–7 days. What’s that expression? “Rome wasn’t built in a day.” Well, I can assure you, neither was my CLI application.

Hungry? Search for a recipe yourself here!

--

--