On Rap Genius we have songs that are “published” and those that are “works in progress”. Recently we decided to streamline our editorial process by creating a third category (“under review”) for songs that are basically complete and just need to be edited. Here’s how I did it
How were we distinguishing between published and non-published songs before adding the concept of “under review”? One strategy here is to add a field in your songs database table for is_published. This field would store a 1 if the song was published, and a 0 if it was unpublished.
This isn’t bad, but it’s more efficient to use a field called published_at, which stores either the time of publication if the song is published, or NULL if the song is unpublished. This strategy allows you to store the time of publication without using an extra field, and is what I was doing.
We could use the same approach for adding the concept of “under_review”. I.e., we could add a field for nominated_for_publishing_at (I prefer “nominate for publishing” to “marked under review”) that would store the time something was marked under review, and NULL if the song was not under review.
There are a few problems with this approach:
It requires us to create a lot of fields.
Both Now. Actually I lied -- in addition to published_at, we also had published_by_id (which stored the user ID of the user who published the song). If we used this approach for adding "under review", we would also need nominated_for_review_by_id
And later. Adding nominated_for_review_by_id isn't such a big deal, but suppose down the line we want to be able to mark some songs hidden (e.g., those with very few explanations). If we went with this approach, we would have to then add 2 more fields (hidden_at and hidden_by_id) -- obviously this approach doesn't scale
It doesn't capture the relationship between published and under review. Obviously a song cannot be both published and under review. This wasn't a problem before because the published_at field could only take one of two values, and so we didn't have to worry about any song ending up in an invalid state. Not so any longer -- we have to write our own application-level code to make sure that no song is both published and under review.
It gets worse: once we've written the code that ensures that no song can be in more than one "state" simultaneously, we'll might need still more code to ensure that a song's state transitions are "valid". E.g., we might want to prohibit songs from transferring from published to under review, or maybe we want to require that songs pass through under review before getting published.
The right approach here is to implement a state machine. Instead of dealing with several disconnected Xed_at fields, a state machine allows you to deal in concepts like:
State. A song can be in one state at a time
Events. A song has events (e.g., “publish”) that transition from certain states to other states (e.g., under review to published). If you try to transition a song to an invalid state (e.g., straight to published without passing through under review), you get an error.
A state machine also solves the “lots of fields” problem. You only need 3 additional fields, no matter how many states you have:
statestate_updated_atstate_updated_by_idThe best part is that building a state machine is a solved problem — you don’t have to write any of this code yourself.
AASM (which kind of stands for “acts as state machine” — for awhile it was voguish to name your Rails plugin “acts as whatever”) is the most popular Rails state machine plugin (state_machine is actually better, but I couldn’t get it to work). Here’s my AASM state machine:

52: I tell AASM the name of the state column for my songs table (I believe the default here is aasm_state, which isn't ideal because it ties the state concept of your song model to your current state machine implementation -- I'd like to be able to swap out AASM for a different plugin without modifying my database schema)
53: All songs start with state = "work_in_progress"
55-57: Songs can also be under_review, and published. Note the information duplication here -- AASM should be able to infer that my default state (work_in_progress) is valid without me having to list it explicitly here. The same is true of the states listed in the transitions below; AASM should automatically recognize any state referenced by a transition as valid
59-70: Song state transitions. Every transition is automatically exposed as a method I can call on any song object to change its state. For example, when I want to publish some_song, I'll call some_song.publish. Transitions also allow you to specify:
Valid to and from states. For example, it doesn't make sense to nominate_for_publishing an already-published song. By listing work_in_progress as the only valid from state for nominate_for_publishing, I tell AASM to raise an error if I try to nominate a song for publishing that's in any state other than work_in_progress"
Requirements for the state transition to take place. For example, the "publish" event will fail for any song that doesn't have a description, embed link, rating, and category
The implementation of guards is another design error in the AASM plugin. Specifically, the conditions for moving a song into a given state are likely to be identical to the conditions for being able to save a song that's already in that state. E.g., if I can't publish a song that lacks a description, I probably can't take an already-published song and remove its description. Since AASM doesn't take this into account, I have to duplicate the guard logic as a validation to account for the latter case:

