Blog

Developing Facebook applications for Ruby on Rails

Jonathon Horsman Jul 20 6 comments

This post is written for Ruby on Rails developers trying to integrate with Facebook, but most of the content is not language specific and may help developers in other languages too.

Overview – complaints

When you begin developing Facebook applications you will quickly discover it’s a painful process.
Documentation is generally out of date since the API changes so frequently, so you have no assurance of what you’re reading is still relevent.

The Facebook developers wiki has a deprecation notice on the main page, but much of the content still appears to be relevent and useful.

The official developers site can be a little difficult to get started with, given the confusing terminology used.

If you’re just starting developing for Facebook, expect to be deeply frustrated by the end of the process. Also it’s quite difficult to diagnose problems as they occur (which is often), so expect a lot of deeply disatisfying trial-and-error loops.

Facebook Connect vs. Social Plugins

When integrating with Facebook you can either put components or widgets on your own site (e.g. the Like button) called Social Plugins, or have a Facebook application, which is fronted (proxied) by Facebook but ultimately served by your server called Facebook Connect.

The latter case is what we we’re developing and requires people accessing your site to install your application first. This then appears in their list of applications on their profile page.

FBML vs. iframes

You have two choices when developing a Facebook site, one is to develop in FBML, which is HTML with a few Facebook extensions; and the second is to develop an iframe which is served directly from your server (not intercepted by Facebook) and appears embedded inside the Facebook page.

Developing in FBML means Facebook hits your server with a request for a particular page, interprets all the FBML tags (e.g. ) and substitues them with proper HTML and Javascript, and serves them up to the client.

Developing an iframe application means you’re serving content directly to the client with no Facebook intervention. There is one important caveat to this which is using XFBML. This is a series of tags just like FBML, but instead of Facebook’s servers intercepting and replacing them with HTML before serving them to the client, a client-side Javascript library does this after they’ve been received on the browser.

So the process of an iframe with XFBML is this:

  1. The client browser accesses your application through a URL like http://app.facebook.com/your-app which includes an iframe in the middle.
  2. The browser requests this iframe which is linked to your site through whatever URL you’ve configured.
  3. Your server serves the HTML and XFBML document directly to the client, including a declaration for a Facebook javascript.
  4. The Facebook javascript then executes onces the page has loaded, reading all the tags and turning them into HTML, including hitting the Facebook server to pull down content.

The advantage of this is discussed here. The main deciding factor for us was speed: the little Facebook chat which appears on the bottom right of every Facebook page is large and slow to render. You can avoid the overhead of loading that with every click by running your site through an iframe, since the outer window is not reloaded each time.

The downside is the delay on the browser when the page loads. If you have Facebook XFBML tags on the page these will appear only after the Javascript has executed, called the Facebook server to receive the data and rendered them on the page.
So your users will see a delay before the comments box or like button appear, with the animated “loading” icon.

A consideration for Ruby on Rails developers opting for the FBML method (instead of iframe) is Facebook makes all requests to your server a POST.
This obviously breaks all your RESTful routes and means you’ll probably have to manually write all your routes for every action in your routes.rb file.

Regardless of whether you choose FBML or iframe, the Facebook API is available to you client or server side. On the server side we used the Facebooker gem which provides a convenient wrapper around the API, such as ensure_authenticated_to_facebook.

We tried to keep hits to facebook to a minimum since they add about an extra 400ms to the response time. You can also achieve this by querying the Facebook API client side.

An inconvenience with developing Facebook applications from behind a firewall in your office is you cannot test without deploying. And due to the numerous quirks there’s a lot of hit and miss style development: try something see if it works, nope, repeat until it works.
Having to do a deployment every 30 seconds makes this problem even more tedious until you give up and start developing on the server (yikes!).

Getting Started

To get started there are a few basic steps:

  • Register your application on Facebook. You will need to add the Facebook Developer application for this. Note also to modify the settings of applications you create, you need to go into the Developer application and look for My Applications.
  • Once you’ve created your application you get an API key and secret. You use these whenever you query the Facebook API.
  • Create facebooker.yml in your config file. It should look something like this:
    @
    development:
    api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    secret_key: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
    canvas_page_name: yoursite
    callback_url: http://yourapp.yoursite.com
    pretty_errors: true
    set_asset_host_to_callback_url: true

