A CLI that helps you find foodtrucks in your area is now here!
(Yep, that is a Chick-Fil-A foodtruck and yes they do exist. And more importantly, this CLI app can help find them near you!)
So I found an easy to use API provided by Streetfood's. The API was very simple as it gave a nice JSON file. With a nice Google Chrome extension called 'JSON Viewer', I was able to look at the JSON file and understand it quite easily.
The JSON file is a nested hash map. Inside the first level of the hash, there were two keys, count and open. The key :count
was easy enough and did not contain anymore levels. It just has a value of the total number of elements there are in the :open
key. The key :open
on the other hand, was a lot more different. Once you go inside the key :open
, your exposed to a new level of key-value hash maps.
In the second level of the hash map, there are always 8 keys that represent the next 7 days and also the current day. The API updates every Friday and updates the information to the next week and the current day. In this case I showed the keys as 'Date # - day of the week' for reading and understanding purposes. Although, in the actual API itself the key would just be the date written in :yyyy-mm-dd
format.
If we look inside the 'Date 1 - Friday' or quite literally, :yyyy-mm-dd
key, we will get an array list. The interesting thing is that in this array, each element inside is another key-value pair hash. This is where the levels start to die down and simplify as for this hash is the last level.
So now that we are inside a specific date, we are given a list of hash maps of foodtrucks that are available and open on that given date. Within each hash map, there are three key-value pairs.
A :identifier
key which gives us the foodtrucks name on the databases as a string. (i.e. 'food_truck')
A :name
key which gives us the actual name of the foodtruck as a string. (i.e. 'Food Truck')
A :region
key which gives us the location of that food truck as a string. ( i.e. 'boston')
Now that we know how this API works and all of its levels and information, we can now start getting information out of it!
To get information out of this API, we first have to require a few things in our project.
require 'net/http'
require 'open-uri'
require 'json'
require 'pry' #this one is optional
First I made a class thats sole responsibility was to scrape and get information from the API.
class Scrape_api
end
In this class I made a function that would actually get access to the API.
def get_program
uri = URI.parse(URL)
response = Net::HTTP.get_response(uri)
response.body
end
So here we have an instance function get_program
. The first line inside this function is dealing with a constant variable URL
which is equal to the link for the API. Then we use URI.parse, that we required earlier, to parse the URL and store it in a variable uri
. Now we actually connect to the API using Net::HTTP.get_response
, also from what we required earlier, and calls .body
on that. Now all we have to do to get access to the hash map that we were looking at earlier, we can just simply make a variable and set it equal to the instance calling this get_program
function and parse it with JSON.parse
.
program = JSON.parse(self.get_program)
Now the variable 'program' returns the nested hash map we discussed earlier.
In this same class, I made instance functions that selected information from the API and instantiated objects and saved itself, using the information. Lets talk about one of those functions. Specifically the function that was responsible for making locations.
Before we get started, let's figure out where the information that we want to get is. We want to get the location's of the foodtrucks. Each location of a foodtruck is stored on the last level where there is an array of hash maps containing information about each foodtruck. The location being one of those information. So we have determined that we want to get to the last level of the nested hash map.
First we can access the hash map by opening the second level by calling program['open']
. Here 'program' refers to the parsed JSON file which is now a hash map. So in this hash map, we said that it has two keys on the first level. We access the second key to go deeper and into the second level.
Now that were in the second level of the API, we can use .keys
to get a return of all the key names as strings. Since we can get all the names of the keys in a string formation, we can go a level deeper by using those string values to open the next level.
program["open"].keys.each do |x|
Here we have the same program["open"]
line but we added .keys
to it. 'program["open"].keys' would return an array list of all the keys inside so we use .each do |x|
to iterate over each key and represent each key as 'x'.
Now that we have each key represented as x (i.e. x = 'yyyy-mm-dd') we can use that key to open up the next level. Since 'x' is actually a key, we can do:
program["open"]["#{x}"]
Here we use #{x}
to pass 'x' in so that it can run program["open"]["yyyy-mm-dd"]
and therefore reaching the next level. We had to use #{x}
because if we simply just used "x", the compiler would think that we just literally mean the letter 'x'. So we used #{x}
to tell the compiler that we want the value of x and not just the letter x.
Now that were on the third level:
We just have to access the :region
key to get the information we want. Since we already have a way to get to the third level thats actually an array of hashes, we can just go through each element in the array and access each :region
in every element of the array.
program["open"].keys.each do |x|
program["open"]["#{x}"].map do |x|
Here we have the first line getting the keys for us and the second line getting us access to the third level. Now we can use a .map
to get information out of this level and we can us do |x|
to represent each element in the array. Now that x = element in the array, we can do:
array << x["region"]
Here we have a variable 'array' that is equal to an empty array. We then append the value of x['region']
to that array. 'x' being each element in the array with each element being a hash map.
Now we have an array list of locations and since there are many food trucks in one location, we simply must use .uniq
to get rid of duplicate locations so that we don't instantiate objects of the same location.
array.uniq.each do |region|
Locations.create_from_api(region)
Here we implement the .uniq
and use a .each
to iterate through each location. Now we call the 'Location' class to make a new object in that class by using a constructor we made in that class called create_from_api
. We then pass in each element in this array and create_from_api
handles the making a new object with this new location.
Now that we got through making a location object, we can repeat the same logic to make objects for the Foodtruck's itself, and the dates.
Now lets get to the command line interface part that actually prompts the user. First the CLI will prompt the user for a location you want to check. It prints out a list of locations that we have using puts Location.all
. Then it checks that the users input is valid by doing checking the user input against each Location instances '.region' attribute. If the user input is valid, we move on to getting the date. The logic is the same and the only difference is that it prints and checks using the 'Dates' class.
Now that we have the users desired location and date, we can then select which instance of the Foodtruck class has attributes that equal to the users location and date objects. Each foodtruck instance has a :location
and :date
attribute that equals to a Location and Date object.
Now we can make a new array and maybe call it 'trucks'. We can then append to that new array all the foodtrucks that have the users location and date.
Then with some puts, we can print out the users location, date, as well as a list of Foodtrucks that will be in that area on that day.
Okay great! Here is a list of foodtrucks that will be at boston on 2021-01-18.
Bon Me 1, Bon Me 2, Bon Me 3, Bon Me 4, Bon Me 5, Bon Me 6, Bon Me 7, Chick-fil-A
Of course there is also a feature that checks if there are actually any foodtrucks available on that location and date because if there is not any, we apologize and give the user the let down.
puts 'Sorry. There are no foodtrucks in #{@location} at #{@date}'
Then at the very end the user is prompted if they want information of another place and date and if so, the program starts up from the top and the user can make a new choice of locations and dates. If not the program says goodbye and shuts down.
Would you like to get more information about another
place and
date?
Yes or No?
User => 'No'
Thank you for using FoodTruck Locator. Goodbye
To me this was a very good learning experience and i'm actually quite happy that there is some practical use to this program. When i'm ever craving for some nice streetfood, and I sure will be at some point, i'll be sure to use this.
Thank you to StreetFoodApp for their API.
Here is the link to the API https://streetfoodapp.com/api.
Top comments (0)