Left/Overs

an app for your pantry’s odds and ends


Last week we made an app.

It’s a simple tool that allows you to insert a protein, vegetable and carb and return a list of recipes that use those ingredients. We also provide the option to select which meal the recipe should be geared towards.

We return these recipes using the Yummly and BigOven APIs. Because the Yummly API returns a flavor object for most of its recipes, we were able to create an algorithm that analyzes the flavor profile and returns a beer that pairs well with it. We used the breweryDB API to return information about the beers.

On the inside, it’s a pretty simple app. First, I’ll explain the components we used to make it work. Then, I’ll walk thru its process step-by-step in the order that the script executes upon page load.

Components:

  • Node to run NPM and install our modules.
  • jQuery
  • Lodash
  • Express to serve our app the local template files. Also to generate our proxy server to make cross-origin requests to the BreweryDB API.
  • PathJS to handle routing. In this app, its use was pretty minimal and non-essential, but was good practice for other applications.
  • Mocha for testing.
  • Chai assertion library for use in Mocha. Again for testing.
  • Foundation for general view structure.

Step-by-step:

  1. window.onload requests app function to fire.
  2. app loads our js libraries (jQuery, Lodash, pathJS and Foundation). It then sets up our Lodash template interpolation settings and creates two objects containing our API keys. Finally, it creates an instance “recipe” with the LeftOver constructor, passing in the previous 2 objects.
  3. Submit is wrapped in an href pointed at “#/results”. When the user completes the form and hits submit, the URI bar is appended with “#/results”.
  4. pathJS is watching for this string. When it finds it, it fires off a function comprised of 2 parts.
  • First, executes an if statement that fires a verification function that returns a boolean based on whether the form was completed. If it wasn’t, then the page redirects to root.
  • Second, if the form was populated, it starts a jQuery “When/Then” deferred function chain.

5. The first two functions of the “When” pull recipes from Yummly and BigOven respectively.

  • These two functions build a URI string by first calling a function that generates an object by using jQuery to iterate over all the form inputs. This object is returned and saved as a variable in the Yummly and BigOven objects, and its properties are used to build the URI string fed to the $.getJSON function.

6. The next two functions of the “When” load a template from the project’s directory to be interpolated over.

7. The last function of the “When” requests an array of beer styles from BreweryDB. These styles are fed as an array into another function which creates an array of just 4 selected beer styles to be used in our pairing algorithm.

8. The two functions of the “Then” simply run _.template on the data objects returned from the first to functions of the “When” and the html strings returned from the second two functions of the “Then”. Then it inserts the return from _.template as the innerHTML of selected DOM elements to populate our page.

9. The only wrinkle to 8. is that the Yummly function that runs _.template first creates an array of the recipes’ flavor objects and then uses $.extend to build the data object of both the Yummly data and the output of the beerPairer algorithm. The beerPairer algorithm compares the flavor array at the associated recipe’s index. Depending on that comparison, it returns an appropriate beer style from the beer style’s array. There is some error handling, in that if the recipe has no flavor object, the beer pairer algorithm returns a key that is interpolated in by _.template as “Sorry but there are no beer pairings for this recipe.

10. We also have desktop notifications and a counter that uses local storage for 2 examples of browser APIs that we incoporated. However, both of those APIs are explained in better detail at the following locations: