Introducing…

Beth Jaswa
gSchool Stories
Published in
8 min readJan 29, 2015

Colorado Wine Tracker!

I’ve been working on a Rails app for the past few months as my personal project for gSchool and I’m finally happy enough with it to share! For friends and family not interested in the details of how it works, stay tuned — I’ll have a post for you soon.

Background

Colorado Wine Tracker started out as a simple idea to track my tasting notes for various wines. I love wine and despite several systems, I am horrible at remembering what I actually liked and what I didn’t. Other tracking systems seemed too complicated or not complicated enough and I never kept up with any of them. And then my Wine Tracker was born!

For the sake of simplicity, I decided to narrow my scope to only Colorado wines. This way I could have a public section of the site listing Colorado wineries’ contact information and wines as well as a private section for individual tasting notes. I also didn’t want the user to input the wine and winery information each time as other systems required because the whole input process seemed to take too long.

Features

Informative How to Taste Wine

This page teaches the public how to taste wine with possible options and is the basis for the tasting note form.

Colorado Wineries

I’m very proud of the the winery index and show pages. It’s been fun to decide where the data for these pages should come from and how to retrieve it. My main goal has always been to populate my database or site and have an option for members or admins to add new wineries.

Yelp API gem

Initially, I decided to pull information from Yelp’s API and since my project is built in Rails I installed the Yelp API gem. I read the docs and display requirements and was concerned because Yelp’s instructions explicitly state that you can’t save their data or have automated calls to their API. Both of which I was planning to do. Out of interest to learn how to do it — I did it anyway and then figured out a better way for the future.

In order to borrow Yelp’s data, I created a rake task that pulled in the API and searched for wineries. Then I iterated over each business and created a hash with my database fields as the keys and Yelp’s fields as the values. Then I instantiated a new winery, passed my hash to it, and saved. Bam! I’ve got wineries!

Well it turns out Yelp doesn’t have that many Colorado wineries in their database and I didn’t just didn’t feel right about doing storing their data, so I searched for a new source of wineries. After I found a new data source, I deleted the wineries that used Yelp’s data and updated the rake task to only store Yelp’s id. The task pulls the API in, then checks my database against the API’s info. If the winery name matches, it will store the Yelp id, but otherwise it will create a new winery with only that property filled in. This allows the admin to see new wineries from Yelp and decide to approve them or not.

Changed my rake task to now only store Yelp’s id if my data’s and Yelp’s names match, otherwise create a new winery with that yelp_id.

Scraping ColoradoWine.com

Finding a site with a good collection of data proved to be a bit challenging and then even more so after I picked a site to scrape. ColoradoWine.com provides a comprehensive list of wineries with their contact info, hours, and other great info — so perfect! Except not really.

I went the Nokogiri gem route and tried to use the CSS classes to get down to the information I needed, but I couldn’t quite get what I wanted. Turns out there’s a distdata.cfm file loaded within the site that has one huge array with all the information I needed. Later in the file, it shows that there are 42 fields for each winery. I copied the array and created a file in my app so that I could start to work with it. (Understandably this is not the best way to go about what I wanted, however it got the job done and I only needed to pull the info once.) In my rake task, I took that array and using the .each_slice method I was able to take the array and separate it into a new array for each 42 items. Then I created a hash with my fields as the keys and the new array’s data with as the values. The data from ColoradoWine included wineries, restaurants, bars, and liquor stores in their data, but I just wanted to pull the wineries. If the data’s types field is “Winery”, then I inserted that information into my winery_array.

Pulling relevant info from ColoradoWine’s file

I saved that array into wineries_cleanedup.rb and set it to the instance variable @cowineries so I could call it in my rake task. Just from briefly looking at the data, I knew some of it wasn’t any good so I went through it and cleaned it up. Then I set a new rake task to load the wineries to the database. I iterated over each winery and imported them into my database much like I did with Yelp’s info.

After populating my database, my winery index and show pages look so much better!

Winery index
Winery show page displaying Yelp API’s rating, number of reviews, and yelp site

Colorado Wines

Currently the only way to populate the wine index page is from user input so it will continue to grow over time. The public does have access to see a list of wines.

Wine index as an Admin

Wines belong to a winery and therefore wines have a winery_id property. In order to display the winery name for a given wine, I did the following within an each loop over wines:

Winery.where(id: wine.winery_id).pluck(:name).join()

Then I also did a similar statement to pull the winery.id so I could appropriately link to a given winery to it’s name on this page. This page will also display wine rating from members’ tasting notes.

Speaking of Associations: Wine Tracker has many!

Building this app has been very enjoyable because before I was able to hit rails new, I needed to decide how I was going to structure my database. A few UML diagrams later, I decided a few associations:

  • Wineries have many wines.
  • Wines have many tasting notes. A wine belongs to a winery.
  • Users have many tasting notes.
  • A tasting note belongs to a user and also belongs to a wine.

User Authorization & Controller Specs

User auth in 3 methods!

When I added user authorization, I decided to write my controller specs first and it was worth every bit of time spent on my tests. I essentially set up a big before action to create various users using object creation methods that I defined within in my specs folder. Then I wrote a test for each possibility for each action as if my methods were there. I watched my tests fail and pass as I expected before I added user authorization. Then I skipped all failing tests. As I worked through user auth, I un-skipped the tests related to the method I was writing. It was pretty amazing to run my tests and instantly tell if my method was working as expected or not. RSpec for the win!

Once you’re a member

Notice that members can upload photos and that several fields offer options for the member to choose

I did basic CRUD several times and became a pretty comfortable with forms and Bootstrap’s stylings. Once you’re logged in (a member), you can add new wines and wineries to the database. You can see and update your own user information. You can also create your own tasting notes that are kept private. Only the wine rating on your tasting note will be compiled into the overall rating for that wine.

Carrierwave, Fog, and AWS

Wines and wineries both have the option to upload an image to the database. In order to add this feature, I needed to install the carrierwave gem to allow photos to be uploaded. The ReadMe was pretty straight forward. It lead me to installing the fog gem which lead me to setting up an Amazon S3 server. Some of these docs weren’t as simple to navigate, but after some persistence I got through it and it worked. Only later did I realize that my carrierwave initializer broke my Travis CI because it didn’t allow for tests and was attempting to hit my Amazon server without its keys. After reading documentation and some help from a teacher, we were able to write up an if statement to satisfy carrierwave locally when the app is in test mode.

Basic members vs Advanced members: Simple Tasting Notes vs Full Tasting Notes

This is feature was very important to me because not everyone wants to fill out 20 fields about a wine they are tasting, others do. If a member is really into wine tasting, they should select to be an “advanced member” during registration or by editing their user information. If members don’t select anything, they will default to a basic member. The only difference between members is the tasting note form and show pages.

Please note that most fields provide options for the member to select from several options.

Basic member new tasting note form
Advanced member new tasting note form

Admin Only

The admin in my app is very important because it acts as a filter to approve new wineries and make changes to existing ones (including delete). Admins right now can see tasting notes and user information. This might change if the app grows and members have privacy concerns.

Deployment

I started with a Heroku generated url since I host my app on Heroku, but having frozen-plains-8303.herokuapp.com as a url is kinda lame. So I purchased a domain name from Namecheap.com and hooked it up.

What’s next?

I still have a few more things I’d like to get done on this app to make the user experience flow more smoothly from sorting on the index pages and seamlessly integrating adding a new wine from the tasting note form.

Creating this app definitely helped to refine and ingrain the skills that we learned in class. I’m thinking about the next projects and I’ll share once I’ve figured it out.

--

--

Beth Jaswa
gSchool Stories

Engineering Leader. Enjoys Tasty Food and Beverages. Wishes Upon a Star (Disney Fanatic).