Halloween season came around and with it came big bags of candy at the office. Some candies disappeared faster than others, leading exclamations like “aww all the Twix are gone!” or “there’s nothing but stupid 3 Musketeers left” to be heard around the office. Summer remarked that we needed an app to tell us how best to divide the candy so that no one who didn’t want stupid 3 Musketeers would be forced to eat them, and the weirdos who actually like 3 Musketeers could revel in their abundance. Enter my next pet project.
I knew I needed a database to keep track of all the candy, plus let users give their candy preferences. I went with a Rails app since I knew it would be fastest for me to get going, since I’m most familiar with Rails as a platform for basic CRUD apps. I basically wanted to make an app that would let a group of people see each other’s candy opinions, as well as let individuals rate each candy. I wanted the rating process to be fun, and I had the idea for a range of smiley faces as indicators of how much you liked a candy.
I figured I’d deploy to Heroku because I always deploy to Heroku, again because it’s the fastest, most convenient option when deploying my own little apps. So I went ahead and set up the app with a PostgreSQL database since that works well on Heroku. I hadn’t fiddled with PostgreSQL on my MacBook since upgrading to El Capitan, but it wasn’t hard using Homebrew.
The first thing was figuring out my data model, which is one of my favorite parts of any new project that involves a database. I knew I wanted accounts, so I got going with Devise for authentication, which necessitated a users table. I customized Devise a bit, going with a username field instead of email because I wanted a single login for my whole office to use, and I wanted it to match that of an app David built for us.
This deviation from the Devise default of using an email address actually led to a bug because of an oversight on my part. Steve helpfully caught the bug which allowed a user to register a new account with a blank password, but then you couldn’t log back in after you’d logged out. This happened because I couldn’t use
devise :validatable in my User model because that validates that the email is provided when signing up, but it also validates a password is given. So I had to manually add some password validation logic to the User model, along with validation for my custom username field that replaced the email field.
I added a candies table and tied each candy to a user account. I did this thinking some non-American might try the app someday and not give a hoot about our silly American candies, or they might think that Smarties are some chocolate candy made by Nestlé instead of those powdery things found in the US. So each user account gets their own set of candies, though I do have users by default get a predefined selection of American candies for them to rate.
There’s a people table and each user account can have many people. This is how I have a single account for my office and all my coworkers are included in it. I went with something simple like this because I knew strict access control wasn’t a concern. It works on the honor system, each person only setting their own preferences. If you were using the app with your rambunctious children, you might have a problem where one child sets the other’s preferences for him so as to ensure their brother gets all the terrible candies, for example. This hasn’t been a problem at my office… yet.
The preferences table ties a person to a candy with a rating of like, love, hate, or dislike. If you are indifferent towards a candy, you just don’t have a preference table entry at all for it. Summer proposed distinguishing between indifference and just not having rated a candy yet, and I agree it would be helpful. I’d like to show a list, for example, of which candies people have not yet rated.
The major feature of the app was to tell the user how to divide their hypothetical trick-or-treat bag of candy in a way that pleases everyone. I did this by adding a few scopes to my Candy model:
- All candies that are only liked or loved by a single person. I display these all listed together with the suggestion that they all be given to that one person:
- All candies that are liked or loved by multiple people. If the candies are all liked or all loved, I suggest they be split evenly among the people. If the reviews are mixed, though, with one or more people loving the candy while the remainder like it, I suggest they all get some of the candy, but whoever loves the candy should get a bit more. Nick made the suggestion that pickier eaters — those who have more dislikes and hates than likes and loves — should be favored when distributing candy, which I’m considering.
- Candies that have been rated, but that no one likes or loves. The app suggests you throw these in the trash. 😝
Another fun part of the app is the Trends page. It lists the most popular, least popular, and most divisive candies. At our office, the most popular candies include Kit Kat, Sour Patch Kids, and Reese’s Cups. I’m a bit surprised by a sour gummy candy beating out tried-and-true chocolate, but there you are. The most unpopular candies include Circus Peanuts (of course), Necco Wafers, and Milk Duds (???).
The divisive candies are fun because they’re candies that are rated favorably and unfavorably by the same number of people. These include Laffy Taffy, Hershey’s Kisses, and Jolly Ranchers. Jolly Ranchers seem innocuous enough, but anyone actually liking Laffy Taffy kind of confuses me. That stuff will rip your teeth out!
Finally, I tried a new gem with Candyfair: wikipedia-client. I wanted to add photos of each candy, especially since Jon pointed out he didn’t know what some of the candies even were. I was faced with the conundrum that users could add their own candies, and I didn’t want to host a bunch of images or make the user enter image URLs. The Wikipedia gem let me add both a photo and a brief blurb about each candy, as pulled from the Wikipedia API:
There were a couple of hurdles: not every candy has its own Wikipedia page, most notably individual Little Debbie cakes, and not every candy can be found on a page of the same name on Wikipedia. So I added two extra fields to the candies table:
skip_wikipedia. When you visit a candy’s page on Candyfair, it makes an AJAX request to my Rails app which uses the gem to hit up Wikipedia for a page for that candy. If
wikipedia_title is set, it will search for that, otherwise, it searches for the candy’s name. If
skip_wikipedia is set, no AJAX request is ever made and no Wikipedia info is shown.
So that’s Candyfair, a fun little app I built between October 27 and November 3, so just a little over a week. The source code is all on Github and it’s deployed on Heroku if you want to try it out. Pull requests accepted, if you want to contribute!