test:
api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
secret_key: yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
canvas_page_name: yoursite
callback_url: http://yourapp.yoursite.com
pretty_errors: true
set_asset_host_to_callback_url: true
@

  • When developing your Rails views, inside your layout add the xml namespace to your HTML tag (in HAML):
    @ ruby
    %html{html_attrs(‘en’), “xmlns:fb” => “http://www.facebook.com/2008/fbml”}
    @

if you haven’t yet discovered the joys of HAML use plain old HTML:

@ html

@

  • Also in your layout you need this empty div to appease the angry IE6 gods (directly below the body tag) – in HAML:

@ html


@

  • and directly below the empty div, add the Javascript which does all the XFBML processing:

@ ruby
= javascript_include_tag “http://static.ak.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php”
@

note the exact URL here, previously I had a slightly different URL I’d seen mentioned in some documentation and it was causing random wierdness. This seems to work a lot more reliably.

  • You need a file called xd_receiver.htm in your public folder, which contains just this:

@ html
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
Cross-Domain Receiver Page
@

this allows cross-domain communication between your iframe app and the outer Facebook page.

Begin Development

Now you should be ready to start developing your application. Most of your development is standard Rails stuff: models, controllers and views.
The touch points with Facebook is mostly around authentication (inside your application_controller.rb) and adding Facebook widgets to your pages.

Lets say you have a page which you want people to leave comments. To do this simply drop the fb_comments tag on your page (see the facebooker docs for specifics):

@
fb_comments “arbitrary-unique-id-123”, true, false, 20, :callbackurl => “http://app.facebook.com/your-app/your-url”, :width => 613
@

The main page’s URL always starts with http://app.facebook.com/your-app and anything after that is appended to the iframe’s URL.
So http://app.facebook.com/your-app/photo/1 will result in a request to your server to /photo/1

Checking the current user likes our page

Part of our application included ensuring the current user is a fan of a particular page. Facebook changed their terminology from “being a fan” to “liking”. Some out of date documentation or API calls may use the old “fan” terminology, but as far as I can tell these are the same thing.

So inside our application controller we first check the user is logged into Facebook, and have installed our application.
Then we query to ensure they like our group (which we have the Facebook ID for):

@ ruby
def likes_group?
return true if RAILS_ENV == “development”
return current_session.is_fan(1234567890, current_session.user.uid)
end

def current_session if facebook_session && !facebook_session.expired? return @facebook_session end ensure_authenticated_to_facebook # redirects to the facebook login URL end

@

note the cheat there if we’re in development mode we don’t try and hit the Facebook API.

Internet Explorer 6 and 7 bug

Another gotcha we discovered in Internet Explorer 6 & 7’s handling of cookies means you have to add this to your ApplicationController too:

@ ruby
class ApplicationController < ActionController::Base

before_filter :set_p3p def set_p3p response.headers[“P3P”]=‘CP=“CAO PSA OUR”’ end

@

For non-rails developers, this is simply setting the “P3P” header value on every response.

Setting the full site host path

If you’re using the FBML method (not iframe) and you have images or other resoures you want to link to in your site, you will want to specify the full host when creating the links so they are requested directly from your server. If they’re relative URLs they’ll be requested from http://app.facebook.com/your-app/images/blah.jpg

In your Rails ApplicationController there’s a method you can override called default_url_options:

@ ruby
def default_url_options(options)
{:host => “myapp.mysite.com”}
end
@

Ensuring the user comes through Facebook

Because you’re displaying a web page through an iframe embedded inside another web page, you may want to ensure people don’t bypass the outer page and go directly to your site (which they can do in Firefox by right-clicking in the iframe and clicking This Frame → Show Only This Frame).
Unforetunately there’s no easy or elegent way to achieve this. The best you can do is ensure the parameters which Facebook appends to the iframe URL are present.
Facebook appends fb_sig_in_iframe=1 along with several other parameters which you can check the presence of. If you’re really enthusiastic there’s a complicated hashing process you can perform described here.

Conclusion

This covers most of the stumbling blocks when encountered developing our apps. Hopefully this means you’ll have to spend a little less time discovering this yourself.

I may write some follow ups with instructions for adding particular functionality in later posts if there’s interest. Please leave a comment.

If you get really stuck Arctic Kiwi can develop your Facebook application so you don’t have to. Please get in touch

Comments //

joost

joost Aug 07

The facebooker gem takes care of many things; but unfortunately makes testing more complicated. I haven't found an integrated way to do rspec controller testing, which is a shame because it tempts you to forego testing altogether. Which would be bad.

If you have any resources regarding spec'ing/testing that would be great. There is this page: http://stackoverflow.com/questions/2193942/writing-functional-tests-for-facebooker-controller but seems very convoluted to me. I don't wish to re-create the facebook API with fixtures, I just want to set one flag which will pretend the user is logged in to facebook.

Any ideas?

Jonathon Horsman

Jonathon Horsman Aug 09

In our application_controller.rb I have a current_user method which returns a Facebook user.

For development purposes I created a TestUser class which just had a uid parameter.

So for testing you could either use that or just stub it out (I like Ruby Double for this: http://github.com/btakita/rr).

In the application_controller.rb I also have this code:

  if RAILS_ENV == "production"
    ensure_authenticated_to_facebook
  end

which is kind of kludgy, but again you could stub out ensure_authenticated_to_facebook for your tests and have it always return true.

Hope that helps

joost

joost Aug 09

Thanks for the reply! Based on your feedback I completed a shared example that works really well for me. I dropped this into spec/support and now in the testing environment Facebook doesn't exist.

shared_examples_for 'a facebook application' do
  before do
    @facebook_user = stub("facebook user").as_null_object
    
    controller.stub!(:ensure_application_is_installed_by_facebook_user).and_return(true)
    controller.stub!(:facebook_session).and_return(stub("facebook session", :user => @facebook_user))
  end
end

Pascal

Pascal Oct 27

Just this friendly note: I had to search around a bit on this blog to find out what year this post was written... For people arriving via Google that info should be readily available...

NikolasTesla

NikolasTesla Jan 15

I found the answer in <a href=http://www.google.co.uk>google</a>, remove the topic pls.

Rooby G

Rooby G Jan 25

This tutorial was exactly what I was looking for and saved me a lot of frustration. Excellent work!

Post a comment

  1. optional

Recent Tweets

Blog: The monumental Myspace cock-up: http://bit.ly/emgRKV
Tweeted on Friday at 09:43

Awww railsapi, delete some logs: http://bit.ly/htBNDH
Tweeted on Wednesday at 16:15