Getting Started with Facebooker

Developing for the Facebook platform can be a big headache, and on Rails your headaches are unfortunately compounded from the get-go. While the otherwise-inferior PHP users get an API library from the Facebook development team (I'm kidding, I love you PHP guys), on Rails we have to deal with gems that aren't even at version 1.0 yet. While Facebooker is quite good at this point, its documentation covers a rather sparse selection of its impressive feature set, and RFacebook, which is better documented, hasn't been updated in aeons and is way more difficult to use besides. And unfortunately the standard Facebooker tutorial doesn't include the newest features from the recent Facebook profile overhaul or even all the necessary steps to get a new Rails application running on Facebook. So, enter Josh.

In this blog post I'll tell you the best way to integrate Facebooker into a new Rails project so you can start developing social networking applications quickly and easily, and how to hook them in to Facebook's new profile plan. The goal is that by the end of this post you'll have a totally working Facebooker Rails application and you'll understand how to develop in it at least a little bit.

Getting Started

rails test
cd test
rm public/index.html
script/plugin install git://github.com/mmangino/facebooker.git

Now go to the Facebook developer application and add it in.


Allow Access to the Developer Application

Click on Allow, then on Create a New Application to get the ball rolling. Come up with a good name for your future masterpiece, read and agree to the Facebook TOS, and create your application.

You'll be dropped to your application page which lists your API key and your secret. Let's get those into our application. After you installed the Facebooker plugin, you'll find a new yml file in config called facebooker.yml. Copy and paste the API key and secret into facebooker.yml under the correct fields in the development environment (and also the production environment if this copy of the application will eventually be going live).

Now there are some settings we have to monkey with. Click on Edit Settings and you'll be taken to your application settings page. There are a couple hyper important things to do here.

The first is to fill in the callback URL. This is the real URL of the server that will be hosting your application. For development purposes, this should be a non-standard port on a server that you can open non-standard ports on. We'll change this to a more accessible port once we've finished development.

Secondly, the canvas URL specifies the location people will see in their address bar when they actually access your app. This is the point of access to your application for users -- if they go to the actual URL of your server (like www.test.com), they'll be redirected to this canvas page by Facebooker. So make it something memorable and sensible.

Callback URL and Canvas Path

Third, ensure you've selected "yes" for "Can your application be added to Facebook?" Otherwise you're not going to be seeing your application in its full FBML glory anytime soon. When you click "yes" the "Installation Options" area appears. Check "Users" for who can add your Facebook application to your account, and also check the "Development Mode" box so only you can your team can install the app.

Once you've made those changes, save your application, and add in the canvas URL and canvas_page_name to facebooker.yml (in my example, http://www.test.com:8000 and pathfinder_test, respectively). Once you click save you'll be returned to your application settings page.

Application Info

When you're returned to the application's setting page, click on the "View About Page" link. (It's in the lower right of the screen shot right above this). You'll then be looking at the application's about page, which should look a lot like this:

Application Homepage

If it does, we're on the right track.

Digging a Tunnel

When you're developing Facebook apps, having a remote server to develop on is completely integral. If you're developing in FBML (and in the new version of the Facebook profile system, FBML is practically required), to see the results of your hard work you'll have to actually allow the Facebook servers to interpret your markup. That means they need access to your development machine, or you need a tunnel from your development machine to a remote server that's already accessible from the Internet, or you set up a staging server by following the advice of Noel Rappin's excellent blog post, Facebook Application Logistics for Team Development.

I recommend the latter route.

But if you don't have the resources to come up with your own staging server, we have to choose either your own computer or a tunnel. Opening up your computer to the Internet is a bad thing; development machines and networks have firewalls for a reason. So select a remote server that you have sudo rights to and let's get started building a tunnel.

Sudo rights are necessary because you'll likely have to edit your remote host's sshd config. (At least, I did.) Append this to the end of sshd_config:

GatewayPorts clientspecified

Now return to your handy-dandy facebooker.yml. Under the tunnel section for development, put in the username you have on the remote machine, the machine's URL, the port you can open on the remote machine (under public port), and the server you'll be running on your local machine (3000 is the Rails default).

Starting the tunnel is simple. From the root of your app:

rake facebooker:tunnel:start

You'll be asked for your SSH password to the remote server. If the connection is successful you will see nothing on this terminal session. Open up a new terminal and start your local server on your local port.

Navigate to http://apps.new.facebook.com/canvas_path and your server will report this routing error:

It Works! Kind of!

Congrats, we're up and running... sort of.

Getting on the Rails

We're seeing this error because Facebooker expects a route to be generated with :conditions => {:canvas => true} if it's intended for consumption by Facebook. We have no such route and there's nothing in our app at this point anyway. But now that we've finished with the Facebook side of this application, let's work on getting something in Rails that Facebook can understand.

A good place to start is our first scaffold.

script/generate scaffold airplane model:string pilot:string destination:string arrival:datetime
rake db:migrate

Unfortunately scaffolding won't do a whole lot to help us right away. We have to modify the Rails-generated files fairly significantly before our Facebook page will work. To start with, open up config/routes.rb. At the top you'll see map.resources :airplanes. Change that to:

map.resources :airplanes, :conditions => {:canvas => true}

And while we're here go ahead and uncomment map.root and change it to:

map.root :controller => "airplanes"

Now our controller is connected correctly, but if you try to refresh you'll see we're getting 406 errors: the format that the request is coming in is unacceptable to the webserver. Fixing that is as easy as opening up config/initializers/mime_types.rb. Append this line to it:

Mime::Type.register_alias "text/html", :fbml

Restart your server after you save.

FBML will now be correctly processed by Rails, but your application still doesn't understand when to call FBML pages. You'll need to go into the airplanes_controller and change every format.html in a respond_to block to format.fbml. For example, after I altered the index action, it looked like this:

app/controllers/airplanes_controller.rb

# GET /airplanes
# GET /airplanes.xml
def index
  @airplanes = Airplanes.find(:all)

  respond_to do |format|
    format.fbml # index.fbml.erb
    format.xml  { render :xml => @airplanes }
  end
end

After you've finished that, there's another fun task awaiting you. Now our application knows that it's receiving FBML and that it has to call FBML pages when it gets a request formatted in FBML. Unfortunately we have no files that are formatted with FBML: they're all HTML right now. So, open up app/views and change every file there from *.html.erb to *.fbml.erb. Yes, this is kind of painful, but it's over quickly.

When you're done, your views should look like this.

What your airplane views should look like

Finally, you need to edit layouts/airplanes.fbml.erb. <body> tags are illegal in FBML and must be removed before Facebook will process our page. For simplicity's sake, I usually remove <html> and <head> as well, since you can't change the head of the page anyway. My layouts/airplanes.fbml.erb ended up looking this simple:

app/views/layouts/airplanes.fbml.erb

<p style="color: green"><%= flash[:notice] %></p>

<%= yield  %>

The Fun Stuff: Getting Information

Finally! Now that this is all accomplished, if you go to your canvas page in your browser, you should see your local server receive the request and render the airplanes index. You can go through the regular scaffold actions here but it's honestly not very exciting. Didn't I promise some FBML at the beginning? All we've really got here is HTML. Sure, it's cool and all, but can't we do something more with it?

The key to that 'something more' is inserting the following line at the top of airplanes_controller:

ensure_application_is_installed_by_facebook_user

This is a standard before filter and it accepts :except and :only just like a regular one might. If a user encounters this filter they'll be forced to install your application. Installation empowers an already-existing controller method that I haven't referenced before: facebook_session. facebook_session is the Facebooker object that allows you to make calls to the Facebook API server but it wasn't doing us a whole lot of good before now, since almost no API calls can be made before a user installs an application.

After a user installs your application, this changes dramatically. We can now leverage the power of the Facebook API to pull from and push to the user profile.

A good, straightforward idea at this point is to automatically identify the Facebook user when they visit to your application. Create a user model and make sure it has a uid string field in the migration. Once that's done, let's put in a quick before filter in our airplanes_controller:

app/controllers/airplanes_controller.rb

  before_filter :get_user
  ...

  private

  def get_user
    @current_user =
      User.find_or_create_by_uid(facebook_session.user.id)
  end

Now you'll automatically load the @current_user every time they return to your Facebook app. You'll probably want to tie most of your application data to this user: their posters, their songs, whatever your Facebook application does, this is the easy way to identify the incoming Facebook user and get their stuff accordingly.

You can call any of a user's profile information from the facebook_session.user object, like facebook_session.user.name, facebook_session.user.books, and so on and so forth. (id obviously returns the Facebook user id for our subject.)

The Funner (More Fun) Stuff: Setting Information

Of course, our application can't reasonably be considered cool unless it can put messages all over a user's profile. Facebooker provides an amazing utility to do this that is almost hilariously under-documented: it allows you to set up ActionMailer-style models to deliver information straight to a user's profile!

Before we actually update the user's profile, though, we must have permission to do it. The new Facebook profile system requires you to specifically request permission to add your application to a user's profile; if you send information to their profile before you request information, you'll get a success message but nothing will appear on the user profile. Let's put in a cute little block in the airplane layout to bug a user to add our app to their profile:

app/views/layouts/airplanes.fbml.erb

<fb:if-section-not-added section="profile">
  <p>Add Airplanes to your profile to see it listed there.</p>
  <fb:add-section-button section="profile" />
</fb:if-section-not-added>

Thanks to the magic of FBML the if-section-not-added tags appear only if the section isn't added for the users; the add-section-button presents them with a button to add your application to their profile. Once they've added your application this section will disappear and you'll be able to update their profile as per normal.

To add information to a user's profile, make a new file in the models directory, and name it as if it were an ActionMailer.

app/models/user_mailer.rb

class UserMailer < Facebooker::Rails::Publisher

  def profile(user)
    send_as :profile
    from user
    recipients user
    fbml = "This is some test FBML that will be inserted into a user's profile."
    profile(fbml)
    profile_main(fbml)
  end

end

Obviously, the mailer is descending from Facebooker::Rails::Publisher instead of the regular ActionMailer. send_as :profile instructs the Publisher to send this as a profile update, rather than a mini-feed update or other Facebook content type. You can designate the user the profile update is from and the one that it's to: in my application they're both the same, but they might differ for yours. You can render the FBML from a file here too if you want.

Setting profile is the deprecated way of updating a user's profile: profile_main is the way used in the new Facebook styles, but for backwards compatibility include both here.

Now, from your controller, simply include this line in an action to update their profile:

UserMailer.deliver_profile(facebook_session.user)

The profile information will be packaged and sent off to the target user and will appear immediately in their profile (provided they've added your app).

Going Further

Obviously this is a very broad overview of the power of Facebooker, concentrating more on its installation and setup then how to really leverage its power. If you want to figure out everything it can do, the Facebooker tutorial on the Facebook is sure to be a great help to you. Otherwise my suggestion to you is to inspect the source of Facebooker to really understand its functionality. As I said at the beginning of the document, it is unfortunately not very well documented at all, but at least if you follow the advice I've posted here you'll have an excellent start for developing your Facebook applications.