Thursday, August 25, 2011

link_to remote in Rails 3

If you're like me, and come from a rails 2 background, you'll probably have noticed that in Rails 3 the way of creating these nifty AJAX links is completely different then the way it used to be. Actually it's not that much different, you're just required to do alot more work, and honestly the documentation on this is piss-poor on the web. I've found a few blogs that gave some insight on what needs to be done, but they're all written for jQuery.

In this case, I'm going to show you how to create a simple link_to remote which uses prototype to call an action on of our controllers and relies on a simple rj.erb file to update a
element on our page.

The first step we need to undertake is to create a JavaScript file. We'll call it bindings.js and it should be located in your /public/javascripts folder. The content of the file should something like this:

document.observe("dom:loaded", function(){
$('our_div_id')
.observe("ajax:success", function(evt, data, status, xhr){
// The response is Javascript, so evaluate it.
eval(xhr.responseText);
// Return false to avoid jumping
return false;
})
.observe("ajax:failure", function(evt, data, status, xhr){
alert("failed");
// Insert a custom error message when something goes wrong
$('
our_div_id').replace('
');

$('
our_div_id').insert('

A problem occured.

');


// Return false to avoid jumping
return false;
});
});


Make sure that you replace the 'your_div_id' with the actuall id of yourdiv-tag. Note that in regards to jQuery, you do not need to append the # symbol before the id.

The general idea behind the javascript is that we observe the 'dom:loaded' event of the entire HTML document, which is triggered after loading the page, and then we bind the 'ajax:success' and 'ajax:error' functions.
Why these 2 functions? Because they're the functions defined in rails.js. These functions are called when an AJAX calls succeeds or fails. If you want to know the details, check the rails.js file

I'm not going to discuss how your action on the controller should look like, as this is nothing special. I personally do not bother with the respond_to |format| stuff, and just let rails decide which action template needs to be server. In most cases this is always correct.
The contents of your action.js.erb file should be pure Javascript. Here is the content of mine:

$('semantic').replace('
');

$('semantic').insert("

<%=h @country.abstract.value %>

");

$('semantic').insert("

<%=h @country.comment.value %>

");

$('semantic').insert("

Currency: <%=h @country.currency.value %>

");

$('semantic').insert("

Population: <%=h @country.population.value %>

");

$('semantic').insert("

Capital: <%=h @country.capital.value %>

");


Basicly this piece of Javascript updated the semantic div on my page with new content.

The last step is creating the actuall link. This is the easiest part of everything and the syntax looks like this:

= link_to @vacancy.country.name, semantic_country_url(@vacancy.country.id), :remote => true

The trick is in the ":remote => true" part, which allows rails to make it an AJAX call.

In short:

  • Create a Javascript file that binds the ajax callbacks to your element
  • Create a js.erb (or other template) to generate the javascript/output
  • Create a link_to :remote => true
Hope this makes it clear on how AJAX works in Rails 3

Tuesday, August 23, 2011

Ruby on Rails : Consuming a SPARQL endpoint

Right,

At work, I've been plunged into the semantic technologies such as OWL, SPARQL and RDF. The goal is to create a Ruby on Rails demonstration site that relies on semantic technologies to gather information from the web, based on specific keywords. Because we can go really far into this, I have taken the following points for demonstration purposes:

  • We base ourselves on the Country of our model
  • We collect specific information such as a generic description, captial, currency and population
Ok, with that defined, we take dbpedia as our source of information (http://dbpedia.org). DBPedia has a public SPARQL endpoint that can be used to generate SQPARL queries to retrieve information from their RDF datasets. I'm not going to discuss how all these technologies work, just how I approached the problem and solved it.

First things first, I create a class that allows me to consume queries. Because I've spent several hours working out how RDF in Ruby on Rails works (and did not found a good solution), I have opted to let the SPARQL endpoint return it's data to me in the form of JSON strings. The benefit here is that the amount of data transfered is as small as possible. This is the complete class that performs the search for me on DBPedia:

# This class represents the SearchEngine to search RDF stored, relying on semantic technologies.
# The class is fine tuned for specific searches on hardcoded repositories used for the ESCO matching
# demonstration. Special functions will be created that allow the searching of specific data used for
# the demonstration.
#
# The class can be used by creating a new instance and then call the appropriate search function that
# will search the specific RDF store and return information related to the specific query. The information
# returned will always be as a single string, which can be used to display on a website.
class SemanticSearchEngine
# This function will try to query the SPARQL endpoint of the dbpedia website and return the absolute
# URL to the RDF store for the specified country
# The function returns an RDF triplet collection containing several bits of information
# about the requested city, in the language specified.
def country_information(country, language_code)
query = "
PREFIX dbo: <http://dbpedia.org/ontology/
>
PREFIX prop: <http://dbpedia.org/property/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT ?abstract ?comment ?population ?capital ?currency
WHERE {
?country rdfs:label '#{country}'@en;
a dbo:Country;
dbo:abstract ?abstract .
FILTER langMatches( lang(?abstract), '#{language_code}')
OPTIONAL { ?country prop:populationEstimate ?population }
OPTIONAL { ?country prop:capital ?capital}
OPTIONAL { ?country rdfs:comment ?comment FILTER langMatches( lang(?comment), '#{language_code}') }
OPTIONAL { ?country prop:currency ?currency }
}"
# execute the query and retrieve the RDF data.
SemanticCountry.new(retrieve_json_data("http://dbpedia.org/sparql?query=#{CGI::escape(query)}&format=json")['results']['bindings'].first)
end

# Retrieves the information from ESCO in JSON format with the given URL
# If no data is found, an empty JSON hash is returned instead.
private
def retrieve_json_data url
JSON.parse HTTParty.get url
end
end
The most important part of the class is the actuall query that we're sending towards the SPARQLE endpoint:

PREFIX dbo:
PREFIX prop:
PREFIX foaf:
PREFIX rdfs:
SELECT ?abstract ?comment ?population ?capital ?currency
WHERE {
?country rdfs:label '#{country}'@en;
a dbo:Country;
dbo:abstract ?abstract .
FILTER langMatches( lang(?abstract), '#{language_code}')
OPTIONAL { ?country prop:populationEstimate ?population }
OPTIONAL { ?country prop:capital ?capital}
OPTIONAL { ?country rdfs:comment ?comment FILTER langMatches( lang(?comment), '#{language_code}') }
OPTIONAL { ?country prop:currency ?currency }
}


The idea behind the query is as follows:
  • Select the country which has label the value we specify: e.g 'United Kingdom'
  • Verify that the datasource we select is a Country according to DBPedia
  • Verify that the subject has a property called abstract, and filter the selection for the specified language
  • Optionally get the estimated population
  • Optionally get the capital of the Country
  • Optionally get any comments in the English language of the Country.
Normally it should be possible to specify in the HTTP header that we want to retrieve the information as a sparqle result + JSON, but I encountered some issues with this during the execution of the query, so I added to the end of the entire url $format=json to make sure that the information is returned as a JSON string.

To eliminate multiple rows, I only select the first hash beeing returned, and neglect everything else. This ofcourse leads to the potential case where data is lost, or the wrong data is selected, but as I said before, it's only a demonstration.

To easily seperate the parsing logic of the JSON hashes, I created a small seperate class that strips the array from it's values into small managable entities. This is the SemanticHash class, which looks like this:

class SemanticHash
attr_accessor :value, :language, :type
# Initializes the instance of the class using the values stored inside the Hash.
# The Hash needs to be constructed in the following way:
# - 'value'
# - 'xml:lang'
# - 'type'
# If any of the keys is missing, an argumentException will be raised.
def initialize(value = {})
self.value = value['value'] || ""
self.language = value['xml:lang'] || 'en'
self.type = value['type'] || ""
end
end
To finish up, on my views where I display the countries, I provided a new div element that would receive the information. With a link_to remote action on the view I make a call to the controller action that retrieves the information from DBPedia. The code of the action looks as follows:

country = Country.find params[:id]
engine = SemanticSearchEngine.new
@country = engine.country_information country.name, 'en'
render(:update) { |page| page.replace_html 'semantic', :partial => 'semantic/country', :layout => false}
I know this is not the cleanest way of doing this for a remote action, but I'm still struggling to understand how remote links work in Ruby on Rails 3, and this also does the trick for me at the moment. This returns Javascript that updates the div with id semantic and places all the content from the partial view inside that div.

Monday, August 8, 2011

World of Wacraft

I've been playing World of Wacraft for nearly seven years now. 2 months ago, I decided to quit the game. I could no longer afford the time I invested in this game, and truth be told, I had a nice history in the game. So I thought I'd rather go out with some nice memories than keep playing till I got frustrated from boredom.

However, yesterday I hopped ingame again after reading some posts on my old guild's website. It didn't take long for the conversations to get started again ingame and before I knew it, I was dragged into the latest raiding content without having done it before or reading/preparing for what was comming.

In the seven years that I've been playing, I've always been somebody who wants to achieve the top of the game, but not at the cost of having fun. I've seen every content that there was to be seen in the game, and I have no regrets of spending soo much time in the game.
That beeing said, my character Nashiko has quite some history in the game. Just look at the profile, achievements or whatever and you can see I've been everywhere and have done almost anything that the game had to offer.

Yesterday's raid night however opened a new door of fun for me. I've always been rather lucky when it comes to receiving items ingame, and last night was no different. After beeing dragged through some puzzeling encounters, we managed to beat the Firedruid himself, and behold he dropped his scythe.
Beeing the only feral druid in the guild, and the other people already beeing geared, I received the staff despite the fact that I was not really playing anymore. Equipping this weapon ingame bestows a new effect on Feral druids when they enter combat :



I reactivated my account, and made the agreement that I'd help out my fellow guildmembers untill StarWars The Old Republic is released, after which I will bid them a farewell and take my gamingexperience elsewhere.

But for now, I think i'll definitly enjoy running around with my furr on fire !