Facebook Application Development with Ruby on Rails

Exclusive offer: get 50% off this eBook here
Ruby on Rails Web Mashup Projects

Ruby on Rails Web Mashup Projects — Save 50%

A step-by-step tutorial to building web mashups

$26.99    $13.50
by Chang Sau Sheong | May 2008 | Open Source

In this article by Chang Sau Sheong, we learn how to create a Job Board Mashup Application using Ruby on Rails and Facebook. This application allows the user to perform some functions and features of a job board. Through this article, we will be able to acquire candidates through Facebook and search for jobs.

Technologies needed for this article

RFacebook

RFacebook (http://rfacebook.rubyforge.org/index.html) is a Ruby interface to the Facebook APIs. There are two parts to RFacebook—the gem and the plug-in. The plug-in is a stub that calls RFacebook on the Rails library packaged in the gem. RFacebook on Rails library extends the default Rails controller, model, and view. RFacebook also provides a simple interface through an RFacebook session to call any Facebook API. RFacebook uses some meta-programming idioms in Ruby to call Facebook APIs.

Indeed

Indeed is a job search engine that allows users to search for jobs based on keywords and location. It includes job listings from major job boards and newspapers and even company career pages.

Acquiring candidates through Facebook

We will be creating a Facebook application and displaying it through Facebook. This application, when added into the list of a user's applications, allows the user to search for jobs using information in his or her Facebook profile. Facebook applications, though displayed within the Facebook interface, are actually hosted and processed somewhere else. To display it within Facebook, you need to host the application in a publicly available website and then register the application. We will go through these steps in creating the Job Board Facebook application.

Creating a Rails application

Next, create a Facebook application. To do this, you will need to first add a special application in your Facebook account—the Developer application. Go to http://www.facebook.com/developers and you will be asked to allow Developer to be installed in your Facebook account.

Facebook Application Development with Ruby on Rails

Add the Developer application and agree to everything in the permissions list.

You will not have any applications yet, so click on the create one link to create a new application. Next you will be asked for the name of the application you want to create. Enter a suitable name; in our case, enter 'Job Board' and you will be redirected to the Developer application main page, where you are shown your newly created application with its API key and secret.

Facebook Application Development with Ruby on Rails

You will need the API key and secret in a while.

Installing and configuring RFacebook

RFacebook consists of two components—the gem and the plug-in. The gem contains the libraries needed to communicate with Facebook while the plug-in enables your Rails application to integrate with Facebook. As mentioned earlier, the plug-in is basically a stub to the gem. The gem is installed like any other gem in Ruby:

$gem install rfacebook

To install the plug-in go to your RAILS_ROOT folder and type in:

$./script/plugin install svn://rubyforge.org/var/svn/rfacebook/trunk/rfacebook/plugins/rfacebook

Next, after the gem and plug-in is installed, run a setup rake script to create the configuration file in the RAILS_ROOT folder:

$rake facebook:setup

This creates a facebook.yml configuration file in RAILS_ROOT/config folder. The facebook.yml file contains three environments that mirror the Rails startup environments. Open it up to configure the necessary environment with the API key and secret that you were given when you created the application in the section above.

development:
key: YOUR_API_KEY_HERE
secret: YOUR_API_SECRET_HERE
canvas_path: /yourAppName/
callback_path: /path/to/your/callback/
tunnel:
username: yourLoginName
host: www.yourexternaldomain.com
port: 1234
local_port: 5678

For now, just fill in the API key and secret. In a later section when we configure the rest of the Facebook application, we will need to revisit this configuration.

Extracting the Facebook user profile

Next we want to extract the user's Facebook user profile and display it on the Facebook application. We do this to let the user confirm that this is the information he or she wants to send as search parameters.

To do this, create a controller named search_controller.rb in the RAILS_ROOT/app/controllers folder.

class SearchController < ApplicationController
before_filter :require_facebook_install
layout 'main'
def index
view
render :action => :view
end
def view
if fbsession.is_valid?
response = fbsession.users_getInfo(:uids =>
[fbsession.session_user_id], :fields =>
["current_location", "education_history",
"work_history"])
@work_history = response.work_history
@education_history = response.education_history
@current_location = response.current_location
end
end
Ruby on Rails Web Mashup Projects A step-by-step tutorial to building web mashups
Published: April 2008
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

Note the before_filter we've added to this controller. This filter is a part of RFacebook and forces the user to install the application whenever this controller is used. Let's look at the view action.

fbsession is the current user's Facebook session represented in Rails. fbsession allows you to call any existing method in the set of Facebook REST APIs through a commonly used Ruby meta-programming idiom. There is a method in the Ruby Kernel module (which is included in the base Object class and therefore available to every Ruby object) called method_missing. This is called every time any Ruby object is sent a message it cannot handle (that is, it is called when there is a 'missing method'). Normally the Ruby interpreter will raise an error if this happens but this method (like any method in Ruby) can be overridden to process the message. This meta-programming idiom is widely used in many Ruby libraries and frameworks, including Ruby on Rails. For example, in ActiveRecord, the find_by_xxx methods are implemented in this way.

In the case of RFacebook, method_missing is overridden in fbsession (which is an instance of RFacebook::FacebookSession) to call the corresponding Facebook API. In the above code, the method users_getInfo is called in fbsession. This is translated into the Facebook API facebook.users.getInfo. The general rule is to drop 'facebook' and convert the dot (.) to an underscore (_). The parameters passed to the API are passed as a Hash. In this case, we are passing the current user's Facebook session UID and a list of fields we want from the API.

RFacebook stores the response in a format called Facepricot, which is an extended form of Hpricot, but with some specific and simplified methods for getting Facebook data from the returned response document. In our Job Board Facebook application, we're looking specifically for three pieces of information—the user's education history, work history, and current location.

Displaying the user profile and creating the search form

Now that we have the information let's go to the corresponding view and see how we can display it. Create a file called view.rhtml in the RAILS_ROOT/app/views/search folder:

<h1>
Job Board
</h1>
<p>Select to include your education and work history.</p>
<% form_tag :action => "search" do %>
<label>Education history &nbsp; <%= check_box_tag
'education'%></label>
<ul>
<%
education = []
@education_history.education_info_list.each { |education_info|
education << education_info.concentrations.concentration_list
%>
<li><%= h education_info.name %>(<%= h education_info.year%>) -
<%= h education_info.concentrations.concentration_list.join ", "%>
</li>
<% } %>
</ul>
<%= hidden_field_tag 'education_info', education.join(", ") %>
<label>Work history &nbsp; <%= check_box_tag 'work'%></label>
<ul>
<%
work = []
@work_history.work_info_list.each { |work_info|
work << work_info.position
%>
<li><%= h work_info.company_name %> (<%= h work_info.start_date%>
- <%= work_info.end_date%>) - <%= h work_info.position%>
</li>
<% } %>
</ul>
<%= hidden_field_tag 'work_info', work.join(", ") %>
<label>Other keywords</label>
<p>Enter additional keywords to search jobs on (separate keywords with commas) </p>
<%= text_field_tag 'keywords'%>
<br/>
<label>Job location</label>
<p>Where should the jobs be located?</p>
<% locations = []
locations << @current_location.city << @current_location.country %>
<%= text_field_tag 'location', locations.compact.delete_if {|item| item == '' }. join(', ')%>
<p/>
<%= submit_tag "search" %>
<% end %>

Let's take a look at how to display the education history. This is sample response format from the Facebook API documentation:

<education_history list="true">
<education_info>
<name>Harvard</name>
<year>2003</year>
<concentrations list="true">
<concentration>Applied Mathematics</concentration>
<concentration>Computer Science</concentration>
</concentrations>
</education_info>
</education_history>

Note that the education_history element has an attribute list=true, which indicates that there can be one or more education info elements. To get the listof education info elements, just attach _list to education_info to get education_info_list and use that as a method name. This will produce an Array of education_info elements, which we iterate to get and display the information.

<%
education = []
@education_history.education_info_list.each { |education_info|
education << education_info.concentrations.concentration_list
%>
<li><%= h education_info.name %>
(<%= h education_info.year%>) - <%= h education_info.concentrations.concentration_list.join ", " %>
</li>
<% } %>

Repeat this with work history.

<%
work = []
@work_history.work_info_list.each { |work_info|
work << work_info.position
%>
<li><%= h work_info.company_name %> (<%= h work_info.start_date%>
- <%= work_info.end_date%>) - <%= h work_info.position%>
</li>
<% } %>

We wrap a form around the displayed information and place check boxes next to education and work history to let the user choose if they want to use the data in their education history, work history, both, or none at all, in the job search. The fields we are using are the work positions and the education history concentrations. We also place the current location of the user as indicated in the user profile in a text field. This allows the user to search jobs in their current location by default or change it according to the location that they want.

The search form will call a search action in the search controller, which we will describe in the next part. For now we need to deploy the Facebook application and then configure it.

Deploying and configuring the Facebook application

Facebook applications, as mentioned earlier, are displayed through Facebook but hosted elsewhere. When a user accesses our application through Facebook, Facebook will call our application and return the results to the user.

Facebook Application Development with Ruby on Rails

As a result, the Facebook application needs to be accessible by Facebook and this means it needs to be hosted in a publicly available Internet site. This also means that our Facebook application cannot be deployed locally on your desktop PC even for testing purposes.

If you don't happen to have access to a server on the Internet or have access to a publicly available hosting account, there is another way of deploying your Facebook application. Assuming that you're running your application on your PC at home and accessing it through an ISP, you can use a dynamic DNS service to point an Internet domain name to the IP address that the ISP assigned to your PC. You will usually need to install a small application on your PC that will automatically update the DNS entry whenever your ISP-assigned IP address changes. There are many freely available dynamic DNS services on the Internet.

The caveat is that if you run your Facebook application on your home PC, you will need to keep it running all the time, unless it is acceptable that the application can be unavailable when it is not turned on. Also you should be aware that many ISPs do not allow their subscribers to run services on their home PCs and some even block off certain ports, in particular the HTTP port.

No matter where you plan to run the Facebook application, you can start the application as you start any Rails application.

$./script/server

After you have started up the Facebook application, you need to go back to the Facebook Developer application in your Facebook account to configure it. Click on the Edit Settings link for the application. There are a number of settings you need to configure here.

Facebook Application Development with Ruby on Rails

The callback URL is the public URL of your Facebook application. For our Job Board application, assuming that you are hosting on yourdomainname.com, you should use http://www.yourdomainname.com:3000/search.

Note that the default port number for the server script in the RAILS_ROOT/script folder is 3000. You can change it like this:

$./script/server –p 80

There are several ways of integrating your application in Facebook and the two most common ways are as a canvas page and as a profile box. The canvas page provides the most real estate for your application as it provides a boxed area that covers most of the page except for top and left navigation bars.

Facebook Application Development with Ruby on Rails

The canvas page URL is the URL you want to use to identify the canvas page of your application. In our application this is http://apps.facebook.com/job_board/.

Now let's go back quickly to the RFacebook configuration we left alone earlier on:

development:
key: YOUR_API_KEY_HERE
secret: YOUR_API_SECRET_HERE
canvas_path: /job_board/
callback_path: /search
tunnel:
username: yourLoginName
host: www.yourexternaldomain.com
port: 1234
local_port: 5678

Note the two highlighted settings. The canvas_path setting is the setting you have just placed in your Canvas Page URL while the callback_path is the relative path from your application. You can ignore the tunnel settings unless you wish to tunnel a remote domain name to your local machine.

Another configuration you need to set for the canvas page is whether to display the page as FBML (Facebook Markup Language) or to load your application as an iframe within Facebook. Generally speaking, using FBML will result in faster display and more consistent look-and-feel but the set of available markup is much more limited. In addition, there are many things not possible with FBML. In our application we will be using an iframe.

We also want our application to be added to a user's Facebook account, so select Yes for that setting. You will notice that once we click on the Yes radio button, a whole new set of configuration settings appears.

We want to re-direct the user to our canvas page once he or she has agreed to install the application, so enter http://apps.facebook.com/job_board/ in the Post-Add URL setting. As we are not integrating with the user's profile in this application, the other profile-related settings can be ignored.

The next setting to configure is the left-side navigation link. We want to display a nice little logo and enable our user to access our application by clicking on a link in the left navigation bar, so enter http://apps.facebook.com/job_board/ in the Side Nav URL setting.

Finally, before we complete the configuration, create a small (16 pixel by 16 pixel) image in JPG, GIF, or PNG format for your application and upload it in the icon setting.

To add the finishing touches, we want to make our Job Board application's look and feel very similar to the rest of Facebook even though it's using an iframe. The trick is to use Facebook's static stylesheets to style your views. Remember in the search controller we had this line:

class SearchController < ApplicationController
before_filter :require_facebook_install
layout 'main'

This changes the layout of views from this controller to using a layout file called main.rhtml. So to make the look and feel uniform, let's create this main.rhtml under RAILS_ROOT/app/views/layouts:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
<title>Job Board</title>
<%= javascript_include_tag :defaults %>
<%= stylesheet_link_tag
'http://static.ak.facebook.com/css/base.css' %>
</head>
<body>
<%= yield %>
</body>
</html>

The highlighted code shows that our main layout uses the base stylesheet used by Facebook as well.

Ruby on Rails Web Mashup Projects A step-by-step tutorial to building web mashups
Published: April 2008
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

Now let's take a quick view of your new application!

Facebook Application Development with Ruby on Rails

Searching for jobs through Indeed

Now that we have the Facebook application providing search parameters, we will use these parameters and send a search query to Indeed, then parse the results and display them in the Job Board canvas page.

Creating the search action

First, let's create the search action in the search controller. Modify search_controller.rb to add in a search method:

class SearchController < ApplicationController
before_filter :require_facebook_install
layout 'main'
def index
view
render :action => :view
end
def view
if fbsession.is_valid?
response = fbsession.users_getInfo(:uids => [fbsession.
session_user_id], :fields => ["current_location",
"education_history", "work_history"])
@work_history = response.work_history
@education_history = response.education_history
@current_location = response.current_location
end
end
def search
query = []
query << params[:work_info] if params[:work]
query << params[:education_info] if params[:education]
query << params[:keywords]
url = 'http://api.indeed.com/search'
hash = {'key' => 'YOUR_INDEED_API_KEY',
'q'=> query.join(", "),
'limit' => 20,
'latlong' => 1}
hash['l'] = params[:location]
parameters = URI.escape(hash.to_a.collect {|pair| pair.join('=')}.
join('&'))
res = Net::HTTP.get_response(URI.parse(url + '?' + parameters))
case res
when Net::HTTPSuccess, Net::HTTPRedirection
results = XmlSimple::xml_in(res.body, 'force_array' => false)
@jobs_found = results['results']['result']
else
puts res.error!
end
if @jobs_found.nil?
@jobs_found = []
flash[:notice] = "No jobs found"
else
session[:jobs] = @jobs_found
end
end
end

First, we get the parameters from the search form and push them into an array, query, if the user wanted to use that parameter. Next, we fashion the URL parameters out of various parameters required by Indeed, including the API key, the search parameters, the maximum number of jobs to retrieve, and whether to include the latitude and longitude information of the job. We also attach the location to search for the job, if the user has entered any location information.

After formatting the URL parameters properly, we attach them to Indeed's API search REST URL and use Net::HTTP to send a GET request to the URL. Note that we are using GET because Indeed does not support POST requests for the search.

As before we get a returned response object that has data embedded in its body. In this section we use XmlSimple to extract the XML in the response body and format it into a nested hash. Please refer to the earlier section on XmlSimple if you have not installed it. Note that we set the force_array configuration to false in order not to produce arrays to for single values.

Finally, we get the hash that we're interested in (that is, the jobs that are returned) and pass it on to the view. We also store the hash of jobs into a session to be processed later by the map and others.

Parsing and displaying the search results

Let's move on to the view. Create a file called search.rhtml in the RAILS_ROOT/app/views/search/ folder:

<h1>Job Board search results</h1>
<span class="message"><%= h flash[:notice] %></span>
<br/><%= link_to "Back to search", "http://apps.facebook.com/job_board", :target => '_top'%> &nbsp;
<%= link_to "Display jobs on a map", "http://www.yourdomainname.com/map", :target => '_top'%><br/>
<ol>
<%@jobs_found.each { |job| %>
<li class="title">
<a href="<%= job['url']%>" target="_new"><%= h job['jobtitle']%>, <%= h job['company']%>
</a>
</li>
<div class="description">
<div class="underline">Location</div>
<%= h job['city'] %>, <%= h job['country'] %>
<div class="underline">Description</div>
<%= job['snippet'] %>
</div>
<% } %>
</ol>
<br/>
<span id=indeed_at><a href="http://www.indeed.com/">jobs</a>
by <a href="http://www.indeed.com/" title="Job Search">
<img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="http://www.indeed.com/p/jobsearch.gif" style="border: 0; vertical-align: middle;" alt="job search">
</a></span>

The code here is quite self-explanatory. We take the jobs found from calling Indeed's API search and display them accordingly on the page. Remember that this is still a page in the Facebook application. We will wrap up the page by including the obligatory link back to Indeed.

About the Author :


Chang Sau Sheong

Chang Sau Sheong has more than 12 years experience in software application development and has spent much of his career in web and Internet-based applications. He has a wide range of experience in banking payment-related as well as Internet-based e-commerce software. Currently he is the Director of Software Development of a 50+ strong software development team in Welcome Real-time, a multi-national payment/loyalty software company based in France and Singapore.

Sau Sheong hails from tropical Malaysia but has spent most of his adult and working life in sunny Singapore, where he shares his spare time between enthusiastically writing software and equally enthusiastically playing Nintendo Wii with his wife and son.

Books From Packt

Ruby on Rails Web Mashup Projects
Ruby on Rails Web Mashup Projects

    Building Dynamic Web 2.0 Websites with Ruby on Rails
    Building Dynamic Web 2.0 Websites with Ruby on Rails

Ruby on Rails Enterprise Application Development: Plan, Program, Extend
Ruby on Rails Enterprise Application Development: Plan, Program, Extend

Aptana RadRails: An IDE for Rails Development
Aptana RadRails: An IDE for Rails Development

OpenCms 7 Development
OpenCms 7 Development

Building Powerful and Robust Websites with Drupal 6
Building Powerful and Robust Websites with Drupal 6

Learning Drupal 6 Module Development
Learning Drupal 6 Module Development

Moodle E-Learning Course Development
Moodle E-Learning Course Development


 

 

Your rating: None Average: 4.7 (3 votes)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
P
b
1
K
e
X
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software