+ All Categories
Home > Technology > Peepcode facebook-2-rails on facebook

Peepcode facebook-2-rails on facebook

Date post: 06-May-2015
Category:
Upload: sushilprajapati
View: 5,931 times
Download: 7 times
Share this document with a friend
67
by Shane Vitarana and David Clements $9 Hooking into the network Rails on Facebook
Transcript
Page 1: Peepcode facebook-2-rails on facebook

by Shane Vitarana and David Clements

$9

Hooking into the network

Rails onFacebook

Page 2: Peepcode facebook-2-rails on facebook

2

Rails on Facebook

©2008 Shane Vitarana and David Clements

Every effort was made to provide accurate information in this document. However, neither the authors nor Topfunky Corporation shall have any liability for any errors in the code or descriptions presented in this book.

This document is available for US$9 at PeepCode.com (http://peepcode.com). Group discounts and site licenses can also be purchased by sending email to [email protected].

"Rails" and "Ruby on Rails" are trademarks of David Heinemeier Hansson. "Face-book" is a trademark of Facebook. Other names are mentioned without any intention of infringement on the trademark.

Page 3: Peepcode facebook-2-rails on facebook

contents

Introduction 5Why Facebook?

Social Data

The Facebook Platform 7The Facebook API

FBML

FBJS

Getting Started 15Facebook Signup

Facebooker

Configuration

Hello World

Development Environment

Facebook Authentication 23

Verifying Request

Authenticating a User

Getting Social Data 26

User and Friend Data

Photos

Events

Anatomy of an App 28

Integration Points

The Profile Page

New Profile Application Tab

News Feed and Wall

Notifications

Emails

Invitations

Info Section

New Profile Publisher

Testing 52

Functional tests

Test accounts

Ruby Footprints App 54

Routes

Models

Advanced Topics 58

FBML and Image Caching

Bebo

Appendix: Terminology 62

Appendix: Models 63

The Authors

Credits

Revisions

Page 4: Peepcode facebook-2-rails on facebook

4

Page 5: Peepcode facebook-2-rails on facebook

5

Introductionchapter 1This book assumes that you know how to develop Rails applications and have a basic understanding of Facebook, or at least have setup a Facebook account and played around with it a bit. Occasionally you will find links to the Facebook wiki on various topics; this is because the Facebook development platform is constantly changing and the wiki is the best place to get the latest information about what is going on with a particular feature. These links will look like this: Developer Wiki Link (http://wiki.developers.facebook.com/index.php/Main_Page).

Throughout the book, we will be citing examples from a sample appli-cation called Ruby Footprints. Ruby Footprints is included with this book and also available on GitHub (http://github.com/digidigo/ruby_footprints/tree/master). We will keep this application up-to-date with Facebook API changes and anyone is free to contribute to it.

Why Facebook?People are spending an increasing amount of time on social net-works (http://lsvp.wordpress.com/2007/07/23/follow-up-on-top-social-networks-by-engagement). The ability to engage online with friends is slowly replac-ing more traditional forms of entertainment, like watching television. Applications on Facebook are popular because access to them is super easy from Facebook. Apps are discovered by getting invita-tions to them from your friends. Since people trust their friends more than random people on the web, they are more likely to try out your app. Furthermore, apps on Facebook don’t require separate logins. Users are more likely to use an app if they don’t have to sign up yet again on another site. The social elements of Facebook apps are also responsible for their popularity. It gives people a chance to interact with their friends in ways that weren’t possible before. Playing games

Page 6: Peepcode facebook-2-rails on facebook

6

with your friends, comparing scores on quizzes, and throwing sheep at old high school buddies, are replacing traditional avenues of com-munication, like email.

It is important to realize that Facebook is replacing other forms of entertainment. Facebook is a place where people come to play. Apps that do well on Facebook are those that facilitate social interaction. A clone of an office application, like Microsoft Word for instance, would not do well on the platform.

Social DataMany applications can be built simply based on knowing who a per-son’s friends are. But most of the data you see on a person’s profile can be used as part of your app. Facebook does not allow you to store any user data besides user IDs, but you can incorporate user data in real-time in your app. For instance, Video Jukebox (http://apps.facebook.com/jookbox) scans your favorite music to find music videos on YouTube, and allows you to embed them on your profile.

Page 7: Peepcode facebook-2-rails on facebook

7

The Facebook Platformchapter 2A Facebook web application is hosted on your own server, just like a regular Rails application. The application runs within Facebook, but requests are proxied via Facebook to your server. The diagram below illustrates this interaction:

the FaceBooK reQUest/response seQUence

User

fFaceBooK raILs app

fFaceBooK

raILs app

fFaceBooKUser

Page 8: Peepcode facebook-2-rails on facebook

8

All Facebook API responses have extra Facebook-specific parameters appended to them. These parameters contain information that will subsequently be used to communicate with the Facebook API.

note Every Facebook API request is a POST

The Facebook Platform also allows you to create desktop applications that make use of Facebook’s social data. However, since we’re talk-ing about Rails and Facebook, this book will only cover how to make Facebook web applications.

The Facebook APIThe Facebook API is a REST web service that makes available social data about users, and allows you to publish back to Facebook. The API is documented at the wiki (http://wiki.developers.facebook.com/index.php/Main_Page). Luckily, we do not have to use this API or parse the XML responses manually. Using a Ruby library like Facebooker (https://ruby-forge.org/projects/facebooker) can greatly simplify the task of developing a Facebook application.

FBMLFBML is an HTML-like language developed by Facebook to make view development easier and help your app conform to the look and feel of Facebook. FBML also provides many convenience tags to do com-mon tasks like displaying user names and constructing friend selec-tors. Some Ruby libraries, such as Facebooker, provide Rails helpers that wrap FBML tags to make the development experience more Rails-like, and absolve the developers from learning the HTML-like FBML syntax, which Rails developers have long forgotten by now.

Page 9: Peepcode facebook-2-rails on facebook

9

TagsThe countless FBML tags are all described on the developers wiki (http://wiki.developers.facebook.com/index.php/FBML). Probably the most useful tags provided by the platform are the ones that allow an application to render a person’s name and/or profile picture while only supplying the ID of the person. This is particularly useful when displaying infor-mation about Friends of the application users. Here is list of some of the more useful helper methods:

fb_name(uid, options)• With this tag you can not only render the users name but also make it possessive, reflexive and linked to the user’s profile

fb_pronoun(uid, options)• This also allows for the possessive, reflexive and objective forms of the pronoun

fb_profile_pic(uid, options)• You can control the size of the profile thumbnail and make it a link to the user’s profile

fb_comments(comment_id, options)• This allows the application to provide a comment section without having to store any of the data

fb_error(message)• Displays a Facebook-styled error message; there are also explanation and success messages

note There are many tags that can be useful in different contexts.

Some provide flow control, others are for specific facebook

features and still others are used to embed media content

FormsStandard rails helpers for web forms work as expected on Facebook Canvas pages. However, there is a special form helper to render a Facebook-styled form, called an editor. This set of helpers ends up rendering custom <fb:editor*> FBML tags. There are, however, a

Page 10: Peepcode facebook-2-rails on facebook

10

couple of gotchas with this form helper. The first is that the form is rendered in a table and as such it can be difficult to style it in certain ways. The second is that the FBML parser seems to strip out under-scores from the names of the field elements. So text_field(:users_friend, :name) will show up as params[:usersfriend][:name] . Here is an example of a facebook_form_for call:

editor.rhtml

<% facebook_form_for(:poke,@poke,:url => create_poke_path) do |f| %> <%= f.text_field :message, :label=>”message” %> <%= f.buttons “Save Poke” %><% end %>

FBJS

OverviewFBJS is Facebook’s approach to allowing developers to use Javascript in their applications. Instead of forcing developers to use IFrames for all their dynamic content, Facebook creates a Javascript Sandbox by limiting the abilities of the Javascript Environment, parsing all appli-cation Javascript, and scoping the methods and variables by prefix-ing variable names with the application ID. Also, note that all DOM IDs get changed in this way as well. For example:

function foo(bar) { var obj = {property: bar}; return obj.property;}

becomes

function a12345_foo(a12345_bar) {

Page 11: Peepcode facebook-2-rails on facebook

11

var a12345_obj = {property: a12345_bar}; return a12345_obj.property;}

A call to it would end up looking like:

var a12345_result = a12345_foo(“some_thing”)

Facebook also changes many of the core Javascript methods used for inspecting and interacting with the DOM. The entire list can be found on the wiki at FBJS—Facebook Developers Wiki (http://wiki.develop-ers.facebook.com/index.php/FBJS). Many of these method changes involve simply adding the prefix get to the method name. So className becomes getClassName and id becomes getId, etc.

Third-Party LibrariesBecause of the way Facebook parses all incoming Javascript, third-party Javascript libraries do not work within Facebook FBML pages. Facebooker developers have started re-creating pieces of the Pro-totype codebase in order to more easily support some of the Rails Javascript Helper methods, most notably the Ajax helper methods. This is a moving target in the Facebooker codebase and as of this writing $(‘element_id’) and link_to_remote and remote_forms are working when passing a DOM ID to update. RJS is not working and may not be able to work due to the absence of the eval method.

AJAXFacebook has two kinds of support for AJAX, Mock AJAX and the Local Proxy. When using Mock AJAX, Facebook makes the call to your server on the browser’s behalf and the response then goes

Page 12: Peepcode facebook-2-rails on facebook

12

through Facebook, gets processed the same as all requests, and returns to the Javascript callback. The callback can then either process JSON results or insert FBML by calling $(‘updated_div’).setInnerFBML(data). When using the Local Proxy, you may opt to send requests directly to your server, but in this case you cannot use any of the Facebook FBML tags in your response since Facebook will not have the opportunity to parse them before they reach the browser. This Local Proxy method uses a Flash Hack and requires that the Flash Plugin be installed. Refer to the wiki, FBJS_LocalProxy (http://wiki.developers.facebook.com/index.php/FBJS_LocalProxy) for complete setup details. Currently Facebooker does not have helper methods for the LocalProxy so you will have to roll your own AJAX.

The current Facebooker implementation for link_to_remote does not support javascript methods being called after the request has completed, so Ruby Footprints rolls its own Ajax call using Mock Ajax. It also uses the FBJS Dialog class to create a confirmation dialog that will eventually fire off the request. Here is the code from Ruby Footprints that grabs the friend ID to step on and sends it as an AJAX request. After the request is complete it does some animation to notify the user what was accomplished.

function submitForm(varForm, url, div_id){ try { //Grab the friend ID from the FORM name = varForm.serialize().friend_selector_name; friend_uid = parseInt(varForm.serialize().friend_to_step_on); if( friend_uid == undefined || friend_uid == 0 || isNaN(friend_uid) || name.length == 0) throw RangeError; //Confirmation Dialog sends request on Confirm dlg = new Dialog(); dlg.showChoice(‘Confirm Request’, “Are you sure you want to step on “ + name + “?” , ‘Yes’, ‘No’); dlg.onconfirm = function() { var ajax = new Ajax(); ajax.responseType = Ajax.FBML;

Page 13: Peepcode facebook-2-rails on facebook

13

ajax.ondone = function(data) { // Replace FBML $(div_id).setInnerFBML(data); //Animate the updated div. obscure(highlight(revealDiv(div_id).checkpoint()).checkpoint()).go(); }; ajax.post(url, varForm.serialize()); } } catch(err) { new Dialog().showMessage(“Error”,”Something weird happened. Did you type in a friend?”); } return false; }

note The obscure, revealDiv and highlight methods are

provided in the application.js file and are not directly a part

of FBJS.

Animation LibraryFacebook has actually released their animation library and, while it lacks the breadth of features of Scriptaculous, it does have a nice approach to animation by using method chaining. Here is a high-lighting animation example from Ruby Footprints:

Animation(document.getElementById(div_id).to(‘background’, ‘#FF00FF’).duration(2000).checkpoint().to(‘background’, ‘#F7F7F7’)

The library uses tweening of CSS attributes in order to get the job done. The above example takes 2 seconds to change the background to purple, stops for an instant, and then restores the background to Facebook’s standard grey. The library also provides support for hid-

Page 14: Peepcode facebook-2-rails on facebook

14

ing and showing elements, moving elements, and both default and custom ease functions. For a complete description, refer to the wiki at: FBJS/Animation. (http://wiki.developers.facebook.com/index.php/FBJS/Animation).

Page 15: Peepcode facebook-2-rails on facebook

15

Getting Startedchapter 3

Facebook SignupThe way you manage your Facebook applications is through an application created by Facebook called Developer. Install the app by logging into Facebook, going to the Get Started Page (http://developers.facebook.com/get_started.php), and clicking Add Facebook Developer Appli-cation (http://www.facebook.com/developers). After you have this applica-tion installed you can create a new application by clicking the ‘Setup New Application’ button. This will produce a form page on which to submit your application settings. The settings for Ruby Footprints are included at the end of each step.

Page 16: Peepcode facebook-2-rails on facebook

16

Application Name: It seems that this does not need to be unique across all Facebook apps.

Callback URL: This is the URL of the server hosting your application. Facebooker assumes that this URL will resolve to the document root of your application. Make sure you have the trailing slash on the end and also put this value in your facebooker.yml file under RAILS_ROOT/config.

Canvas Page URL: This must be unique across all Facebook applications and will be visible to your users in the location bar of the browser. Put the value that you choose into your facebooker.yml file.

Can your application be added on Facebook? After selecting YES here you will be prompted with another list of options.

TOS URL: Enter in your Terms of Service URL. This will show up as a link on your add application page and must either go to an external page or a page that is not authenticated to Facebook.

Page 17: Peepcode facebook-2-rails on facebook

17

Scrolling down, you will find Installation options for your application.

Check the Users box for 'Who can add your application'

Post-Add URL: This is the URL that gets called after a user adds your application. It is best to simply use your Canvas Page URL: http://apps.facebook.com/ruby_footprints

Developer Mode: Check this box to make sure that other users cannot add your application.

Default FBML, Default Action, and Default Profile Box Column are discussed in the Profile section below. You can leave them blank for now.

Application Description: This description will be shown to users when they are given a choice to add your application. It will also appear on the Application About Page and in the Application Directory. Make it enticing so people will add your application.

Next you will configure various integration points for your application.

Page 18: Peepcode facebook-2-rails on facebook

18

Side Nav URL: This is a very important integration point as it will result in an icon and link on the left of the user's profile. It must go to a Canvas Page.

Help URL: This can be an external URL, a Facebook Canvas page that shows help, or a FAQ for your application.

Privacy URL: This will create a link that the user will see when editing the application privacy settings. It should be a Facebook Canvas page.

Private Installation: This will prevent news stories from being published by your application, and is a good idea during development.

Page 19: Peepcode facebook-2-rails on facebook

19

After you save your application settings you will be taken to a sum-mary page that will have your API and Secret keys. Take these keys and place them into your facebooker.yml file. The Ruby Footprints facebooker.yml file looks like this:

development: api_key: 921628d9d70f5e0b5887936802abe9e6 secret_key: 77sEcRETsecretSECRET3ce96c canvas_page_name: ruby_footprints callback_url: http://rfootprints.shacknet.nu

FacebookerFacebooker is our Facebook library of choice. Facebooker goes to great lengths to abstract the XML-based Facebook API calls, so you only have to be aware of pure Ruby objects. Facebooker can be used as a gem or a Rails plugin. I prefer to use it as a plugin, as it takes care of all the initialization needed to integrate the Facebook API with Rails.

Facebooker has recently moved the primary repository over to GitHub and you can install it in a few ways. If you are on Rails 2.1 and have installed git, you can use script/plugin:

script/plugin install git://github.com/mmangino/facebooker.git

If you are on an older version of Rails but still have git installed, you can clone it to the vendor/plugins directory.

cd vendor/pluginsgit clone git://github.com/mmangino/facebooker.git

Page 20: Peepcode facebook-2-rails on facebook

20

Or, download the source directly from GitHub (http://github.com/mmang-ino/facebooker) and unpack it into your vendor/plugins directory.

note Preferably we would be able to use Piston to manage this

plugin, but it does not currently support git. There are some

workarounds but the Rails community hasn’t really settled on

the correct approach yet.

Facebooker does its best to make Facebook app development as close to regular Rails app development as possible. The plugin mon-keypatches Rails controllers to include everything you need to access Facebook, via the facebook_session method.

ConfigurationFirst, generate a basic facebooker.yml configuration file using:

rake facebooker:setup

Fill in your server info appropriately. For example, if your public server is shanesbrain.net and it has port 4007 free, then facebooker.yml will look like:

development: tunnel: public_host_username: shane public_host: shanesbrain.net public_port: 4007 local_port: 3000

Page 21: Peepcode facebook-2-rails on facebook

21

Hello WorldThis simple application will print “Hello” followed by your first name.

hello_world.rb

class Welcome < ApplicationController def index @first_name = facebook_session.user.first_name endend

Hello <%= @first_name %>

In order to test this, you will have to set up a development environ-ment or deploy your application to Facebook.

Development EnvironmentIdeally, you want to be able to test your app as you develop, like you would a normal Rails app. Simply running the Mongrel develop-ment server will not suffice, since that will not route requests through Facebook. Thankfully, the Ruby community is proud to include smart people like Evan Weaver, who came up with a way to test Facebook apps locally using a reverse SSH tunnel (http://blog.evanweaver.com/arti-cles/2007/07/13/developing-a-facebook-app-locally). There is no need to worry about the details of setting it up, as Facebooker provides rake tasks for completing the setup. As long as you have a public facing server with available ports, you can set up a reverse tunnel to your local machine. Facebook will communicate with your public server as if it were a normal Facebook app, while your server routes the requests to your machine.

Start the tunnel with:

Page 22: Peepcode facebook-2-rails on facebook

22

rake facebooker:tunnel:start

You may check the status of the tunnel with:

rake facebooker:tunnel:status

If you don’t have a public facing server with open ports, you can try a service called Tunnlr (http://tunnlr.com).

[Tunnlr is] a forwarding service that you can use at home, at Starbucks, or anywhere you have an Internet connection…It securely connects a port on your local machine to an open port on our public server. Once you start your Tunnlr client, the web server on your local machine will be available to the rest of the world through your special Tunnlr URL.

Page 23: Peepcode facebook-2-rails on facebook

23

Facebook Authenticationchapter 4The Facebook API uses a shared secret called the secret_key to authenticate all communications between your application and the Facebook API. It is implemented by concatenating all the request parameters together with the secret_key and running a hashing func-tion on the result. Facebooker gives your application high level access to the authentication and permissions mechanisms provided by the Facebook Platform.

Verifying RequestEven if your application does not require any special permissions from its users, it is still a good idea to verify that the incoming request has not been tampered with. To accomplish this add a before filter for set_facebook_session to your ApplicationController. This call will raise an Exception if the request is not valid. After a successful call to set_facebook_session the instance variable @facebook_session will be set and session[:facebook_session] will be stored.

note Facebooker overrides the rails Session Key in order to retain

a session across requests for Facebook users. The call to

set_facebook_session will look into session[:facebook_session] in order to see if there is already a session object

available.

Authenticating a UserIn order for your application to take action on the behalf of a user or to gain extra permissions, Facebook will issue a session key for that

Page 24: Peepcode facebook-2-rails on facebook

24

user. These actions can include posting to a news feed or sending notifications and there are currently two types of session keys, infinite and expirable. An Application can request a session key in one of two ways, by sending the user to the Application Login page or sending the user to the Application Add page. Facebooker supports both of these through class level calls available in the controller; both of these calls take the same options as before_filter.

The ensure_authenticated_to_facebook method will attempt to validate the Facebook parameters and on failure will redirect the user to the Login URL. At this URL the user will basically be asked whether they want to grant an infinite session key or a session key that will expire.

The ensure_application_is_installed_by_facebook_user method will validate the Facebook parameters and check to see if the the parameter indicating that the user has added the application is set. Most applications will require that the user add the application as it not only can give the application more permissions but it also will publish the choice to the user’s news feed.

By default after a user adds the application Facebook will redirect the user to the Post Add URL that was setup in the developer app. In some cases this is not ideal, however. Overriding this behavior is supported by the Facebook API, although currently Facebooker doesn’t give easy access to changing this. In order to send your users to some other URL you will need to override the controller method application_is_not_installed_by_facebook_user and pass in a :next parameter to the install_url. Here is an example from Ruby Footprints:

ruby_footprints/app/controllers/application.rb

def application_is_not_installed_by_facebook_user redirect_to session[:facebook_session].install_url(:next => next_path() )end

Page 25: Peepcode facebook-2-rails on facebook

25

private

def next_path

non_keys = [ “method”, “format”, “_method”, “auth_token”] url_hash = {} params.each do |k,v| next if non_keys.include?(k.to_s) || k.to_s.match(/^fb/) url_hash[k] = v end url_hash[:only_path] = true url_hash[:canvas] = false url_for(url_hash) end

note There is surely a cleaner way to create a URL from the request

parameters. Please do investigate.

Page 26: Peepcode facebook-2-rails on facebook

26

Getting Social Datachapter 5

User and Friend DataYou can access nearly all the data that is public on a user’s profile once he or she installs your application. You may use this data in your application, but Facebook has limits on what you may store persistently. You can generally store numerical identifiers such as user IDs (UIDs) and photo IDs (PIDs), but nothing more. It is also illegal to store friend relationship id pairs, such as <uid, uid>. Refer to the developer documentation (http://wiki.developers.facebook.com/index.php/Main_Page) for storage guidelines.

For example, here is how to fetch a user’s favorite music:

facebook_session.user.populate(:music)music = facebook_session.user.music

Assuming the user has a comma-separated list of artists, you can split the string to get each artist. You can use a similar approach to get any number of user data, including activities, interests, favorite movies, etc. See the sidebar for a complete list.

PhotosFacebook organizes photos into albums, and each photo has its own photo id (pid). You can specify either a list of photo IDs, an album id, or a subject id. The subject id is the UID of a specific user. Specifying the subject id will retrieve all the photos in which the particular user was tagged.

complete list of user data

The facebook_session.user.populate() method accepts one or more of the following symbols. Leave blank to get them all.

:status, :political, :pic_small, :name, :quotes, :is_app_user, :tv, :profile_update_time, :meeting_sex, :hs_info, :timezone, :relationship_status, :hometown_location, :about_me, :wall_count, :significant_other_id, :pic_big, :music, :uid, :work_history, :sex, :religion, :notes_count, :activities, :pic_square, :movies, :has_added_app, :education_history, :birthday, :first_name, :meeting_for, :last_name, :interests, :current_location, :pic, :books, :affiliations

All of the above return String values, except for location, high school info, status, affiliation, education info, and work info. These return their own model objects that have accessors for their attributes. For example:

location_example.rb

facebook_session.user(:location)# => Location objectlocation.city# => Chicago

Page 27: Peepcode facebook-2-rails on facebook

27

Facebooker includes a few convenience methods for common photo tasks:

facebook_session.user.albums•

facebook_session.user.profile_photos•

The albums method retrieves a list of album IDs (aids) for the cur-rently logged in user. These can be plugged into facebook_session.get_photos to get the photos for each album. Note that pid, subj_id, or aid must be supplied to facebook_session.get_photos. For example, all the incriminating photos for user 12345 can be retrieved with facebook_session.get_photos(:subj_id => 12345).

Facebook has a special album for profile photos, which cannot be retrieved using the method above. You need to do some trickery involving complex mathematics to get this album. Luckily, the smart developers of Facebooker have abstracted the complexity with the profile_photos method. It simply retrieves all the profile photos for the currently logged in user.

EventsFacebooker provides a convenience method to get the events for a user. You can filter events by start time, end time, and RSVP status. The following returns the events between now and a month from now:

@user.events(:start_time => Time.now.to_i, :end_time => 1.month.from_now.to_i)

Once you have a list of Event objects, you can access the attributes of each Event, such as description, venue, host, location, etc.

Page 28: Peepcode facebook-2-rails on facebook

28

Anatomy of an Appchapter 6

Integration PointsYour main application runs inside a Facebook canvas page, but Face-book allows additional means to interact with, promote, or advertise an action of your application. Your application is entitled to its own box on a user’s Facebook profile page. The content of this box can be rendered differently based on whether users are viewing their friends’ profiles or their own. You also have the option of adding a link under the user’s profile picture.

Feed items (not to be confused with RSS/Atom feeds) are little blocks of content that you publish based on actions taken in your applica-tion. For example, if you have an application that spanks a friend, then it can publish a feed story informing the user and his or her friends about the spank. Now don’t get the bright idea to make a spanking application, as it has already been done, and no, not by me. There are two types of feed items, the News Feed and Mini-Feed. The News Feed contains stories about all your friends’ activities while the Mini-Feed is only for activities that are either initiated by you, or involve you in some way (for example, if someone tagged a photo of you).

Facebook has two types of notifications that are meant to serve as individual communication between the application and the user. Reg-ular notifications go to the user’s notification page, which is accessed by a link in the right sidebar on the Facebook homepage if the user is logged in. This page contains information that Facebook considers relevant yet not important enough to go into the user’s News Feed. The other type of notification is email notifications. As expected this sends an email to the user on behalf of your application, as long as

Page 29: Peepcode facebook-2-rails on facebook

29

their privacy settings are set to allow it.

Facebook also has hooks to allow your application content to be included in wall and message attachments.

Summary of Integration Pointsfacebook home page

Message Inbox•

Notifications•

Status Updates•

News feed•

Requests•

profile page

Narrow box on left side, under picture•

Link under profile picture (this is not available in the new Facebook •layout)

tabs

Boxes tab: Narrow or Wide profile box•

Application tab•

Info tab box•

Wall (previously called the Mini-Feed in the old layout)•

Status update•

email

Attachments•

Share messages•

Page 30: Peepcode facebook-2-rails on facebook

30

other

Application description and image in the Application Directory•

Application reviews and discussions in your About page•

On all pages, your application can be shown on the Sidebar applica-tion list if the user allows it.

Overall, you have quite a few choices for integrating your application tightly with Facebook to create a seamless user experience.

The Profile PageThe Profile Page on Facebook provides a space for your application to have a presence. This space involves a few restrictions.

The user must add your application and grant permission to your •application

The content must be explicitly published to the profile through the •Facebook API

You can use • FBJS but it will not run until the user clicks on the space, and it cannot communicate with your server

The content is restricted to one of two widths: narrow or wide•

There are two main parts of a user’s Profile, the Profile Action and the Profile FBML. The Profile Action is

simply a link that goes below a user’s profile picture. The Profile FBML

Page 31: Peepcode facebook-2-rails on facebook

31

is a piece of FBML that allows a user and the user’s friends to see what the user has been doing with your application. The Ruby Foot-prints Application shows a tally of the number of times the user has stepped on and been stepped on. The Profile Action, if you set it up correctly, shows up as a link below every profile that your user looks at (see Profile Wiki link (http://wiki.developers.facebook.com/index.php/Profile)).

Both the Profile FBML and the Profile Action can have defaults set for the appli-cation. Many apps will setup the Default FBML to simply contain an fb:ref tag. This way the content can be updated for all users of the app with one request. By setting the default Profile Action for your application you actually gain something very powerful. The link will appear under the profile for every profile your application users view, not just the profiles of people who have added the application. You can set both defaults in the application settings page. Here is the default Profile Action for

Ruby Footprints:

<fb:profile-action url=”http://apps.facebook.com/ruby_footprints/step/on”> Step On <fb:name uid=profileowner firstnameonly=”true”/> </fb:profile-action>

There are a couple of ways in Facebooker to publish to a user’s Pro-file. The simplest way is through methods directly on an instance of a Facebooker::User. Its available methods are:

publish_profile.rb

Page 32: Peepcode facebook-2-rails on facebook

32

@facebooker_user.profile_fbml = “A string of FBML”@facebooker_user.mobile_fbml = “A string of FBML for mobile profile”@facebooker_user.profile_action = ‘<fb:profile-action url=”http://www.mysite.com/action/”> Perform Action </fb:profile-action>’ @facebook_user.profile_main = “FBML for a Profile Box on the Main Profile”@facebook_user.set_profile_fbml(profile_fbml, mobile_fbml, profile_action)

These methods are of limited use since you will have to code up the FBML manually or hack in your own template rendering.

The preferred method of managing a user’s Profile page is through what we will call the Publisher API. Facebooker::Publisher uses a simi-lar approach to ActionMailer and it allows you to render templates and to use helper methods to create your FBML. The Publisher API currently supports sending Feed Stories, Notifications and Emails, set-ting a user’s Profile, and setting FBML reference handles. Here are the basic steps to creating a Publisher:

Run the Publisher generator • ruby script/generate Pub-lisher Facebook. This will create a class that extends Facebooker::Publisher.

Define a method with any number of arguments and call it what-•ever you would like, as long as it does not conflict with other instance methods.

In that method call the method • send_as with the type of thing you are publishing. Available choices are :profile, :email, :notifica-tion, :story, :action, :templatized_action, and :ref.

Call • FacebookPublisher.deliver_<method_name>(*args).

Here is an example from Ruby Footprints of setting the user’s Profile FBML and Profile Action using the Publisher API:

ruby_footprints/app/models/facebook_publisher.rb

def profile_for_user(user_to_update)

Page 33: Peepcode facebook-2-rails on facebook

33

send_as :profile from user_to_update.facebooker_user recipients user_to_update.facebooker_user fbml = render({ :partial =>”/footprints/user_profile.fbml.erb”, :locals => {:user => user_to_update} }) profile(fbml) profile_main(fbml) action = render(:partial => “/footprints/profile_action.fbml.erb”) profile_action(action)end

note The above example calls the recipients method, which might

seem a little strange but the Facebook API allows you to set the

Profile for any of your users using a single session key.

With Facebook’s New Profile Design, an application’s Profile Box can live in one of two places, inside the Boxes Tab and on the Main Profile Page. An application will need to ask a User to explic-itly request the applications presence in one of these places. This request is handled via a custom FBML tag, <fb:add-section-button section=”profile” />. If you notice in the example above, the pub-lisher code calls both profile and profile_main. The call to profile will set the profile FBML for the Boxes Tab while profile_main will set the FBML for the user’s Main Profile.

note The Profile Action link has been deprecated by Facebook and

will not be available in their next release of the Platform. This

change is corresponding with the final edits of this book so

we decided to leave it in just in case you see references to it

elsewhere.

Page 34: Peepcode facebook-2-rails on facebook

34

New Profile Application TabThe new Facebook Profile design gives applications a new integration point. Your application can get access to some coveted real estate on the user’s profile. Instead of there being application profile boxes on the main user profile, users will have to select applications that they would like to highlight on their own profiles. To give your users access to this tab you configure both the Profile Tab URL and Profile Tab Name in the application settings. The Profile Tab URL is a canvas page URL and there a few differences with this FBML canvas:

You cannot use IFrames•

The session key that you get is a temporary key and is different •from the user’s infinite session key, it is also a read-only

It doesn’t know who the viewing user is•

Flash and Javascript have the same restrictions as the Profile Box•

Once your application is setup to support the Profile Tab, when a user loads that particular tab Facebook will call the Profile Tab URL. You can add dynamic interaction with the Tab via AJAX; however, all HREF links will go to your canvas page.

note Since this feature is so new it is likely to change as users and

application developers interact with it. Check the wiki under

Tabbed Profile (http://wiki.developers.facebook.com/index.php/New_Design_

Tabbed_Profile) for the latest information.

News Feed and WallThere are three different ways to publish stories to a user’s feeds. Each way gives you access to either the Wall or News Feed or both. You also get the ability to publish stories to friends that have not

Page 35: Peepcode facebook-2-rails on facebook

35

added your application. This last feature may sound very appealing for its possibilities of rapidly propagating your application; don’t get your hopes up too high, though, as Facebook’s standards appear to be very restrictive for this type of messaging. The table below shows the difference between each method:

Feed Action Story TemplatizedAction

Wall Yes No Yes

News Feed Friends’ with app Own All Friends’

Usage Limits 10 times every 48 hours once every 12 hours 10 times every 48 hours

Facebooker exposes each of these API calls via the Publisher API, as introduced above.

Publishing StoriesIn the Ruby Footprints example application we decided to nudge users back to the application by notifying them once a day of their friends’ activities. Many applications have some sort of visual compo-nent, perhaps a photo or an icon of a gift. For Ruby Footprints we will include our Ruby Footprints logo to add some flare to the feed story.

ruby_footprints/app/models/facebook_publisher.rb

def nudge_story(user) send_as :story recipients(Array(user.facebooker_user))

db_friends = FacebookUser.find_friends(user.friends)

if( db_friends.blank? ) title = “None of your friends stepping on people!!” else title = db_friends[0..3].compact.collect{ |friend| fb_name(friend.uid) }.to_sentence + “ have been stepping on people.”

Page 36: Peepcode facebook-2-rails on facebook

36

end

body = “Get <a href=’#{footprints_url}’> stepping </a> on your friends.”

body = body[0..200] if body.length > 200 self.body( body ) self.title( title ) image_1(image_path(“tiny-footprint.png”)) image_1_link(footprints_url())end

note There are limits on your title and body length, excluding tags, so

if these are generated dynamically make sure to truncate them,

otherwise you will get an error from Facebook.

Publishing Templatized ActionsPublishing a templatized action adds some complexity to the pro-cess. The reason behind this is aggregation. In order for Facebook to increase the quality of News Feed items, they attempt to aggregate similar stories into a single item on the News Feed. This is a good thing for developers as it gives you more of a chance for your story to appear on the News Feeds of friends. The more stories you pub-lish, in theory, the more of a chance you have of your aggregated story getting published. In general the body_template, body_data, title_template, title_data, and target need to be identical for a story to be aggregated. So the story “David stepped on Shane” can become “David and Doug stepped on Shane”, but only if we both stepped on Shane. You should definitely read up on the wiki (http://wiki.developers.facebook.com/index.php/Feed.publishTemplatizedAction) to understand better how aggregation works; it can be very powerful. And notice that you will need to go through the steps of registering the templates before you can take advantage of the aggregation.

Page 37: Peepcode facebook-2-rails on facebook

37

Here is Ruby Footprints method from your FacebookPublisher class:

ruby_footprints/app/models/facebook_publisher.rb

def footprint_feed_item(attacker, victim) send_as :templatized_action from(attacker) title_template “{actor} stepped on {target}” target_ids([victim.id]) body_general(“Check out #{link_to(“#{fb_name(victim, :possessive => true)} Ruby Footprints”, new_footprint_url(:friend_to_step_on => victim.id )) } “) image_1(image_path(“tiny-footprint.png”)) image_1_link(footprints_url())end

And here is what it will look like in the Mini-Feed and (if you are lucky) the News Feed:

note The call to target_ids is a specific parameter for these types

of actions. Facebook will use these IDs in order to aggregate

stories from many different users.

Publishing ActionsPublishing Actions is very similar to publishing Stories; in fact the Facebooker::Feed::Action class is a duplicate of the Story class. Actions, like stories, have a title, a body, and images. Since we are

Page 38: Peepcode facebook-2-rails on facebook

38

using Templatized Actions in Ruby Footprints we don’t really have any use for regular actions. Here is some example code; notice that it is very similar to the code for sending a Story.

ruby_footprints/app/models/facebook_publisher.rb

def example_action(user) send_as :action from( user) title “just learned how to send a Mini-Feed Story using Facebooker.” body “#{fb_name(user)} just checked out #{link_to(“Ruby Footprints”, footprints_url)}. A Ruby based Facebook example application.” image_1(image_path(“tiny-footprint.png”)) image_1_link(footprints_url())end

note Not only can you use helper methods in there, like fb_name, but

you can call render :partial => ‘/some/partial’.

The New Facebook Feed Story FormatSometime in the Summer of 2008 Facebook will finally release its new Profile design and with that comes a new format for News Feed Items. The purpose of this new format is to give users a greater amount of control over their personal profile pages. Users will be given control over how Mini-Feed stories appear on their profiles and application developers will have some work to do in order to support this greater level of control.

Mini-Feed stories will now support three different lengths of stories, one-line, short, and full. The API requires that you register these sto-ries before using them and the nice thing is that registering is built into the API and Facebooker supports it. When generating a new Publisher, Facebooker will create a database migration to store all the needed template IDs since a template ID is required to publish feed

Page 39: Peepcode facebook-2-rails on facebook

39

stories. Your Publisher works very much the same way as the Pub-lishTemplatizedAction feed stories, only it also need to support the registering of the templates. Here are the steps:

Create a method that describes your template. Call it something •like mini_feed_template (it must end in template)

Describe the template with three methods: • one_line_story_tem-plate, short_story_template(title, body), and full_story_template(title, body)

Create a method for delivering the feed story in the same way that •TemplatizedActions are supported. It must start with the same pre-fix as your template method. e.g. def mini_feed

Call • send_as :user_action, from(user), and data(hash_of_data_to_populate_the_template) inside the delivery method

Call • FacebookPublisher.register_mini_feed at some point during the flow of your app

And here is an example from the Ruby Footprints application.

ruby_footprints/app/models/facebook_publisher.rb

def new_footprint_feed_item_template one_line_story_template(“{*actor*} stepped on {*target*}”) short_story_template(“{*actor*} stepped on {*target*}”, “You can {*general_step_link*} on people too! With Ruby Footprints.” ) full_story_template(“{*actor*} stepped on {*target*}”, “{*image_link*} <p>Don’t let them get away with treating each other this way.</p><p> {*call_to_action*}</p>”)end

def new_footprint_feed_item(footprint) victim = Facebooker::User.new(footprint.victim.uid) attacker = Facebooker::User.new(footprint.attacker.uid) send_as :user_action from(attacker) target_ids([victim.id]) data({ :general_step_link => link_to(“Step”, footprints_url) ,

Page 40: Peepcode facebook-2-rails on facebook

40

:image_link => link_to(image_tag(“tiny-footprint.png”), footprints_url), :call_to_action => “Check out #{link_to(“#{fb_name(victim, :possessive => true)} Ruby Footprints”, new_footprint_url(:friend_to_step_on => victim.id )) } “ })end

note The place holders for template data have a different format than

they did with the TemplatizedAction templates. The key is

surrounded by asterisks, as in the example above.

NotificationsNotifications are generally shorter than feed items and do not con-tain any images. They are handled by the Publisher API and simply have a sender, recipients and a block of FBML. A Notification can be a great way to spread your application by notifying friends that a user has taken some action that involves them. Ruby Footprints uses notifications to let other users know that they have been stepped on so that they can retaliate and do some stepping on their own.

ruby_footprints/app/models/facebook_publisher.rb

def footprint_created(footprint) send_as :notification victim = Facebooker::User.new(footprint.victim.uid) attacker = Facebooker::User.new(footprint.attacker.uid) # Note this only works during a request cycle # since the session is being held for us self.recipients(victim) self.from(attacker) fbml “ stepped on you. #{link_to(“See all your Ruby Footprints” , footprints_url) } “end

Page 41: Peepcode facebook-2-rails on facebook

41

note One thing to note is that when you send a notification to a user

on another user’s behalf, the sender will also get a notification

that they sent something, so be careful not to abuse the system,

or your users will know and punish you for it.

EmailsYour application can send emails to users who have added the application. The email can contain both a text version and an HTML version. Do not try to do anything fancy though, because the set of allowed tags is limited to tags that result in text only. The email actually appears to come from your application so you cannot send emails from one of your users to another; that is what notifications are for.

ruby_footprints/app/models/facebook_publisher.rb

def example_email(from,to, title, text, html) send_as :email recipients(to) from(from) title(title) fbml(html) text(text)

Page 42: Peepcode facebook-2-rails on facebook

42

end

Like notifications, the limit on emails is calculated per application, and once again a big part of that calculation is user feedback. The email itself will have a link for your users to click in order to block your application from sending any more emails, so be kind. See the side-bar for details on allocation limits.

note If your notifications or emails fail, make sure that you are not

sending any markup other than text and links. Facebook will

happily drop your notification on the floor and give you a

successful response. Start simple.

InvitationsInvitations in Facebook are another way for your users to interact with each other. Invitations differ from previously mentioned ways of com-municating with users in that they must be specifically initiated by a user. Probably the most common invitation is a request to install an application. Some applications go so far as to require users to do this in order to unlock certain functionality, while others will offer additional points or benefits based on the number of friends that get invited. The Invitation is handled through a few specific FBML tags and Face-booker has methods for each of those. Here are all the pieces you will need to put this together:

Text for the call to action button on the invite itself•

Text for the send button•

URL• for the recipient to click on

URL• to go to after sending the Invitations. This request will include the IDs of the invite users and can be used for tracking purposes

Page 43: Peepcode facebook-2-rails on facebook

43

Call to action text for user sending the Invitations•

Ruby Footprints gently encourages users to pass the word by pro-viding a link at the bottom right of the layout. This link renders the following FBML:

ruby_footprints/app/views/admin/invite_friends.fbml.erb

<% content_for(“invite_user”) do %> Check out Ruby Footprints. It is an example Facebook app built with Ruby. <%= fb_req_choice(“Check it out!”, friend_footprints_url()) %><% end %>

<% fb_request_form(“Ruby Footprints”, “invite_user”, invited_friends_url()) do %> <%= fb_multi_friend_selector(“Invite your friends to check out Ruby Footprints.”, :showborder => true, :exclude_ids => current_user.friends_with_this_app.map(&:id).join(“,”)) %><% end %>

The first part of this template sets up the content for the Invitation itself. This is what the recipient will see. The call to content_for basi-cally assigns the FBML from the block to an instance variable to be later used by the request form. The name passed into this method is the same name that is passed into fb_request_form. The con-tent includes an fb_req_choice tag which renders the button to be clicked by the recipient of the invitation and includes the URL that the user will go to when called to action.

Next we have the friend selection form, accessible with the fb:request-form tag as shown at the wiki (http://wiki.developers.facebook.com/index.php/Fb:request-form). Familiarize yourself with all the different permutations, as they can be useful in different circumstances. The request form takes a String for the button the user clicks once all the friends are selected, and a URL to go to after the Invitations are

Page 44: Peepcode facebook-2-rails on facebook

44

sent out. This callback URL will contain the IDs of the users that were selected. Ruby Footprints simply stores the IDs in the session to be used in a flash message.

The request form takes a block and this block needs to render one of the many Facebook friend selector widgets. Ruby Footprints is using the multi friend selector, which is rather menacing, but popular among applications. You will notice, if you play with Ruby Footprints, that there is an indication on the form indicating how many people can be invited. This number is one of the allocation limits that your application is governed by and is a per day limit.

Page 45: Peepcode facebook-2-rails on facebook

45

Info SectionIn the New Facebook Profile, applications have the ability to provide lists of data that users can embed into the Info Tab on their Profiles. This integration is done with an explicit action from the user clicking on a button provided by a custom FBML tag, <fb:add-section-button section=”info” />. Before the user clicks on this button

Page 46: Peepcode facebook-2-rails on facebook

46

the application must publish some data to Facebook in a list format. You can check out the wiki for all the details at Profile.setInfo (http://wiki.developers.facebook.com/index.php/Profile.setInfo). The format of the JSON data is an array of hashes with an array of hashes inside. This can be a little tricky, so here is an example script that can be used to publish this list data:

set_info_list.rb

require File.dirname(__FILE__) + ‘/../config/environment’ENV[“FACEBOOKER_API”] = “new”def footprints_info_list [ { :label => “Red Footprints”, :link => “apps.facebook.com/rubyfoot_dev”, :image => ActionController::Base.asset_host + “/images/tiny-footprint.png” } ]end

info_fields = [ { :field => ‘Ruby Footprints’, :items => footprints_info_list }]

FacebookUser.find(:all).each do |user| user.set_profile_info(“Ruby Footprints”, info_fields)end

New Profile PublisherFacebook recently launched a new way to publish content, that aligns with their new Publisher feature. Publisher (not to be confused with Facebooker::Publisher) is a new tool that allows applications to

Page 47: Peepcode facebook-2-rails on facebook

47

publish any kind of content to a user’s, or a user’s friend’s, Wall. This is going to be an important integration point due to its prominence on the user’s profile page. It is right at the top and sorted by most recent activity. The Publisher is much more powerful than the Mini-Feed it replaced. You can now publish FBML, Flash, or even load FBJS.

To begin using the Publisher, you need to specify an action and a callback URL in the application settings.

There are two sets of actions and callback URLs, one for publishing to your own profile and one for publishing to your friends’ profiles. When you have configured the above, your application will show up in the drop-down list near the Publisher bar on the Profile page, provided the user has agreed to show it in your application’s Terms of Service:

Page 48: Peepcode facebook-2-rails on facebook

48

Your application first needs to render its own version of the Publisher interface. Then it must generate the Feed story for the content to be published.

Facebooker comes with support for Facebook’s Publisher. You first need to check if Facebook is requesting the interface for Publisher by calling wants_interface? If true, you can render the form using render_publisher_interface. This builds the JSON for the Pub-lisher interface. If the interface has already been rendered, you can publish the content using render_publisher_response. This method takes in a UserAction object, which is provided by the Facebooker::Publisher class.

Previously we discussed how to publish Feed Stories using the new API provided by Facebook. The API and code for this new user-con-trolled publishing uses the same mechanism and API as that used to publish Feed Stories. First the application creates and registers a template, then when asked the application returns the data for that template. Ruby Footprints allows the user to publish his own stats to his Wall; the controller logic is in AdminController, and the template and template data are in the FacebookPublisher. Here is the con-troller:

ruby_footprints/app/controllers/admin_controller.rb

def publish_self user = FacebookUser.find_by_uid(params[:fb_sig_profile_user]) FacebookPublisher.register_publish_self unless

Page 49: Peepcode facebook-2-rails on facebook

49

Facebooker::Rails::Publisher::FacebookTemplate.find_by_template_name(“publish_self_item”) if wants_interface? render_publisher_interface(render_to_string(:partial=>”/footprints/user_profile”, :locals => {:user => user})) else render_publisher_response(FacebookPublisher.create_publish_self(user)) endend

This one action must respond to requests for the form that takes in the user data and the request which returns the JSON-encoded data for the template. In our case we are not actually requesting any form data from the user, but typically this is what an application would want to do. Notice that we check to see if the template has been registered first; this is not very efficient from a programming perspec-tive but makes the example clearer. Now take a look at the template definition and the template data in the FacebookPublisher class.

ruby_footprints/app/models/facebook_publisher.rb

def publish_self_template title = “{*actor*} Is a Ruby Footprint Master” one_line_story_template(title) short_story_template(title, “You can {*general_step_link*} on people too! With Ruby Footprints.” ) full_story_template(title, “{*fbml*}”)end

def publish_self(user) send_as :user_action from(user) data({ :general_step_link => link_to(“Step”, footprints_url), :fbml => (render :partial => “/footprints/user_profile.fbml.erb”, :locals => {:user => user}) })end

These definitions work exactly like the definitions used for publishing

Page 50: Peepcode facebook-2-rails on facebook

50

Feed stories.

note In order to create the UserAction object we call

FacebookPublisher.create_publish_self. This is

another way the that Facebooker::Publisher is built around

the same concepts as ActionMailer.

Page 51: Peepcode facebook-2-rails on facebook

51

programmatic configuration

Facebook continues to add more and more programmatic access to configuration and application data. One of the these that you can programmatically verify is the alloca-tion limits put upon your application. There are allocation limits for how many emails, notifications and requests are sent.

The limit on Notifications is calculated per application, and a big part of that calculation is user feedback. You can check the current limit programatically and from within the Developer application under Stats • Allocation. The initial limit is 20 per day and Facebooker will raise an exception after you have reached the limit. Check it by calling:

@facebook_session.admin.get_allocation(:notifications_per_day)

Like Notifications, the limit on Emails is calculated per application, and once again a big part of that calculation is user feedback. The email itself will have a link for you users to click in order to block your application from sending anymore emails, so be kind. If Facebook likes your applica-tion it will put this disable link at the bottom of the email. The initial limit is 5 per day and Facebooker will through an exception after you have reached the limit. You can check the limit by calling:

@facebook_session.admin.get_allocation(:emails_per_day)

You can also see where the disable message is by calling:

@facebook_session.admin.get_allocation(:email_disable_message_location)

A certain number of invitations can be sent from your application per user per day. You will see this as a limit on the friend selector widgets provided by Facebook. Check it programmatically:

@facebook_session.admin.get_allocation(:requests_per_day)

Page 52: Peepcode facebook-2-rails on facebook

52

Testingchapter 7

Functional testsTesting Facebook apps is not much different from testing normal Rails applications using the helpers that come with Facebooker. Unit tests are pretty much the same as Rails tests. However, functional tests require a little extra work since the Facebook parameters must be appended to all requests. Luckily, facebooker comes with the facebook_post method that replaces the normal post method that comes with Rails. The facebook_post method will handle appending the Facebook request parameters and generating the signature, so you really do not have to know what is going on behind the scenes, and can use facebook_post just as you would use post in a Rails functional test.

Here is an example using all the test helper methods available with Facebooker.

functional_test.rb

def test_create assert_difference ‘Footprint.count’ do facebook_post ‘create’, :id => 2 end assert assigns(:stepped_on_user) assert_facebook_redirect_to(‘http://apps.facebook.com/rubyfoot_test/’)end

Actions that use the Facebook canvas use facebook_post to simu-late a post to Facebook with the required parameters. Rails’ usual assert_redirect does not work in the Facebook environment since the request has to pass through Facebook’s server first. Rather, a

Page 53: Peepcode facebook-2-rails on facebook

53

Facebook redirect is a POST to Facebook with a special FBML redirect tag. The assert_facebook_redirect_to test helper abstracts these details for you.

Stubbing out the Facebook serverOften in functional tests, you would want to stub out calls to Face-book so you will not have to make any calls over the network. Since the main entry point to Facebook is via the facebook_session, it is convenient to stub out calls to the facebook_session. Here’s an example:

stubbing_example.rb

def test_friends stubbed_user = stub(:friends => “1,2,3”) Facebooker::Session.any_instance.stubs(:user).returns(stubbed_user) facebook_post ‘friends’ assert assigns(:friends)end

Test accountsFacebook maintains a special network for developers only, where you can create fake users for testing. This is great for manual testing before launching an application. You can also automate this process by creating acceptance tests via a tool like Selenium. To create a test acccount, first create a normal Facebook account. Then go to the test account page (http://www.facebook.com/developers/become_test_account.php) to automatically join the developer test network. For more info, see the wiki (http://wiki.developers.facebook.com/index.php?title=Test_Accounts).

Page 54: Peepcode facebook-2-rails on facebook

54

Ruby Footprints Appchapter 8Ruby Footprints is a simple application based on the PHP example-application footprints. It is an extension of the integrated Facebook functionality called Poke and is intended to allow users to nudge their friends by stepping on them. A user will get a notification that some-one has stepped on them and be given the opportunity to retaliate by stepping back. Many of the code examples come directly from the running Ruby Footprints application.

RoutesFacebooker tries its best to keep routes looking just like Rails routes. The main difference is that if your route is for a canvas page, you should have :conditions => { :canvas => true } appended to your route. Rails resource routes will also work with Facebooker. Although all Facebook API calls are POST operations, one of the fb_params actually passes back the HTTP method used when ini-tially making the request. Facebooker uses this parameter to simulate resource routes.

Here is how to specify a route for the Facebook canvas:

map.root, :controller => ‘footprints’, :action => ‘index’, :conditions => { :canvas => true }

For non-canvas routes:

map.root, :controller => ‘footprints’, :action => ‘web’, :conditions => { :canvas => false }

A regular named route:

Page 55: Peepcode facebook-2-rails on facebook

55

map.remove_user(“remove_user”, :controller => “admin”, :action => “remove_user”)

Resource routes work too:

map.resources :footprints

ModelsAt the core of the Ruby Footprints application is the FacebookUser model. The FacebookUser model contains all that we need in order to be able to communicate with the Facebook API on behalf of a user. The three pieces of data required are the user’s Facebook ID, a session key, and the expiration time of the session key. Typically an application requires access to user data even when the user is not directly interacting with it, and this is obtained by requiring that the user add the application. Once this is done Facebook issues an infi-nite session key for the application to use. The FacebookUser model, when created, stores all this information in the database and then uses it to create a Facebooker::Session and to communicate with the Facebook API. Here is the application before filter used in Ruby Footprints to create the FacebookUser:

ruby_footprints/app/controllers/application.rb

def setup_db_facebook_user # Grab the facebook user if we have one # and store it in the session unless( @fb_user || facebook_params.empty? )

user_id = facebook_params[“user”] session_key = facebook_params[“session_key”] expires = facebook_params[“expires”]

fb_user = FacebookUser.ensure_create_user(user_id)

models

The creation and management of the Face-bookUser model has a couple of unique things that you don’t always see in a Rails applica-tion. The first is the call in the before_filter to the method FacebookUser.ensure_cre-ate_user. Typically what you would see here is a call to FacebookUser.find_or_create_by_uid(). This turns out to not be sufficient for any application that carries a significant load. The reason is that the create is not transaction-ally safe and the database will either end up will duplicate rows or if there is a unique key on uid then an database constraint error will be thrown. The second unique piece of code is the check for the session_key changing. This can happen if the user removes the application and then later adds it again. This check would not be necessary if the application deletes users from the database upon remove.

Another thing to note is that Facebook User id’s are 64 bit integers and the current Rails migra-tions do not support this as it is not database agnostic. MySQL and other databases can support 64 bit integers and there is a plugin for handling it (http://agilewebdevelopment.com/plugins/mysql_bigint).

Page 56: Peepcode facebook-2-rails on facebook

56

if( fb_user.session_key != session_key || fb_user.last_access.nil? || fb_user.last_access < (Date.today.to_time - 1 )) fb_user.session_key = session_key fb_user.session_expires = expires @previous_access = fb_user.last_access fb_user.last_access = Date.today fb_user.save! end @fb_user = fb_user end session[:current_user] = @fb_user @fb_user.facebooker_session = (@facebook_session || session[:facebook_session]) return @fb_userend

def publish_story_to_user if(@previous_access && (@previous_access < Time.parse(Date.today.to_s)) ) FacebookPublisher.deliver_nudge_story(current_user) endend

Now that the model contains a session key, uid, and expiration time, it has the ability to create a Facebooker::Session to communicate with the Facebook API. The API is typically called with either a ses-sion object or a Facebooker::User object. FacebookerUser provides access to both via these two methods:

ruby_footprints/app/models/facebook_user.rb

def facebooker_session unless(@facebooker_session) @facebooker_session = Facebooker::Session.create(ENV[‘FACEBOOK_API_KEY’], ENV[‘FACEBOOK_SECRET_KEY’]) @facebooker_session.secure_with!(self.session_key,self.uid,0) @facebooker_session.user.uid = self.uid end @facebooker_session

Page 57: Peepcode facebook-2-rails on facebook

57

end

def facebooker_user @facebooker_user ||= facebooker_session.userend

Now that the FacebookUser model can access an API session it can be set up to delegate method requests to the enclosed Facebooker::User, which is done via method_missing and allows an instance to quack like a Facebooker::User.

ruby_footprints/app/models/facebook_user.rb

def method_missing(symbol , *args) begin value = super rescue NoMethodError => err if(facebooker_user.respond_to?(symbol)) value = facebooker_user.send(symbol,*args) return value else throw err end endend

note There are instances inside the Facebooker code that

explicitly validate that it has been passed an instance of a

Facebooker::User, so even though the model delegates its

methods to an instance of Facebooker::User the method will still

throw an exception.

Page 58: Peepcode facebook-2-rails on facebook

58

Advanced Topicschapter 9

FBML and Image CachingReferences are a little hidden treasure in the Facebook platform. They allow you to cache a bunch of FBML on Facebook’s servers that can later be retrieved using a special FBML tag, <fb:ref>. The advantage of caching FBML is that your application doesn’t have to dynami-cally generate it each time for each user. But the real magic lies in the ability to fire off an API command that will instantly push out the cached FBML to wherever it is referenced. For example, if you have an application that applies the same FBML to each user’s profile, you can call just one API command that instantly updates it on each pro-file without you having to manually publish it for each user.

Refs are great for storing large chunks of FBML. You can even use them in creative ways, such as making your CSS a ref and updat-ing the ref anytime you change the CSS in order to update the view across all your users’ profiles. A common use is to have an admin section for your site that uses refs to update code that is common across all profiles, like CSS. It is also perfect if you have an app that publishes static content, like sports scores.

Handle RefsThere are two ways to cache FBML. The simplest way is to define a key value pair, where the key is a unique handle or identifier for the reference, and the value is the FBML content. You set the cache using:

facebook_session.server_cache.set_ref_handle(‘handle’, ‘FBML content’)

Page 59: Peepcode facebook-2-rails on facebook

59

Then “FBML content” will be replaced anytime you have:

<fb:ref handle=”handle”/>

URL RefsFacebook gives us another option for caching: you can store the FBML at a URL which will be cached by Facebook’s servers. Set and refresh the URL with:

facebook_session.sever_cache.refresh_ref_url(:url => “http://url_for_cached_fbml”)

This URL will be cached by Facebook until you call the method again. As with handle refs, you can access the content with:

<fb:ref url=”http://url_for_cached_fbml”/>

Handle refs are preferred over URL refs because they are set in real-time as opposed to being retrieved from your server when needed. If your server happens to be down and has a maintenance page up, then it is possible for your content to actually be the maintenance page when using URL refs. Furthermore, if you use URL refs exten-sively, and a torrent of requests come in, it is possible to overload your servers. Facebook also seems to have a high rate of failure with retrieving URL refs. Another reason to use handle refs is because they are left unchanged when a failure occurs. If a failure occurs while requesting a URL ref, the content will get filled up with garbage.

Image CachingImages that are shown on non-canvas pages, such as in profiles and

Page 60: Peepcode facebook-2-rails on facebook

60

Mini-Feeds, can also be explicitly cached by Facebook servers.

facebook_session.sever_cache.refresh_img_src(:url => “http://url_for_cached_image”)

They can be accessed with:

<fb:ref url=”http://url_for_cached_image”/>

Images that are served to Facebook canvas pages are automatically cached by Facebook on the first request. The above is mainly useful for the same reason that handle and URL refs are useful; an image that is identical across many profiles can be updated in one go using this method.

BeboRecently we added support into Facebooker that allows your Rails instance to simultaneously support both Facebook and Bebo (http://bebo.com). Bebo supports the current API provided by Facebook; how-ever, there is little indication that Bebo will be embracing the next version of the Profile and API. That being said, the differences are not big enough that they can’t be coded around. In order to support Bebo you will have to go through the same steps to create an appli-cation on Bebo that you did within the Facebook developer applica-tion. After doing this you will be given an api_key and a secret_key that can be put into your facebooker.yml. To setup your yaml file you will need to add 5 key value pairs as shown in the example below:

development: api_key: 921628d9d70f5e0b5887936802abe9e6 secret_key: 77sEcRETsecretSECRET3ce96c canvas_page_name: ruby_footprints callback_url: http://rfootprints.shacknet.nu

Page 61: Peepcode facebook-2-rails on facebook

61

bebo_api_key: abc45673de67f2234682919810adece7 bebo_secret_key: 77sEcRETsecretSECRET3ce96c bebo_canvas_page_name: bebo_ruby_foot bebo_callback_url: http://rfootprints.shacknet.nu bebo_adapter: BeboAdpater

The most important key is bebo_adapter. This key tells Face-boker to load the Facebooker::BeboAdpater class instead of the Facebook::FacebookAdapter class. This adapter contains all the information needed to talk to the Bebo API and to generate Bebo-specific canvas page URLS. They’re handled with a before filter on ApplicationController that will look at the incoming api_key, look up the correct configuration for that api_key, and load the appropri-ate Adapter. Occasionally it will be necessary to load a particular Adapter manually. This can be done by calling Facebooker.load_adapter(“bebo”) since the default adapter is always the Facebook-Adapter.

There are some differences in the Bebo container and most of these are discovered along the way. In order to support conditional rendering of Bebo-specific FBML, you can call Facebooker.is_for?(:bebo).

Page 62: Peepcode facebook-2-rails on facebook

62

Appendix: Terminologychapter 10

Term Description

Canvas page The page where your application runs inside Facebook.

Callback URL The URL where your appli-cation is hosted.

News feed Recent friend activity that shows up on the main Facebook page

Mini-Feed Recent friend activity that shows up on user profile pages

Notification Friend and app invites/communication, tab under Inbox

Wide profile Box on the right side of the profile page

Narrow profile Box on the left side of the profile page

Wall Comments section on the profile page, most recent post shown on top

Page 63: Peepcode facebook-2-rails on facebook

63

Appendix: Modelschapter 11Affiliation

name

nid

status

type

year

Album

aid

cover_pid

created

description

link

location

modified

name

owner

size

ApplicationProperties

about_url

application_name

callback_url

dashboard_url

default_column

default_fbml

description

desktop

dev_mode

edit_url

email

help_url

installable

ip_list

is_mobile

message_action

message_url

post_install_url

preload_fql

privacy_url

private_install

see_all_url

tos_url

uninstall_url

use_iframe

Cookie

expires

name

path

uid

value

EducationInfo

concentrations

degree

name

year

HighschoolInfo

grad_year

hs1_id

hs1_name

hs2_id

hs2_name

Event

creator

description

eid

end_time

event_subtype

event_type

host

location

name

nid

pic

pic_big

pic_small

start_time

tagline

update_time

venue

Attendance

eid

rsvp_status

uid

Page 64: Peepcode facebook-2-rails on facebook

64

FriendList

flid

name

Group

creator

description

gid

group_subtype

group_type

name

nid

office

pic

pic_big

pic_small

recent_news

update_time

venue

website

Membership

gid

position

uid

InfoItem

description

image

label

link

sublabel

InfoSection

field

items

Location

city

country

state

zip

Notifications

event_invites

friend_requests

group_invites

messages

pokes

shares

Photo

aid

caption

created

link

owner

pid

src

src_big

src_small

title

Tag

pid

subject

xcoord

ycoord

Page 65: Peepcode facebook-2-rails on facebook

65

User

about_me

activities

affiliations

birthday

books

current_location

education_history

first_name

has_added_app

hometown_location

hs_info

id

interests

is_app_user

last_name

meeting_for

meeting_sex

movies

music

name

notes_count

pic

pic_big

pic_small

pic_square

political

profile_update_time

User

quotes

relationship_status

religion

session

sex

significant_other_id

status

timezone

tv

uid

wall_count

work_history

Status

message

time

WorkInfo

company_name

description

end_date

location

position

start_date

Page 66: Peepcode facebook-2-rails on facebook

66

The Authors

David ClementsDavid has been doing Ruby On Rails consulting for over three years and is currently acting as Director of Web Development for Meeting-Wave (wwww.meetingwave.com). His current focus is on providing value through Social Networking integrations on the Facebook, Bebo and the iPhone Platforms. As regular contributor to the Facebooker library he also maintains the Facebooker Tutorial (http://apps.facebook.com/facebooker_tutorial) application on Facebook. He lives in sunny Boulder, Colorado and occasionally finds time for Mountain Biking and Snow-boarding. He can be reached at [email protected] or on Facebook (http://www.facebook.com/profile.php?id=830904376).

Shane VitaranaShane is a programmer living in Chicago, IL, and is one of the com-mitters on the Facebooker Rails plugin. He has built a handful of Facebook applications, with the most popular being Video Jukebox (http://apps.facebook.com/jookbox). He has worked for some big companies such as Motorola and Orbitz, but is now an independent developer. Shane started programming at the tender age of 12 and has worked on a broad range of platforms and languages. He is currently trying to apply his Facebook experience to the iPhone application market. He has a few applications in the iTunes App Store and working on more. When he is not on a coding binge, he likes to play the drums and ride a snowboard. His application, Drum Kit, is one of the Top 25 selling iPhone apps.

Check out Shane’s iPhone apps in the App Store (http://phobos.apple.com/WebObjects/MZStore.woa/wa/viewArtist?id=284976112).

Page 67: Peepcode facebook-2-rails on facebook

67

CreditsThanks to Michael Neissner for reviewing the book, and everyone at PeepCode for amazing layout, design, and editing.

RevisionsJanuary 7, 2009 – Corrected name of facebooker:setup rake task.•


Recommended