At the Forge - Integrating with Facebook Data
All of this seems pretty reasonable and straightforward, and it isn't hard to implement in any modern Web framework. But, if we want to implement the same functionality in a Facebook application, we have to consider that about half the database we just defined is going to be unnecessary. We don't need to worry about the Friends table, because that's something Facebook does quite well. And, we don't really need to worry about the People table either, as Facebook handles logins and authentication.
At the same time, we obviously can't use only the Friends table by itself. We need it to point to something, somewhere, so we can associate a visit with a user. How do we do that?
The answer is that instead of storing the users' information, we store their Facebook user IDs. Our People table, thus, will look like this:
CREATE TABLE People (
id SERIAL NOT NULL,
facebook_session_key TEXT NOT NULL,
facebook_uid TEXT NOT NULL,
PRIMARY KEY(id)
);
By storing the Facebook information in our database, we effectively hook our id column to what Facebook provides. But, how will we use this link?
The answer is that we don't really have to, if we use a plugin that handles the underlying details for us. I have used RFacebook for the past few months; this is a plugin for Ruby on Rails that makes it fairly easy to create a Facebook application using Rails. First, I create my models using the generate script that comes with Rails:
./script/generate model person facebook_session_key:string ↪facebook_uid:string
This creates a new model—that is, a Ruby class that represents a database table—called person.rb. Although this script doesn't create the model directly, it does create a migration file that defines our database table in Ruby:
class CreatePeople < ActiveRecord::Migration
def self.up
create_table :people do |t|
t.column :facebook_session_key, :string
t.column :facebook_uid, :string
end
end
def self.down
drop_table :people
end
end
Assuming that our database is all set up, we can run the migration using the built-in rake tool (think make, but in Ruby):
rake db:migrate
The output tells us a bit of what's going on:
== CreatePeople: migrating ======================
-- create_table(:people)
NOTICE: CREATE TABLE will create
implicit sequence "people_id_seq" for serial column "people.id"
NOTICE: CREATE TABLE / PRIMARY KEY will create
implicit index "people_pkey" for table "people"
-> 0.1939s
== CreatePeople: migrated (0.1944s) =============
The advantage of using rake and migrations is that we can modify our migrations file, change our database definitions, and move forward and backward in time through our database design. Migrations mean that you can keep track of your changes to the database and automatically upgrade (or downgrade) the database to the latest version without losing data. And, sure enough, if we look at our database, we see that it has three columns, just as we defined.
Next, we create another model for our visits table:
./script/generate model visit person_id:integer ↪visited_at:timestamp
We migrate the database to the latest version:
rake db:migrate
And, sure enough, we have a visits table with a person_id column. Unfortunately, because Rails migrations are written in a generic language, there isn't any built-in support for handling foreign keys and other referential integrity checks. So, the table, as we defined it above, would have indicated that person_id always must point to a value.
Also note that the default model-generation script allows null values. We could go into the migration file and change this, but we will ignore it for now.
Now that we have a place for the Facebook information in our People table, we need to tell Rails to put it there. We do this by adding the line:
acts_as_facebook_user
in the model file, person.rb. By default, it will be almost empty, indicating that we will use ActiveRecord's defaults to work with our database table via Ruby:
class Person < ActiveRecord::Base end
When we're done, our class will look like this:
class Person < ActiveRecord::Base
acts_as_facebook_user
end
In our controller file (which I'm sneakily reusing from what we did last month, modifying the facebook method in the hello controller), I've modified the method to read:
def facebook render :text => "hi" end
Because my application is called rmlljatf, I can go to the following URL: http://apps.facebook.com/rmlljatf/, and see my “hi” at the top of the page. After loading this page, I then look at my People table and find...that nothing has changed. After all, I told the system to create the table, but I didn't actually do anything with it! For that to happen, I need to use the built-in fbsession object, which gives me access to Facebook information. I then can say:
def facebook person = Person.find_or_create_by_facebook_session(fbsession) render :text => "hi" end
And, sure enough, reloading the page creates a row in our People table.
Next, I modify my method to add a row to our visits table. I can do that with the following:
def facebook
person = Person.find_or_create_by_facebook_session(fbsession)
Visit.create(:person_id => person.id,
:visited_at => Time.now()).save!
render :text => "hi"
end
Once I've modified the facebook method in this way, each visit to the site does indeed add another row to the visits table.
Now we should produce some output, indicating exactly how many visits the person has made to the site. For this, we create a view (facebook.rhtml), which can display things more easily:
<p>This is your <%= @number_of_visits.ordinalize %> visit.</p>
This short view displays the instance variable @number_of_visits and puts it into ordinal form, which is convenient. However, this means we need to set @number_of_visits in the facebook method. We do this by adding the following line:
@number_of_visits = ↪Visit.count(:conditions => ["person_id = ?", person.id])
In other words, we grab the current user's ID. We then use that ID, along with a built-in ActiveRecord value, to sum up the number of visits the user has made to the site.
Finally, it's time to introduce the Facebook magic. We know, from last month, that we can display the current user's Facebook friends without too much trouble; we use fbsession to grab a list of friends (and specific pieces of information about those friends), and then iterate over them, displaying them however we like.
Now, we do the same thing, but we also create a hash, @friends_visits, in which the key will be the Facebook user ID (uid), and the value will be the number of visits by that person. We give our hash a default value of 0, in case we try to retrieve a key that does not exist. We also use a bit of exception handling to ensure that we can handle null results. The final version of the facebook method looks like this:
def facebook
person = Person.find_or_create_by_facebook_session(fbsession)
Visit.create(:person_id => person.id,
:visited_at => Time.now())
# Count the number of visits
@number_of_visits =
Visit.count(:conditions => ["person_id = ?", person.id])
@friend_uids = fbsession.friends_get.uid_list
# Get info about friends from Facebook
@friends_info =
fbsession.users_getInfo(:uids => @friend_uids,
:fields => ["first_name", "last_name"])
# Keep track of friend visits to the site
@friends_visits = Hash.new(0)
@friends_info.user_list.each do |userInfo|
begin
friend = Person.find_by_facebook_uid(userInfo.uid)
@friends_visits[userInfo.uid] =
Visit.count(:conditions => ["person_id = ?", friend.id])
rescue
next
end
end
end
In other words, we grab friend information via fbsession. We then iterate over each friend, getting its Facebook uid. With that UID—which we have in our People table, in the facebook_uid column—we can get the person's database ID, and then use that to find the person's number of visits.
With this in place, we can rewrite the view as follows to include friend information:
<p>This is your <%= @number_of_visits.ordinalize %> visit.</p>
<% @friends_info.user_list.each do |userInfo| %>
<ul>
<li><fb:name uid="<%= userInfo.uid -%>" target="_blank" />
<fb:profile-pic uid="<%= userInfo.uid -%>" linked="true" />
<%= @friends_visits[userInfo.uid] %> visit(s)</li>
</ul>
<% end %>
Sure enough, when you visit the page, it tells you how many times you have visited, as well as how many times each friend has visited.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Designing Electronics with Linux | May 22, 2013 |
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
- Designing Electronics with Linux
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Dynamic DNS—an Object Lesson in Problem Solving
- Using Salt Stack and Vagrant for Drupal Development
- New Products
- A Topic for Discussion - Open Source Feature-Richness?
- Validate an E-Mail Address with PHP, the Right Way
- What's the tweeting protocol?
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Home, My Backup Data Center
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?




2 hours 3 min ago
12 hours 6 min ago
16 hours 33 min ago
20 hours 9 min ago
20 hours 41 min ago
23 hours 5 min ago
23 hours 8 min ago
23 hours 9 min ago
1 day 3 hours ago
1 day 5 hours ago