Chapter 5. Job board mashup application

What does it do?

The job board mashup application is a fully fledged web application (running in the Facebook interface) that allows a user to perform some functions and features of a job board. This chapter describes how some functions of a job board can be performed and enhanced by mashing up APIs from various sources.

Job board

A job board is a web application that 'converts' visits to a web site into actual candidates registering for consideration for a job. It can belong to a company that is recruiting its own employees or a third-party provider that helps companies to recruit.

The basic functions of job board are:

  • Acquiring candidates

  • Storing and providing access to candidate profiles

  • Processing candidate profiles

  • Acquiring job postings

  • Storing and providing access to job postings

  • Processing job postings

  • Providing value-added functions to candidates and employers

In this mashup we will show how some of a job board's functions can be duplicated using various existing APIs on the Internet.

Requirements overview

We will define and map some of the functions of a job board and create mashups to support these functions. We're not going to cover all the functions of a job board (there are too many to cover) and we will be using numerous APIs from different providers to provide these functions.

The functions are:

  • Create a Facebook application that extracts and displays the user's profile. Facebook is used here as a marketing tool to acquire candidates.

  • Search for jobs through Indeed according to the user's Facebook profile. Most job boards will have their own database of job postings but here we emulate acquiring job postings by using a job search engine.

  • Display the location of the jobs found using the user's profile in Google Maps. Displaying job locations in a map provides more visual impact to candidates.

  • Search and display news on the recruiting companies through the Daylife platform. News on the recruiting company provides more information on the company and its products and services.

  • Search and display blog postings in the blogosphere on the recruiting companies through Technorati. Blog postings on the recruiting company provide a different view of the company through the eyes of employees, ex-employees, customers, or partners.

Design

This mashup application takes input and provides output to many different mashup APIs. We will break down the process of creating this mashup into different steps. The final application is not a single piece of software but a mashup in every sense.

We are going to use Facebook as a marketing tool, tapping its social network for candidates. To do this we will create a Facebook application. This means while the software resides within our server, the interface is through Facebook.

Searching for jobs through Indeed is displayed through the Facebook application but displaying them on Google Maps is more conventionally displayed in our own interface. News and blog entries on the company offering the job are displayed when clicked on within Google Maps, in a separate panel to the side of the map.

Mashup APIs on the menu

We are going to use five different mashup APIs in our mashup application:

  • Facebook

  • Google Maps

  • Indeed

  • Daylife

  • Technorati

We're also going to use a few technologies to access these APIs:

  • RFacebook for accessing Facebook

  • YM4R/GM for accessing Google Maps

  • Net::HTTP for accessing Indeed, Daylife and Technorati

  • XmlSimple for parsing input form Indeed, Daylife, and Technorati

Here's a quick rundown and introduction to the APIs and tools.

Facebook