A callback function to run on successful transition. I haven't implemented any yet, but it's easy to imagine an announce_new_song_to_mailing_list method that executes whenever a song is published
One last thing: how do I record who last-changed a song’s state and when it happened? Ideally I could specify a callback function execute whenever any state transition took place, but AASM doesn’t have the capability. So rather than add a callback to every single transition (and remember to add one to all future transitions), I implemented this functionality as a before_save callback on the song model itself:

Disqus is a drop-in commenting system that I’m using on Rap Genius. It solves these problems:
Before Disqus, if you wanted comments on your website, you had to build the technology yourself (i.e., reinvent the wheel)
Before Disqus, your comments were split across several identities — you might comment with your Google account on Blogger blogs, maybe you have a Wordpress account you use on Wordpress blogs, and when you come across a blog that’s neither Wordpress nor Blogger, maybe you leave an anonymous comment.
With Disqus, you have one account (your Disqus account) you can use at any site, which ties all your comments together and lowers the barrier to entry for commenting on new sites.
Disqus also has a few downsides:
The web wasn’t designed with “mashups” in mind, and so importing content or functionality from another site into your page is always somewhat hacky. At a high level, here’s how Disqus works:
<div> with the id disqus_thread
An inherent problem of the way the web is designed is that this Javascript can do anything it wants -- for example it could steal your users' cookie (the equivalent of stealing the password of the current site).
To use Disqus you have to be super-confident both that they're not malicious and that they'll take the necessary precautions to prevent someone who is malicious from getting control of the Javascript they serve.
The Javascript Disqus gives you to include looks something like this:
<script type="text/javascript" src="http://disqus.com/forums/rapexegesis/embed.js"></script>
Unfortunately, including this Javascript file directly on your page (as Disqus suggests) is bad for performance. This is because browsers download and execute included Javascript files synchronously, meaning that everything else has to wait while the file downloads and runs.
Because of this, it would be disastrous to include this Javascript file at the top of your page — doing so would mean that everything on the page (text, images, etc) would have to wait for the commenting form to load, which is bad because users can enjoy the page before the commenting form appears, and since it’s at the bottom, users aren’t likely to notice if it takes an extra 2-3 seconds to load.
Loading the comment form synchronously is a problem even if you include the Javascript at the bottom of your page because it will still delay any other Javascript that needs to run. For example, on Rap Genius, the Javascript that turns on the explanation tooltips couldn’t run until the Disqus comment form loaded — clearly not ideal
Ideally the Disqus comment form would load and execute asynchronously — i.e., without making everything after it wait.
jQuery abstracts away the details of loading remote scripts asynchronously with its $.getScript() method, so ideally we should be able to just replace the inline script tag with this jQuery code:
$(document).ready(function(){
$.getScript("http://disqus.com/forums/rapexegesis/embed.js");
})Unfortunately, this produces a blank white screen. What gives?
Let’s have a look at the source of the script we’re loading (http://disqus.com/forums/rapexegesis/embed.js). This section in particular is interesting:

The Disqus Javascript is using document.write() to print the CSS necessary to style the Disqus comment box onto the page.
This is bad because document.write() can only be called before the DOM has loaded — after the DOM has loaded document.write() makes no sense because there’s no way to determine where the writing should take place. Because of this, the browser freaks out, and overwrites the entire document — in this case with a single style tag. This is what causes the blank screen.
Since the whole idea of our plan was to not block the loading of the DOM in order to load the Disqus comment form, Disqus' use of document.write() is a problem.
Why does Disqus use document.write() in the first place? Backwards compatibility — precisely because it’s so primitive, document.write() enjoys wide cross-browser support.
Thankfully, Disqus leaves us an out — if we set the window.disqus_no_style variable to false, the Disqus script won’t use document.write() to load the styles, and our page won’t get overwritten:
window.disqus_no_style = true;
$.getScript("http://disqus.com/forums/rapexegesis/embed.js")However, we need some way of loading these styles in order that the Disqus form not look terrible. I copied them into a separate stylesheet and included it.
Taken at my office:
