-
Get a monthly update on best practices for delivering successful software.
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.
ails 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.
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.
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.
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:
If it does, we're on the right track.
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:
Congrats, we're up and running... sort of.
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 <img src='http://www.pathf.com/blogs/wp-includes/images/smilies/icon_mad.gif' alt=':x' class='wp-smiley' /> ml => @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.
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 %>
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
nly 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.)
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).
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.
Related posts:
Topics: Facebook, Ruby on Rails
Great post, I’m going to be looking into doing something with facebook (and bebo which I fear will be tougher) in the next few weeks so I’ll be back to consult this post
Comment by C, Thursday, July 31, 2008 @ 5:53 pm
Nice guide. Thanks.
Comment by LacKac, Wednesday, August 13, 2008 @ 3:35 am
Hi, I’m having problems posting to profile.
When you call ‘profile(fbml)’, it loads again in the stack entering and infinite loop.
Comment by Federico Soria, Friday, September 26, 2008 @ 6:14 pm
same here, got a “stack level too deep” exception on rails. anyone has figured this out?
btw, would this be due to a name conflict of the “user” model?
Comment by Alice, Thursday, November 6, 2008 @ 3:07 am
the sample code has a problem; it calls a method named profile within a method of the same name, thus causing a stack-too-deep issue. we’ll have to rename the method name to something else.
Comment by Alice, Thursday, November 6, 2008 @ 3:26 am
Facebooker is sorely under-documented and this isn’t helped by the fact that the tutorial has been broken (won’t load) since I started trying to use facebooker.
I can’t get profile updates to work, even after using the above code and correcting the infinite loop. It seems that the plugin has changed since you authored this as profile_main is not a function (thats the error I now get.)
I had to change some of the code in the plugin itself to get things working. It seems really under-tested, under-documented and under-supported. I’m considering going back to rfacebook.
Comment by chris, Saturday, December 27, 2008 @ 1:34 pm
[...] with two interesting additional components: a Facebook application and an iGoogle gadget. Though a Rails Facebook plugin was easy to find, Rails development tools for iGoogle weren’t as thick on the [...]
Pingback by Agile Ajax » Ask the readers: How do you keep DRY when exposing your Rails apps to iGoogle? » Pathfinder Development, Monday, January 12, 2009 @ 1:47 pm
After editing sshd_config, remember to restart your sshd service. Command would generally one of:
/etc/init.d/ssh restart (Ubuntu, etc.)
or
service sshd restart (Fedora, RedHat, etc.)
Comment by Michael Johnson, Thursday, March 5, 2009 @ 8:29 pm
Well, Josh, your tutorial stands head and shoulders above all other documentation for facebooker. Unfortunately, it wasn’t enough to get me up and running on Facebook.
To be fair, I’m trying to do an iframe app (it’s already all done, don’t ask me to redo it in FBML!), so there is that one difference. At best, I’ve been getting the error: Facebooker::Session::MissingOrInvalidParameter: Invalid parameter:
auth_token = 68db9e468e801338196d0a21bb7b1b73
Clearly, the auth_token is not missing. So what makes it invalid? *sigh*
Anyway, your tutorial is great, but facebooker still needs more testing and documentation. Heck, I’ll settle for just more documentation.
Comment by Steve, Friday, March 20, 2009 @ 12:31 pm
Hi, what if I want to make my facebook app on “top” of my actual rails app ?
Comment by tutoratr, Monday, July 27, 2009 @ 7:49 pm