View The Space is Hiring

Wednesday, June 15, 2011

Realtime facebook updates with Rails, Koala, and Resque

It's one thing if you know i hit like on skittles three years ago, it's another if I liked skittles 5 minutes ago. There is a good chance that I am currently still thinking of candy. Enter facebook realtime updates with Rails, Koala, and Resque.

I am going to mostly assume that you are starting off with an empty rails3 project with koala and resque installed. Let's create a controller for receiving updates from facebook:



Notice we have two methods. The index method is a get request from facebook and it is simply for verification. The create is where the real action happens. Let's move on to the FacebookLike#real_time_update! implementation.



The above FacebookLike#real_time_update! method take a payload object which might look like this:



Some important points about the realtime update payload:

  1. The id and uid are the same. They represent the facebook user id of the person doing the liking of something.
  2. There is no information about what is being actually liked. We're going to have to query the facebook API to figure that out ourselves.
  3. The same uid's are repeated for each timestamp. If I liked skittles at 12pm and rainbow skittles at 12:01 pm, there may be two entries for me in the payload.

Our code first concentrates on problem #3 from above. The FacebookLike#real_time_update! method first removes duplicate facebook ids by creating a hash of uids to entries and then taking entry with the smallest timestamp. Why with the smallest timestamp you ask? Because we are going to be querying the facebook api next using the since timestamp parameter? In my example, I liked skittles at 12pm and rainbow skittles at 12:01. If we query for all of my likes since 12pm, we will pick up my 12:01 like of rainbow skittles.

So now that we have removed all duplicates, you're probably thinking that we should go ahead and query the facebook api for all likes since the first timestamp. Tempting, but this can be a time consuming query in the context of an http post to our rails app. Let's instead enqueue a resque job to do this enventually for each uid within the FacebookLike#RealtimeUpdate#enqueue_updates! method which just iterates over all facebook ids, finds the corresponding user, and enqueues a job to query and update all facebook likes since a given unix timestamp. Let's take a look at the resque job:




The job simply delegates back to the FacebookLike model invoking update_likes_for_user! passing in the user object along with an options hash containing the :since timestamp. Now finally the FacebookLike#update_likes_for_user! queries the graph api for facebook likes since the given timestamp. Notice if the timestamp is not passed into the method, we query for all facebook likes. Next we iterate over the restuls and create the FacebookLike if it does not yet exist within our database.

One last step before we deploy our code. We need to create the routes for our controller.



Ok, almost everything is in place. Let's deploy all that to production, open up a rails console, and let facebook know that we are interested in real time updates.



Obviously in the above sample, replace app_id and secret_key with your actual app_id and secret key.

Alright, now you should be getting updates. How do you know? In our app, facebook posts to our controller every few seconds. The result should be facebook update jobs enqueued within resque.

5 comments:

  1. Great write up Karl! This was very helpful information.

    ReplyDelete
  2. Fantastic post! Very helpful. I just want to include some additional data. I was trying to access the User Feed data, but Facebook would not post back the data. Eventually I figured out that I had to add 'read_stream' permissions to :scope for omniauth. Here's the Facebook page that helped me: http://developers.facebook.com/docs/reference/api/permissions/

    ReplyDelete
  3. Hi,
    Where did the access_token in the following code come from?
    def self.graph
    @graph ||= Koala::Facebook::GraphAPI.new(access_token)
    end

    Shouldn't it be the access_token of the user?

    ReplyDelete
  4. hi,
    i am trying to subscribe my application to facebook but i am getting following error
    (#2200) callback verification failed: Operation timed out after 6001 milliseconds with 0 bytes received

    any idea?

    ReplyDelete
    Replies
    1. jia, I'm getting the same error. Did you ever solve it?

      Delete