Flickr Panda – a mashup written in Ruby on Rail and Ajax

Flickr is one of the largest online photo management and sharing application which claims to host more than 3.6 billion of images as June 2009. Flickr provides APIs for outside developers to develop mashup application which can interact with Flickr services. Flickr Panda is a set of AIPs allows users to take a list of recent public and safe photos.

In web development, a mashup is "a web page or application that combines data or functionality from two or more external sources to create a new service. The term mashup implies easy, fast integration, frequently using open APIs and data sources to produce results that were not the original reason for producing the raw source data.

An example of a mashup is the use of cartographic data from Google Maps to add location information to real estate data, thereby creating a new and distinct Web service that was not originally provided by either source.

Below is a step by step instruction to develop a simple Flickr Panda mashup application using in Ruby on Rail and Ajax.

Installation and configuration

Install Ruby on Rail:

The easy way to install Ruby on your machine is to visit download section of rubyonrails.org, and download the bundle  install package, then download and install RubyGem.
When you have Gem installed, then you can install Rails by command:

gem install rails

Configure MySQL

By default, ROR bundle comes with a light weight database carrier call SQLite but I think that many web developers are fans of MySQL. To install MySQL :

gem install mysql

After installing MySQL, when you start your application, you may suffer the following error:

"!!! The bundled mysql.rb driver has been removed from Rails 2.2. Please install the mysql gem and try again: gem install mysql."

The problem is because MySQL 5.1 client library doesn't play well with Rails - the solution is simple however:

Create ROR application

rails flickr

The above command will generate a ROR standard directory structure
ruby_create
ruby_dir

Change the configuration of the database in Rails application:
Browse to flickr/config, open the file database.yml  by wordpad and make some change:

JAVA:
# MySQL version

#   gem install
development:
adapter: mysql
database: ruby
encoding: utf8
username: root
password: admin
host: localhost

* You may need to change the values database, username and password to the setting on your machine

Start webrick server:

In flickr directory:

ruby script/server

By default, webrick server will start on port 3000, open a web browser and type in:

http://localhost:3000/flickr/

to view your ROR application.

Note: even that your application does not need to use any database, you still have to install and config MySQL ( or another database carrier ) accurately in order for ROR to start a service.

Now, you have everything ready, let start to code.

Coding

ROR follows MVC model with a very strict naming convention, you need to pay a good attention to the names of model, view and controller

Model

To create a ROR model, you can go flickr/app/models , then create a new file named flickr.rb, but there is a better way

rails script/create model flickr

This command will automatically generate all necessary files and folders, such as controller, view, layout, test and set up the configuration as well.
Browse to flickr/app/models/ and open file flickr.rb to add some codes.
We are going to use 3 APIs from Flickr:

flickr.panda.getList : get a list of available Pandas
flickr.panda.getPhotos : get a list of photos under a Panda name
lickr.photos.getInfo: get information of a specific photo.

Noting that , in order to use Flickr APIs, you need to have a API key, to obtain a new key go to
http://www.flickr.com/services/api/keys/apply/

Because Flickr API will return a XML document, so we need to have to import rexml package to handle XML and of course net package to send REST request, you model will start with:
require 'net/http'
require 'rexml/document'

Sample code :

JAVA:
require 'net/http'
require 'rexml/document'
# key : ce8cc447f982b2f864ba423b8dff3e1a
# secret : 3abb6ef1ec9e1d75
# http://www.flickr.com/services/api/explore/?method=flickr.panda.getList
class Flickr
#@@proxy_addr = 'localhost'
#@@proxy_port = 8000
@@api_key=''
@@uri_list = 'http://api.flickr.com/services/rest/?method=flickr.panda.getList'
@@uri = 'http://api.flickr.com/services/rest/?method=flickr.panda.getPhotos'
@@uri_info = 'http://api.flickr.com/services/rest/?method=flickr.photos.getInfo'

def initialize(key)
@@api_key = key
@@uri ="#{@@uri}&api_key=#{@@api_key}"
@@uri_list ="#{@@uri_list}&api_key=#{@@api_key}"
@@uri_info ="#{@@uri_info}&api_key=#{@@api_key}"
end

def get_list()
#res = Net::HTTP::Proxy(@@proxy_addr, @@proxy_port).get_response(URI.parse(@@uri_list)).body
res = Net::HTTP.get_response(URI.parse(@@uri_list)).body
xml = REXML::Document.new(res)
re="
<ul id="
list">"
xml.elements.each('rsp/pandas/panda') do |e|
re =  re + "
    <li>"
+"<a href="\&quot;javascript:getPanda('#{e.text}')\&quot;">"+ e.text+"</a>"+ "</li>
"

end
re = re + "</ul>
"

puts re
return re
end

def get_panda(panda_name,limit=10)
panda_name = panda_name.gsub(' ','+')
uri_temp = "#{@@uri}&amp;panda_name=#{panda_name}&amp;per_page=#{limit}&amp;page=1"
#res= Net::HTTP::Proxy(@@proxy_addr, @@proxy_port).get_response(URI.parse(uri_temp)).body
res= Net::HTTP.get_response(URI.parse(uri_temp)).body
xml = REXML::Document.new(res)
photos =[]

xml.elements.each('rsp/photos/photo') do |e|
photos &lt;&lt;e
end
i=0
re= "
<div id="
panda_inside">
<h2>List #{limit} photos of Panda #{panda_name}</h2>
"

re= re + "
<ul id="
panda_list">"
while i

if photos[i].attributes["title"]==""
photos[i].attributes["title"]="No Title"
end
re= re + "
    <li>"
+ "<a href="\">" + photos[i].attributes["title"] + "</a></li>
<a href="
\">"
i+=1
end
re=re + "</a></ul>
</div>
<a href="
\">"
puts re
return re
end</a>