Facebook (http://www.facebook.com) is a social networking tool that connects you to other people that you know on Facebook. It has the largest registered user base among college-focused sites in the United States with an estimated 34 million users as of September 2007.

In May 2007, Facebook opened up its network to application developers by launching a set of APIs for third-party developers, called the Facebook Platform. Since then there has been a flurry of third-party applications developed on Facebook and as of September 2007 there are more than 4,500 such applications deployed on its platform.

Facebook Platform

The Facebook Platform consists of several parts:

  • Facebook REST APIs

  • Facebook Query Language (FQL)

  • Facebook Markup (FBML)

Facebook REST APIs allow you to write applications (either completely external or attached to the Facebook interface) that use Facebook data or write data to user's Facebook accounts.

FQL allows you to write SQL-like queries in your applications to request Facebook data. FQL is used instead of the REST APIs if you want to have finer control on what data you want from Facebook. For example if you only want a small subset of data from Facebook, or if you want to concatenate data from a few sources in Facebook, then you might not want to call two or more APIs to do the work where a single FQL query will do the job. However, FQL as its name implies is only available for reading data off Facebook.

FBML is a markup language that allows your application to integrate well into the Facebook application. It is set of HTML-like tags that can be used at various places in the Facebook application. FBML can be integrated in many different parts of a Facebook application, though the two main parts are the profile page and the canvas page.

We will be accessing Facebook through the RFacebook Ruby library and Rails plugin.

RFacebook

RFacebook (http://rfacebook.rubyforge.org/index.html) is a Ruby interface to the Facebook APIs. You can use the REST APIs, FQL, and FBML through RFacebook. There are two parts to RFacebook—the gem and the plugin. The plugin 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.

Google Maps

Google Maps is a free web-based mapping service provided by Google. Google provides a free JavasSript API library that allows developers to integrate Google Maps into their own applications. In this mashup we will be using only the online mapping function and not the geocoding capabilities of Google Maps. We will be using the YM4R/GM plugin to access the online map. For more information on getting a Google Maps API key please refer to Chapter 2.

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.

Indeed provides a search API through its web services offering. To register for the web service API go to http://www.indeed.com/jsp/createaccount.jsp and register for an account. Once you have signed up you will receive a search API key. You are allowed to make up to 99,999 queries in a single day. The web service API provided by Indeed is only available through a GET request from a REST URL.

Technorati

Technorati (http://www.technorati.com) is an Internet search engine that specializes in searching blogs. As of August 2007, Technorati had indexed 94 million blogs on the Internet. Technorati provides a set of APIs for developers to help them integrate Technorati data into their applications and to create mashups.

To create a developer account in Technorati, go to http://technorati.com/signup/ and sign up for an account. Once you have signed up you will be provided with an API key. The number of queries you make on Technorati each day is tracked and you are not allowed to make more than 500 queries in a single day, unless you have a commercial agreement with Technorati.

Technorati provides a number of APIs of which search is only one. You are also able to get information on its members and the blogs its members own as well as various blog statistics.

Daylife

Daylife (http://www.daylife.com) is a news aggregation site that gathers news items from sources around the world and presents them in a meaningful and connected way. One of the key features in Daylife is its ability to connect different stories and topics together. Daylife also offers a platform for third-party applications to use its data. Daylife provides a set of APIs around their News objects through a REST interface. APIs provided include those used for Articles, Images, Quotes, Topics, and Searches.

To use the Daylife platform you need to register at http://developer.daylife.com/member/register. After registering you will be given an API access key and a shared secret.

To send a request to the Daylife APIs, we need to compose a URL that specifies an API endpoint and a list of input parameters. Input parameters are provided as URL-encoded name value pairs.

This what an endpoint looks like:

http://freeapi.daylife.com/<resultformat>/publicapi/<version>/<NewsObject name>_<method name>

Note that the current version is 4.2 and therefore the version number above is 42. Daylife provides resultsets in three formats; that is in XML (xmlrest), in serializable PHP objects (phprest) and in JSON (jsonrest). In this mashup we will use the XML format only.

Input parameters are appended to the API endpoint as an '&'-separated list of name-value pairs of arguments as a query string using the standard HTTP GET formatting.

For example, a call to search for news containing the text "ruby on rails" and a request for the resultset in an XML format looks like this:

http://freeapi.daylife.com/xmlrest/publicapi/4.2/search_getRelatedArticles?accesskey=<accesskey>&signature=<signature>&query=ruby+on+rails

where <accesskey> is your assigned API access key, and <signature> is replaced by an MD5 hashed signature. To create the signature, we concatenate the API access key, the shared secret, and a core input that varies according to the API called. We will be describing how to create the signature in detail when we do it later.

Net::HTTP

For a more detailed discussion on Net::HTTP please refer to Chapter 3. We will only be sending GET requests to the REST APIs in this mashup, so our code will be in this form:

response = Net::HTTP.get_response(URI.parse('http://www.packtpub.com'))

XmlSimple

XmlSimple (http://xml-simple.rubyforge.org/) is a Ruby API that allows XML formatted data to be easily read and written to. It is a Ruby translation of the Perl module XML::Simple and is written on top of REXML, an XML parser that is included in the Ruby distribution.

To install XmlSimple, run this at the command line:

$gem install xml-simple

The main use for XmlSimple in our mashup is to read the XML response that is sent by the API and convert the XML into a nested hash. For example, when we send a request query to Technorati using its search query, this is the returned XML response:

<?xml version="1.0" encoding="utf-8"?>
<!-- generator="Technorati API version 1.0 /search" -->
<!DOCTYPE tapi PUBLIC "-//Technorati, Inc.//DTD TAPI 0.02//EN" "http://api.technorati.com/dtd/tapi-002.xml">
<tapi version="1.0">
<document>
<result>
<query>Ruby on Rails</query>
<querycount>42254</querycount>
<rankingstart></rankingstart>
</result>
<item>
<weblog>
<name>Urubatan's Weblog</name>
<url>http://www.urubatan.info</url>
<rssurl>http://www.urubatan.info/feed/</rssurl>
<atomurl></atomurl>
<inboundblogs>8</inboundblogs>
<inboundlinks>11</inboundlinks>
<lastupdate>2007-10-16 14:17:27 GMT</lastupdate>
</weblog>
<title>Ruby out of the Rails - Nitro and Og</title>
<excerpt> The Ruby language, started to grow inside the enterprises and to be the topic in many blogs after the Rail framework showed up, but Rails is not the only option for developing web applications with Ruby, there are other frameworks, and one of the others is called Nitro Framework, this one has almost the same age as Rails</excerpt>
<created>2007-10-16 14:17:27 GMT</created>
<permalink>http://www.urubatan.info/2007/10/ruby-out-of-the-rails- nitro-and-og/</permalink>
</item>
</document>
</tapi>

Running this through XmlSimple with this code:

XmlSimple::xml_in(xml_input, 'force_array' => false)

results in a nested hash like this:

{"document"=>
{"result"=>
{"querycount"=>"42255", "rankingstart"=>{}, "query"=>"Ruby on Rails"},
"item"=>
{"permalink"=>
"http://www.urubatan.info/2007/10/ruby-out-of-the-rails-nitro- and-og/",
"title"=>"Ruby out of the Rails - Nitro and Og",
"excerpt"=>
" The Ruby language, started to grow inside the enterprises and to be the topic in many blogs after the Rails framework showed up, but Rails is not the only option for developing web applications with Ruby, there are other frameworks, and one of the others is called Nitro Framework, this one has almost the same age as Rails",
"weblog"=>
{"inboundlinks"=>"11",
"rssurl"=>"http://www.urubatan.info/feed/",
"name"=>"Urubatan's Weblog",
"atomurl"=>{},
"url"=>"http://www.urubatan.info",
"inboundblogs"=>"8",
"lastupdate"=>"2007-10-16 14:17:27 GMT"},
"created"=>"2007-10-16 14:17:27 GMT"}},
"version"=>"1.0"}

By setting the attribute force_array to false in this example, we allow elements in the nested hash not to be an array if there is only 1 element. From the nested hash we can extract the data that we need.

We will be using XmlSimple pretty extensively in many of the subsequent sections.

What we will be doing

The following section describes the steps we will be taking to create the mashup.

Acquire candidates through Facebook

The first and longest step is to acquire candidates through a Facebook application. These are the steps we will take to create this Facebook application:

  1. Create a Rails application

  2. Create a Facebook application

  3. Install and configure the RFacebook plugin and gem

  4. Extract the user's Facebook user profile using RFacebook

  5. Display the user profile information we need and create a form to send the user profile to the search page

  6. Deploy and configure the Facebook application

Search for jobs through Indeed

Next, we will enable searching for jobs through Indeed while displaying the jobs in the Facebook application. The Facebook application also allows you to display the location of the jobs found in a map and this redirects to our mashup. The steps to achieve this are:

  1. Create the action and use Net::HTTP to send the search parameters to Indeed

  2. Parse the results with XML:Simple and display the search results in the Facebook application

Display jobs in Google Maps

Displaying Google Maps is done in our own mashup interface:

  1. Use the search results to display the location of the jobs in the map

  2. Create a link on each job to show the news and stories on the company

Search and display job news from Daylife

Searching and displaying job news extracted from Daylife:

  1. Use Net::HTTP to accesss Daylife APIs to extract Daylife news data and display it on the web page

Search and display job stories from Technorati

Searching and displaying job news extracted from Technorati:

  1. Use Net::HTTP to access Technorati's REST APIs and extract blog information

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, then register the application. We will go through these steps in creating the Job Board Facebook application.

Creating a Rails application

We begin this mashup by creating a Rails application.

$rails Chapter5

This will create a new Ruby on Rails application. Note that in this application we will not be using ActiveRecord or accessing any databases to retrieve or store data. All data will be retrieved and parsed from the various APIs we will be using.

Creating a Facebook application

Next, create a Facebook application. To do this, you will need to first add a special application in your Facebook account&mdash;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.

Creating a Facebook application

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

You will not have any applications yest, 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.

Creating a Facebook application

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

Installing and configuring RFacebook

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

$gem install rfacebook

To install the plugin 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 plugin 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

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&mdash;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 list of 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.

Deploying and configuring the Facebook application

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. This is the same technique we used in Chapter 2 to enable us to geolocate the user of the application through Hostip.info. We will use this technique again in Chapter 6.

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.

Deploying and configuring the Facebook application

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.

Deploying and configuring the Facebook application

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:
job board, mashup applicationFacebook application, configuringkey: 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.

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

Deploying and configuring the Facebook application

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="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. For the next API we are mashing up with, we will be using our own site, so place a link on the page back to our mashup application. We also wrap up the page by including the obligatory link back to Indeed.

Display jobs in Google Maps

Displaying locations on Google Maps has been covered extensively in Chapter 2, so I will not go into the details of setting up the various plugins to do this but go straight into the code. However note that this mashup does not do any geocoding so we don't need to install GeoKit. We only need YM4R/GM.

Displaying the location of the jobs on the map

To display the location of the jobs, we need a new controller. Create a file called map_controller.rb in RAILS_ROOT/app/controllers:

require 'net/http'
require 'cgi'
class MapController < ApplicationController
layout 'main'
def index
@jobs = session[:jobs]
@map = GMap.new("map_div")
@map.control_init(:large_map => true, :map_type => true)
markers = {}
count = 1
@jobs.each { |job|
info = <<END
<div style='width: 350px'>
<label>#{job['jobtitle']} (#{job['company']})</label> <br/>
#{job['snippet'].capitalize}</div>
END
markers[count] =
GMarker.new([job['latitude'],
job['longitude']],
:title => job['jobtitle'], :info_window => info)
count = count + 1
}
@map.overlay_global_init(GMarkerGroup.new(true, markers),"job_markers")
# zoom to the source
@map.center_zoom_init([@jobs.first['latitude'], @jobs.first['longitude']], 12)
end
end

First, take the jobs out from the session where we stored them earlier on. We will need them in the view, so make them an instance variable. Next, create the map object and initialize it as described in Chapter 2. Then create a marker for each of the jobs and put them into a hash. Finally, overlay these markers on the map and zoom in to the map to display it.

Create a file named index.rhtml in RAILS_ROOT/app/views/map:

<table width="1024px">
<tr>
<td valign="top">
<%= GMap.header %>
<%= javascript_include_tag("markerGroup") %>
<%= @map.to_html%>
<%= @map.div(:width => 640, :height => 480)%>
</td>
<td>
<div id="display" style="height:480px;overflow: auto;width:384px">
<%= render :partial => 'jobs'%>
</div>
</td>
</tr>
</table>
<p/>
<%= link_to 'Back to Facebook Job Board application', 'http://apps.facebook.com/job_board'%>

As in Chapter 2, we add the GMap headers and include the marker group JavaScript files then convert the map object from the action into HTML and place the div element in the page. This will show us the map of jobs that have been found from Indeed.

We're also going to place a list of the jobs that we've found earlier on next to the map&mdash;so render a job partial next to it and set the style to overflow to the height of the map. This allows you to view the map all the time even as you scroll through the list of jobs found.

Create a file called _jobs.rhtml in the RAILS_ROOT/app/views/map:

<h2><%= @jobs.size %> jobs displayed.</h2>
<ol>
<% count = 1
@jobs.each { |job| %>
<li class="title">
<a href="#" onclick="job_markers.showMarker(<%= count %>);return false;"><%= 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>
<%
count += 1
} %>
</ol>
<span id=indeed_at><a href="http://www.indeed.com/">jobs</a>
by <a href="http://www.indeed.com/" title="Job Search">
<img src="http://www.indeed.com/p/jobsearch.gif" style="border: 0; vertical-align: middle;" alt="job search">
</a></span>

Note that this is very similar to the jobs listing in the Facebook application. The main difference is that while clicking on the job title in the Facebook application shows the job URL, clicking on the job title here will show the map showing the location of the job.

Displaying the location of the jobs on the map

Creating a link on each job to show the news and blog articles

Next we want to find out more about the companies that posted the jobs. In each job, we want to place a link to search Internet news on the company and another to search for blog articles that mention the company.

Creating a link on each job to show the news and blog articles

When we click on the link we want to display the list of news or blog articles to the right of the map, where the list of jobs is now. We are going to do this using AJAX to replace the HTML with the list of news or blog articles. However, we also want to be able to go back to the list of jobs that we had originally.

To do this, modify the index method of the map controller and add a few new lines into the information box that is popped up when we click on the marker:

@jobs.each { |job|
job board, mashup applicationlink creating on jobs, Google maps usedinfo = <<END
<div style='width: 350px'>
<label>#{job['jobtitle']} (#{job['company']})</label> <br/>
#{job['snippet'].capitalize}</div>
<span><a href="#" onclick="new Ajax.Updater('display', '/map/news?company=#{job['company']}', {asynchronous:true, evalScripts:true}); return false;">News</a></span>&nbsp;
<span><a href="#" onclick="new Ajax.Updater('display', '/map/blogs?company=#{job['company']}', {asynchronous:true, evalScripts:true}); return false;">Blogs</a></span>&nbsp;
<span><a href="#" onclick="new Ajax.Updater('display', '/map/list', {asynchronous:true, evalScripts:true}); return false;">Jobs list</a></span>
END
markers[count] =
GMarker.new([job['latitude'],
job['longitude']],
:title => job['jobtitle'],
:info_window => info)
count = count + 1
}

Remember that the AJAX links will not work unless you have added the default JavaScript libraries in your layout file main.rhtml:

<!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>

I will describe what the news and blog links will do in the next few sections but let's look at what happens when you click on the job list link. Add a new method in the map controller:

def list
@jobs = session[:jobs]
end

Then create a file called list.rhtml in RAILS_ROOT/app/views/map:

<%= render :partial => 'jobs'%>

This view simply renders the same partial that we created earlier on.

The next two mashup APIs we are using are search based, like Indeed. The main idea behind using these two APIs is to search for information on the company that is posting the job then display them in the same right panel as the job list.

Searching and displaying news from Daylife

In this mashup we will use only one API endpoint from the Search API, which is getRelatedArticles. We will be searching for news the same way as we did searches on Indeed, that is, constructing the REST URL and sending a GET request to Daylife using Net::HTTP.

Searching for news on the company

To search for news on the company that posted the job, add a new method in the map controller:

def news
server = 'freeapi.daylife.com'
protocol = 'xmlrest'
version = '4.2'
service_name = 'search'
method_name = 'getRelatedArticles'
access_key = 'YOUR API ACCESS KEY'
shared_secret = 'YOUR SHARED SECRET'
query = params[:company]
core_input = query
url = "http://#{server}/#{protocol}/publicapi/#{version}/ #{service_name}_#{method_name}"
hash = {}
hash[:signature] = Digest::MD5.hexdigest(access_key +
shared_secret + core_input)
hash[:accesskey] = access_key
hash[:query] = query
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)
@results = results['payload']['article']
if @results.nil?
flash[:notice] = "No news found for this company."
@results = {}
end
else
flash[:notice] = res.error!
end
end

As with searching through Indeed, we send a GET request to Daylife using Net::HTTP and parsing the resulting XML into nested hashes. However we need to specify the return format and an API version. We also need to create a signature that is sent along with the API access key with each call.

hash[:signature] = Digest::MD5.hexdigest(access_key + shared_secret + core_input)

To create the signature, we concatenate the API access key, the shared secret, and a core input that varies according to the API called. For the API we are calling, this core input is the query we want send to Daylife. This concatenated string is then hashed with the MD5 hash function to return the signature.

We also need a view corresponding to the action method. Create a file called news.rhtml in the RAILS_ROOT/app/views/map folder:

<h1>News (by <%= link_to "Daylife", "http://www.daylife.com"%>)</h1>
<span class="notice"><%= h flash[:notice] %></span>
<br/>
<ol>
<% @results.each { |article| %>
<li class="title">
<a href="<%= article['url']['content']%>" target="job"><%= h article['headline']['content'] %></a>
<div class="description">
<%= article['excerpt']['content'] %>
</div>
</li>
<% } %>
</ol>

This will be displayed to the right of the online map.

Searching and displaying blog articles from Technorati

Technorati provides a set of REST APIs for developers to extract and integrate their data in other applications. The APIs can be called using GET or POST requests. We will be using the Technorati API search to search for blog articles that discuss the recruiting company.

Searching for blog entries on the company

As with the Indeed and Daylife searches, we will be using Net::HTTP to send a GET request to Technorati and then parsing the XML that is returned using XmlSimple. The steps are straightforward and don't need much explanation at this stage.

First, add a new method in the map controller:

def blogs
url = 'http://api.technorati.com/search'
hash = {'key' => 'YOUR TECHNORATI API KEY',
'query'=> params['company'],
'authority' => 'n'}
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)
@stories = results['document']['item']
flash[:notice] = "No blog entries found for this company." if @stories.nil?
else
flash[:notice] = res.error!
end
end

Then create a view corresponding to the blogs action. Create a file called blogs.rhtml in the RAILS_ROOT/app/views/map folder:

<h1>Blogs entries (by <%= link_to "Technorati", "http://www.technorati.com"%>)</h1>
<span class="notice"><%= h flash[:notice] %></span>
<br/>
<ol>
<% unless @stories.nil?
@stories.each { |story| %>
<li class="title">
<a href="<%= h story['permalink']%>" target="job"><%= h story['title']%></a>
<div class="description">
<%= story['excerpt']%>
</div>
</li>
<% }
end
%>
</ol>

And we're done with the mashup!

Summary

Mashup applications are complete applications that use APIs in a synergistic way to provide value that does not exist before. In this mashup application, we showed how a job board could benefit from mashing up APIs from various providers including Facebook, Google Maps, Indeed, Daylife, and Technorati. The combination of the services and the uniqueness of the emergent services show how new applications can add extra value to their existing functions by mashing up external services and integrating them as part of their own.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.97.189