| |
This demo is a game where the users tries to match groups of images
to groups of tags based on what word they have in common. The words,
images and tags are all taken from external sites which makes the game
dynamic and every game is unique. The demo is all written in Ruby on Rails and
uses AJAX to create the user interface. |
 |
The Demo
The process of the Image-Tag matching game is as follows:
- A REST robot goes to Digg Spy and gets 5 words from
Digg stories that have recently been added, voted upon or commented.
These words are called "seed words" since they seed the game with
values.
- The seed words are used as input to a REST robot that
searches Flickr
for images related to the word in question. If no images are found on
Flickr the robot tries Zooomr instead.
- The seed words are also used as input to a REST robot that
goes to Del.icio.us to find related tags, if no such tags are
found at Del.icio.us
the robot goes to Wordpress instead.
- The images and tags related to a seed word are grouped
together and showed to the user.
- The user then have a limited time to try to determine which
group of images that are connected to which group of tags (ie have a
common seed word). Matches are done by dragging and dropping the images
on top of the tags.
The Robots
All in all there are 3 robots in this demo. On to get the seed words
from Digg Spy, one to get images from Flickr or Zooomr and finally one
to get related tags from Del.icio.us or Wordpress.
All these robots used in this demo are REST robots that return XML.
Since all robots need to be executed as quickly as possible (to not
make the waiting time for the user too long) they are all tweaked for
higher performance using page changes to lower the size of pages, not
using frames or cookies to send as little data back and forth between
the sites and the openkapow server etc. For more details about robot
performance enhancing take a look at the tutorial "Improve robustness
and performance of an RSS robot" (same techniques that works for an RSS
robot works for a REST robot).
The GameController
This is the main Ruby on Rails controller in the demo. The method used to acctually
play the game is named
play_the_game. It is
from this method that the
robot that gets the seed words, and the robots that gets the images and
tags are called. In order to make the demo quicker to execute the
robots to get images and tags are run in parallell threads.
threads = []
seed_words.each{ |word|
threads << Thread.new {
@related_tags[word] = get_related_tags_for_word(word)
}
}
seed_words.each{ |word|
threads << Thread.new {
@related_images[word] = get_related_images_for_word(word)
}
}
threads.each {|thread| thread.join}
In the end of the
play_the_game method the
partial view
images_and_tags
is called to show all the images and tags that the robots have returned
to the user.
Calling a REST Service from Ruby on Rails
All REST robots are called from one Controller, the
OpenKapowRESTServiceController. This
controller only have one method,
call_rest_service, which take the path to the
openkapow REST service
(the path is everything after "http://service.openkapow.com"). The
call_rest_service method then performs a GET
request to openkapow and
returns a RestResponse model.
class OpenKapowRESTServiceController < ApplicationController
model :rest_response
hide_action :call_rest_service
def call_rest_service(path)
require 'net/http'
begin
Net::HTTP.start("service.openkapow.com") do |http|
response = http.get(path, 'Accept' => 'text/xml')
response_code = response.code
xml = nil
message = response.message
if response_code == "200"
xml = response.body
end
@rest_response = RestResponse.new(response_code, xml, message)
end
rescue => err
#If nothing else works, return HTTP 503 = Service Unavailable
@rest_response = RestResponse.new("503", nil, "#{err.message}")
end
end
end
Below is an example of a call to
OpenKapowRESTService.call_rest_service. First
a new instance of the
controller class needs to be created, then
call_rest_service
is called
with the REST robots path (including input values & specifying the result format XML). If the HTTP
response code returned in
response.response_code
is "200" (which means
all went OK) then the returned XML needs to be parsed and handled. If
something went wrong then the error message is written to the
flash and later written out in the views to inform the user of the errors.
@open_kapow_rest_controller = OpenKapowRESTServiceController.new
....
response =
@open_kapow_rest_controller.call_rest_service
("/demo/getrelatedimages.rest?resultformat=xml&searchText1=#{word}")
if response.response_code== "200" and !response.xml.nil?
....
//parse XML
....
else
flash[:error] ="An error occurred when
contacting Flickr and/or
Zooomr:<br/>#{response.message}"
end
Note that the
RestResponse model is not
associated with
ActiveRecord
since there is no need to save anything from the model into a database.
Nothing in the whole application uses
ActiveRecord
which means that
there are some changes to the default Rails configurations, for more
information check the free excerpt from
Rails Recipes.
Parse XML in Ruby on Rails
In this demo all XML returned by REST robots are parsed using
REXML.
Below is an example of such parsing, in this case the code parses the
XML returned by the robot that gets images from Flickr or Zooomr. The
returned XML is in the format:
<imageGroup>
<image1>..</image1>
<image2>..</image2>
<image3>..</image3>
<image4>..</image4>
</imageGroup>
A new
REXML::Document is created
from the XML the robot
returned. In this XML all
<imageGroup>
tags are looped
through (this is done even if there should just be one
<imageGroup> tag per response)
and then the text from the
imageX elements are added to the image array.
doc = REXML::Document.new(response.xml)
doc.elements.each('result/imageGroup') do
|group|
images =
add_text_to_array_if_not_empty(images, group.elements["image1"].text)
images =
add_text_to_array_if_not_empty(images, group.elements["image2"].text)
images =
add_text_to_array_if_not_empty(images, group.elements["image3"].text)
images =
add_text_to_array_if_not_empty(images, group.elements["image4"].text)
end
The
add_text_to_array_if_not_empty
adds a new item to an array unless the text in question is empty (to
avoid empty items in an array).
The Views
The main view is play.rhtml and in this view the partial view
_images_and_tags.rhtml show all acctual images and tags to match up.
These views contains both AJAX code generated by the Ruby on Rails
prototype helpers and javascript functions that manipulate the content
to enable all the necessary layers to show and hide and works as
needed.
One example of this is shown below, where the "Play the Game Now" link
is printed out using the Ruby on Rails prototype helper method
link_to_remote to create an AJAX call that loads the result of
the
GameController.play_the_game
method into the content div (the method acctually loads the
images_and_tags partial view into this div). When the link is clicked
the
progressBar div will be shown (this div contains the animated
circle that shows that the demo is working) and the
playGame div will
be hidden (this is the div that contains the link itself). Furthermore
the
resetCorrectGuesses javascript function will be executed to set the
number of guesses to 0. Once the AJAX call is complete the
progressBar
div will once again be hidden.
<div id="playGame">
<p><%= link_to_remote("Play the Game Now",
:update => "content",
:loading => "Element.show('progressBar');
Element.hide('playGame');
resetCorrectGuesses();",
:complete => "Element.hide('progressBar');",
:url => {:action => :play_the_game})%>
</p>
</div>
In the
images_and_tags partial view all the groups of images and tags
are printed. Each groups of images is made into a
draggable_element and
each group of tags are made into a drop zone using the method
drop_receiving_elements.
Summary
This demo shows how to use openkapow robots together with Ruby on
Rails. Basically the robots are used to expand the functionality of the
Ruby on Rails code and it shows how to make a Rails based mashup that
used openkapow robots to interact with websites for functionality and
data. The rapid coding that is possible in Rails fits very well with
the rapid robot development possible in openkapow.