Академический Документы
Профессиональный Документы
Культура Документы
Rails on
Facebook
Hooking into the network
by Shane Vitarana and David Clements
Rails on Facebook
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.
"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.
2
contents Getting Social Data 26 Ruby Footprints App 54
Introduction 5 User and Friend Data Routes
Why Facebook? Photos Models
Social Data Events Advanced Topics 58
The Facebook Platform 7 Anatomy of an App 28 FBML and Image Caching
The Facebook API Integration Points Bebo
FBML The Profile Page Appendix: Terminology 62
FBJS New Profile Application Tab
Appendix: Models 63
Getting Started 15 News Feed and Wall
The Authors
Facebook Signup Notifications
Credits
Facebooker Emails
Revisions
Configuration Invitations
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
5
with your friends, comparing scores on quizzes, and throwing sheep
at old high school buddies, are replacing traditional avenues of com-
munication, like email.
Social Data
Many 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.
6
The Facebook Platform
chapter 2
A 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:
f
!++(((* ''#'%%.((,*) !++(%.(('%,*)
&-*
$$#
User FaceBooK raILs app
f
!++((" ''#'%
FaceBooK
'"$+
raILs app
User
'&+&+&)
f
FaceBooK
'&+&+
•
7
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.
FBML
FBML 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.
8
Tags
The 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
Forms
Standard 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
9
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
Overview
FBJS 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) {
10
var a12345_obj = {property: a12345_bar};
return a12345_obj.property;
}
Third-Party Libraries
Because 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.
AJAX
Facebook 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
11
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.
Animation Library
Facebook 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-
13
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).
14
Getting Started
chapter 3
Facebook Signup
The 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.
15
Application Name: It seems
that this does not need to be
unique across all Facebook
apps.
17
Side Nav URL: This is a very
important integration point as
Privacy URL: This will
it will result in an icon and link
create a link that the
on the left of the user's profile.
user will see when
It must go to a Canvas Page.
editing the application
privacy settings. It
should be a Facebook Help URL: This can be an
Canvas page. external URL, a Facebook
Canvas page that shows help,
or a FAQ for your application.
Private Installation:
This will prevent news
stories from being
published by your
application, and is a
good idea during
development.
•
18
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
Facebooker
Facebooker 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.
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/plugins
git clone git://github.com/mmangino/facebooker.git
19
Or, download the source directly from GitHub (http://github.com/mmang-
ino/facebooker) and unpack it into your vendor/plugins directory.
Configuration
First, 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
20
Hello World
This 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
end
end
Development Environment
Ideally, 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.
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).
22
Facebook Authentication
chapter 4
The 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 Request
Even 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.
Authenticating a User
In 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
23
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_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.
24
private
def next_path
end
note There is surely a cleaner way to create a URL from the request
parameters. Please do investigate.
25
Getting Social Data
chapter 5
complete list of user data
The facebook_session.user.populate()
User and Friend Data method accepts one or more of the following
You can access nearly all the data that is public on a user’s profile symbols. Leave blank to get them all.
once he or she installs your application. You may use this data in
your application, but Facebook has limits on what you may store :status, :political, :pic_small,
:name, :quotes, :is_app_user, :tv,
persistently. You can generally store numerical identifiers such as user
:profile_update_time, :meeting_sex,
IDs (UIDs) and photo IDs (PIDs), but nothing more. It is also illegal to
:hs_info, :timezone, :relationship_
store friend relationship id pairs, such as <uid, uid>. Refer to the status, :hometown_location, :about_
developer documentation (http://wiki.developers.facebook.com/index.php/Main_ me, :wall_count, :significant_
Page) for storage guidelines. other_id, :pic_big, :music, :uid,
:work_history, :sex, :religion,
For example, here is how to fetch a user’s favorite music: :notes_count, :activities, :pic_
square, :movies, :has_added_app,
facebook_session.user.populate(:music) :education_history, :birthday,
music = facebook_session.user.music :first_name, :meeting_for, :last_
name, :interests, :current_location,
:pic, :books, :affiliations
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 All of the above return String values, except
movies, etc. See the sidebar for a complete list. 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:
Photos location_example.rb
Facebook organizes photos into albums, and each photo has its own facebook_session.user(:location)
photo id (pid). You can specify either a list of photo IDs, an album id, # => Location object
or a subject id. The subject id is the UID of a specific user. Specifying location.city
the subject id will retrieve all the photos in which the particular user # => Chicago
was tagged.
26
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).
Events
Facebooker 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:
Once you have a list of Event objects, you can access the attributes
of each Event, such as description, venue, host, location, etc.
27
Anatomy of an App
chapter 6
Integration Points
Your 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).
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
29
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 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
30
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>
31
@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.
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.
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.
33
New Profile Application Tab
The 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:
Facebooker exposes each of these API calls via the Publisher API, as
introduced above.
Publishing Stories
In 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.”
35
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.
36
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:
Publishing Actions
Publishing 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
37
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’.
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) ,
39
: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.
Notifications
Notifications 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
40
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.
Emails
Your 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)
41
end
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.
Invitations
Invitations 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:
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.
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.
44
Info Section
In 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
45
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
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:
47
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.
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
49
Feed stories.
50
programmatic configuration @facebook_session.admin.get_allocation(:email_
disable_message_location)
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- A certain number of invitations can be sent from your
tion limits put upon your application. There are allocation application per user per day. You will see this as a limit on
limits for how many emails, notifications and requests are the friend selector widgets provided by Facebook. Check it
sent. programmatically:
@facebook_session.admin.get_
allocation(:notifications_per_day)
@facebook_session.admin.get_
allocation(:emails_per_day)
51
Testing
chapter 7
Functional tests
Testing 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
Test accounts
Facebook 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).
53
Ruby Footprints App
chapter 8
Ruby 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.
Routes
Facebooker 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.
map.root, :controller => ‘footprints’, :action => ‘index’, :conditions => { :canvas => true }
map.root, :controller => ‘footprints’, :action => ‘web’, :conditions => { :canvas => false }
54
map.remove_user(“remove_user”, :controller => “admin”, :action => “remove_user”)
map.resources :footprints
models
The creation and management of the Face-
bookUser model has a couple of unique things
Models that you don’t always see in a Rails applica-
At the core of the Ruby Footprints application is the FacebookUser tion. The first is the call in the before_filter
model. The FacebookUser model contains all that we need in order to the method FacebookUser.ensure_cre-
to be able to communicate with the Facebook API on behalf of a ate_user. Typically what you would see here
user. The three pieces of data required are the user’s Facebook ID, a is a call to FacebookUser.find_or_create_
session key, and the expiration time of the session key. Typically an by_uid(). This turns out to not be sufficient for
application requires access to user data even when the user is not any application that carries a significant load.
directly interacting with it, and this is obtained by requiring that the The reason is that the create is not transaction-
user add the application. Once this is done Facebook issues an infi- ally safe and the database will either end up
nite session key for the application to use. The FacebookUser model, will duplicate rows or if there is a unique key on
when created, stores all this information in the database and then uid then an database constraint error will be
uses it to create a Facebooker::Session and to communicate with thrown. The second unique piece of code is the
the Facebook API. Here is the application before filter used in Ruby check for the session_key changing. This can
Footprints to create the FacebookUser: happen if the user removes the application and
then later adds it again. This check would not
ruby_footprints/app/controllers/application.rb be necessary if the application deletes users
def setup_db_facebook_user from the database upon remove.
# Grab the facebook user if we have one
# and store it in the session Another thing to note is that Facebook User id’s
unless( @fb_user || facebook_params.empty? )
are 64 bit integers and the current Rails migra-
user_id = facebook_params[“user”] tions do not support this as it is not database
session_key = facebook_params[“session_key”] agnostic. MySQL and other databases can
expires = facebook_params[“expires”] support 64 bit integers and there is a plugin for
handling it (http://agilewebdevelopment.com/plugins/
fb_user = FacebookUser.ensure_create_user(user_id) mysql_bigint).
55
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_user
end
def publish_story_to_user
if(@previous_access && (@previous_access < Time.parse(Date.today.to_s)) )
FacebookPublisher.deliver_nudge_story(current_user)
end
end
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
56
end
def facebooker_user
@facebooker_user ||= facebooker_session.user
end
57
Advanced Topics
chapter 9
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 Refs
There 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:
58
Then “FBML content” will be replaced anytime you have:
<fb:ref handle=”handle”/>
URL Refs
Facebook 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:
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 Caching
Images that are shown on non-canvas pages, such as in profiles and
59
Mini-Feeds, can also be explicitly cached by Facebook servers.
<fb:ref url=”http://url_for_cached_image”/>
Bebo
Recently 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
60
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
61
Appendix: Terminology
chapter 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
62
Appendix: Models
chapter 11
Affiliation ApplicationProperties Cookie Event
name about_url expires creator
nid application_name name description
status callback_url path eid
type dashboard_url uid end_time
year default_column value event_subtype
default_fbml event_type
Album description EducationInfo host
aid desktop concentrations location
cover_pid dev_mode degree name
created edit_url name nid
description email year pic
link help_url pic_big
location installable pic_small
HighschoolInfo
modified ip_list start_time
grad_year
name is_mobile tagline
hs1_id
owner message_action update_time
hs1_name
size message_url venue
hs2_id
post_install_url
hs2_name
preload_fql
Attendance
privacy_url
eid
private_install
rsvp_status
see_all_url
uid
tos_url
uninstall_url
use_iframe
63
FriendList InfoItem Photo
flid description aid
name image caption
label created
link link
Group
sublabel owner
creator
description pid
gid InfoSection src
group_subtype field src_big
group_type items src_small
name title
nid
Location
office Tag
city
pic pid
country
pic_big subject
state
pic_small xcoord
zip
recent_news ycoord
update_time
venue Notifications
website event_invites
friend_requests
group_invites
Membership
messages
gid
pokes
position
shares
uid
64
User User Status
about_me quotes message
activities relationship_status time
affiliations religion
birthday session
WorkInfo
books sex
company_name
current_location significant_other_id
description
education_history status
end_date
first_name timezone
location
has_added_app tv
position
hometown_location uid
start_date
hs_info wall_count
id work_history
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
65
The Authors
David Clements
David 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 david.g.clements@gmail.com or on
Facebook (http://www.facebook.com/profile.php?id=830904376).
Shane Vitarana
Shane 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.
66
Credits
Thanks to Michael Neissner for reviewing the book, and everyone at
PeepCode for amazing layout, design, and editing.
Revisions
• January 7, 2009 – Corrected name of facebooker:setup rake task.
67