def get_info(id)
uri_temp = "#{@@uri_info}&amp;photo_id=#{id}"
#res= Net::HTTP::Proxy(@@proxy_addr, @@proxy_port).get_response(URI.parse(uri_temp)).body
res= Net::HTTP.get_response(URI.parse(uri_temp)).body
xml = REXML::Document.new(res)
owner=""
title=""
date=""
xml.elements.each('rsp/photo/owner') do |e|
owner = "Author: " + e.attributes["realname"] + ". Location: " + e.attributes["location"]
end

xml.elements.each('rsp/photo/title') do |e|
title = e.text
end

xml.elements.each('rsp/photo') do |e|
date= e.attributes["dateuploaded"]
date = Date.jd(date.to_i)
date=date.strftime("%B %d, %Y")

end
re= "<span id="info">Title: " + title + "
Uploaded date in Julian: "
+ date.to_s + "
"
+ owner + "</span>"
return re
end

def get_image(farm,server,id,secret)
src = "'http://farm#{farm}.static.flickr.com/#{server}/#{id}_#{secret}.jpg'"
img = "<img src="#{src}" alt="" />"
info = get_info(id)
re = img + "
"
+ info
return re
end
end

if your program runs through a proxy, then uncomment

#@@proxy_addr = 'localhost'
#@@proxy_port = 8000

and use the net command with proxy:

#res = Net::HTTP::Proxy(@@proxy_addr, @@proxy_port).get_response(URI.parse(@@uri_list)).body
#res= Net::HTTP::Proxy(@@proxy_addr, @@proxy_port).get_response(URI.parse(uri_temp)).body
#res= Net::HTTP::Proxy(@@proxy_addr, @@proxy_port).get_response(URI.parse(uri_temp)).body

Controllers
rails script/create model flickr

ROR will generate a controller name flickr_controller.rb under the directory  flickr/app/controllers/
We will need three actions to handle to different request from the views:

  • index
  • get_panda
  • get_image

Sample code:

JAVA:
class FlickrController &lt;ApplicationController     @@flickr = Flickr.new('ce8cc447f982b2f864ba423b8dff3e1a')   def index     @fl = @@flickr   end   def get_panda     @fl = @@flickr     panda_name = params[:panda_name]     re = @fl.get_panda(panda_name)     render :text=&gt;re
end

def get_image
@fl = @@flickr
farm = params[:farm]
server = params[:server]
id=params[:id]
secret = params[:secret]
re = @fl.get_image(farm,server,id,secret)
render :text=&gt;re
end
end

Views
go to flickr/app/views/flickr/, create a file named  index.html.erb

HTML:
Ruby on Rails - Panda Search
&lt;%= javascript_include_tag "flickr.js" %&gt;;
&lt;%= stylesheet_link_tag "flickr.css" %&gt;;
<div id="wraper">
<div id="top">&lt;%= image_tag "rails.png" %&gt;<span id="header"> Ruby on Rails</span>
<h1>Flickr Panda Search</h1>
<div>&lt;%= render :text=&gt;@fl.get_list() %&gt;</div>
</div>

JavaScript<

The javascript will be stored under directory flickr/public/javascripts/
Create a file named flickr.js and use the syntax bellow to include a javacript in the View
<%= javascript_include_tag "flickr.js" %>

JAVASCRIPT:
var asyncRequest;

function getPanda(name){
//alert(query.value);

try{
asyncRequest = new XMLHttpRequest();
asyncRequest.onreadystatechange = displayPanda;

var url ="http://localhost:3000/flickr/get_panda?panda_name=" + name;
url=url+"&amp;sid="+Math.random();
//alert(url);
asyncRequest.open('GET',url,true);
asyncRequest.send(null);
document.getElementById('panda').innerHTML = '<img id="loader" src="/images/loader.gif" alt="" />';
document.getElementById('img').innerHTML = '';
}
catch(exception){
alert(exception);
}
setCookie("panda",name,180,'/');
}

function displayPanda(){
//alert(query.value);
if(asyncRequest.readyState==4 &amp;&amp; asyncRequest.status==200){
var display =asyncRequest.responseText;
document.getElementById('panda').innerHTML=display;
setCookie("panda_list",display,180,'/');
}
}
function getImage(farm,server,id,secret){
try{
asyncRequest = new XMLHttpRequest();
asyncRequest.onreadystatechange = displayImage;

var url ="http://localhost:3000/flickr/get_image?farm="
+ farm + "&amp;server=" + server + "&amp;id=" + id + "&amp;secret=" + secret;
url=url+"&amp;sid="+Math.random();
asyncRequest.open('GET',url,true);
asyncRequest.send(null);
document.getElementById('img').innerHTML = '<img id="loading" src="/images/loading.gif" alt="" />';
}
catch(exception){
alert(exception);
}
}

function displayImage(){
if(asyncRequest.readyState==4 &amp;&amp; asyncRequest.status==200){
var display =asyncRequest.responseText;
document.getElementById('img').innerHTML=display;
}
}

CSS

The stylesheet will be stored under directory flickr/public/stylesheets/
Create a file name flickr.css and use the syntax bellow to include a CSS in the View

<%= stylesheet_link_tag "flickr.css" %>

CSS:
body {margin:0px;padding:0px;font-family: Verdana, Arial, sans-serif;font-size:9pt;}
#wraper{width:1003px;min-height:600px;margin:0 auto;border:7px inset #666666; padding:10px 20px 20px 20px;}
#header {font-size:20pt;font-weight:bold;}
#footer{clear:both;}
.middle{float:left;vertical-align:text-top;}

Screen shots:

flickr_2

Download: flick_panda.rar

NDLoc, 28/06/2009

June 28, 2009    Posted in: Web Development