Sunday, January 20, 2008

Orkut social network community profiling with GreaseMonkey and Rails, round one.

Hi,

I wanted to figure out some statistics about some Orkut (Google equivalent of Myspace or Facebook) social communities. I want to know the mean age, the gender rate and other things among given communities. And actually I'm getting a lot more as you'll see. The method I'm showing is very simple and can be reused for other purposes. It's automatic enough to grab profiles 15 by 15 and build a good sampling of the community, but not enough to grab ALL the profiles. Anyway, I guess Google would kick out crawlers wanting to grab all the profiles.

The challenge for collecting those data is that you need to be logged in to crawl Orkut communities. Then only you can grab information in the HTML pages if you manage to handle the navigation properly. I didn't really manage to automate all the process using the HPricot HTML parser.

Instead I went for a semi-automatic combination of GreaseMonkey scripts to grab the profiles and a Rails server to store the data. Now I only need to browse every members page of an Orkut community to get all its member profiles stored inside my database. Then I also have a rails action to export data in csv so I can open it in spreadsheet editor:


The first thing is to install the GreaseMonkey Firefox plugin.
Then you'll install the following user script name orkut_crawler.user.js:

// ==UserScript==
// @name Orkut_crawler
// @namespace http://livetribune.org/
// @description grab orkut properties
// @include *
// @exclude http://diveintogreasemonkey.org/*
// @exclude http://www.diveintogreasemonkey.org/*
// ==/UserScript==

var scripts = [
'http://localhost:3000/javascripts/orkut_crawler.js'
];
for (i in scripts) {
var script = document.createElement('script');
script.src = scripts[i];
document.getElementsByTagName('head')[0].appendChild(script);
}


What we are doing here is including the Prototype Javascript library to make it easier to grab the information using CSS selectors. I tried to paste the prototype lib directly inside the browser script but it didn't worked for some unknown reason. So anyway, this last solution works.

You can now make sure the script gets activated when you visit an Orkut page:

OK, now we need to set up our actual orkut_crawler script as well as the Rails backend.
build a simple Rails app using the ">rails orkut_crawler" command line.
You can use any database. I used the default Sqlite DB in my case.

Then create the database with that command ">rake db:create" inside the orkut crawler directory.

Now, let's make a simple persistent model to store our data:
"> ruby script/generate model item"
Now edit the db/migrate/001_create_items.rb migration file and write:

class CreateItems <>

Let's create the table: ">rails db:migrate"

Now we need to create the orkut_crawler Javascript file that'll be called by our GreaseMonkey script. make a new file called public/javascripts/orkut_crawler.js

Inside that file, you first need to copy the Prototype javascript library you'll find in public/javascripts. Then write this code at the end of the file:


/* Crawler */

function appendNewScript(src_url, id) {
var headID = document.getElementsByTagName("head")[0];
var newScript = document.createElement('script');
newScript.id = 'snap';
newScript.type = 'text/javascript';
newScript.src = src_url;
headID.appendChild(newScript);
}

function addTuple(line) {
try {
var key = line.childNodes[1].innerHTML;
var value = line.childNodes[3].innerHTML;
key = key.replace(" ", "_");
key = key.replace("/", "_");
key = key.replace(":", "");
value = value.replace("\\", "");
value = value.replace("\"", "");
value = value.replace(";", " ");
value = value.replace(":", " ");
params += "&" + key + "=" + value;
console.log(key, value);
} catch(e) {}
}

var tab;

if (document.location.href.indexOf('CommMembers.aspx') > 0) {//it's a community page
tab = $$('.listitem');
for (var i =0; i<>
setTimeout(tab[i].innerHTML="<" + "iframe src='"+ tab[i].childNodes[1].href + "'/>", 100 * i );
}
} else if (document.location.href.indexOf('Profile.aspx') > 0) {//it's a profile
var params="";
tab = $$('.listlight');
for (var i =0; i< tab.length;i++) { addTuple(tab[i]); }
tab = $$('.listdark');
for (var i =0; i< tab.length;i++) {addTuple(tab[i]); }

params = document.location.search + params;

appendNewScript("http://localhost:3000/data/new" + params);//send the params back to our Rails app!



This code will grab the profile when you browse an Orkut page. If you are rather browsing a community page, then it will open all the profiles of the listed members of this page and thus grab those profiles.

Finally, we need to write a Rails controller that will persist the data (the 'new' action), render a global csv file ('index' action) and even tell how many profiles we have ('size' action). So edit app/controllers/data_controller.rb this way:

require 'cgi'

class DataController < ApplicationController
def new
params.delete 'action'
params.delete 'controller'
params.each_key {|key| params[key] = CGI.escape(params[key])}
puts params.inspect
if existing =Item.find_by_uid(params[:uid])
item = existing
else
item = Item.new
item.uid = params[:uid]
end
item.properties = params.inspect
if item.save
render :text => 'UPDATED' and return if existing
render :text => 'SAVED!'
else
render :text => 'ERROR!'
end
end

def size
render :text => Item.find(:all).size.to_s
end

def index
all_props = []
@items = Item.find(:all)
res = ""
@items.each do |item|
map = eval item.properties
map.each_key do |key|
all_props << key unless all_props.index key
end
end

all_props.each do |key|
res += key + "; "
end
res = res[0..-3] + "\n"

@items.each do |item|
all_props.each do |key|
map = eval item.properties
res += CGI.unescape(map[key].to_s).gsub("\n", " ").gsub("\r", " ").gsub("\r\n", "").gsub(";", " - ") + "; "
end
res = res[0..-3] + "\n"
end
render :text => res
end
end



OK, we are done.
Now make sure our Rails server is working:
"> ruby script/server"

You can also track what is going on:
"tail -f log/development.log"

Now visit some Orkuts community pages with this URL pattern:
http://www.orkut.com/CommMembers.aspx?cmm=10087467&tab=0&na=3&nst=151&nid=0


As you'll see, instead of the normal member's icons, we are now loading the members profiles inside iframes. At the same time, you can ensure in your terminal our Rails server is storing all the profiles.

Finally; look at the profile you collected:
see how many profiles you have collected: http://localhost:3000/data/size
Now let's export the data as csv (this can take a while depending how much data you stored):
in your terminal, execute:
">wget http://localhost:3000/data"
Now rename the data file into data.csv and browse it in OpenOffice for instance:



Ok, you are done. Now you can start making some statistics, but that's for round two!

Thursday, October 18, 2007

Getting ruby-openid OpenID gem to work with JRuby on Rails

As you may know a few native methods aren't yet implemented in JRuby and a few ones aren't even possible to implement due to the POSIX native calls.

I really don't know where odes FileTest#chardev? method fits in, but JRuby doesn't have it right now.

So if ever you want to use the 'ruby-openid' gem in your JRuby on Rails using for instance the official wrapper, there is a workaround.
Indeed if you do nothing, either at the rake setup, either at startup, you'll get something like:

=> Booting WEBrick...
/home/rvalyi/DEV/jruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27: FileTest#chardev? not yet implemented (NotImplementedError)
from /home/rvalyi/DEV/jruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27
from /home/rvalyi/DEV/jruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27
from /home/rvalyi/DEV/jruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from /home/rvalyi/DEV/jruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
from /home/rvalyi/DEV/livetribune/vendor/rails/activesupport/lib/active_support/dependencies.rb:496:in `require'
from /home/rvalyi/DEV/jruby/lib/ruby/gems/1.8/gems/ruby-openid-1.1.4/lib/openid.rb:1:in `new_constants_in'
from /home/rvalyi/DEV/livetribune/vendor/rails/activesupport/lib/active_support/dependencies.rb:496:in `require'
from /home/rvalyi/DEV/jruby/lib/ruby/gems/1.8/gems/ruby-openid-1.1.4/lib/openid.rb:1:in `require'
from /home/rvalyi/DEV/jruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:32:in `require'
from /home/rvalyi/DEV/jruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:32:in `require'
from /home/rvalyi/DEV/livetribune/vendor/rails/activesupport/lib/active_support/dependencies.rb:496:in `require'
from /home/rvalyi/DEV/livetribune/vendor/plugins/open_id_authentication/init.rb:120:in `new_constants_in'
from /home/rvalyi/DEV/livetribune/vendor/rails/activesupport/lib/active_support/dependencies.rb:496:in `require'
from /home/rvalyi/DEV/livetribune/vendor/plugins/open_id_authentication/init.rb:120:in `binding'


The workaround is to override the FileTest module and define a stub value for the chardev? method. That method is to return true or false. Returning always true for that useage seems to work well.

So at the VERY BEGINNING of your environment.rb, just write:

module FileTest
def self.chardev?(file_name); return true; end
def chardev?(file_name); return true; end
end


Then Open Id is going to play nice with your JRuby on Rails app. At the time I'm speaking, I'm on JRuby SVN head trunk and the String#split method is having a little bug that'll ouput a stack trace but fortunately this has no consequence on the functionality.

JRuby rocks!

Saturday, October 06, 2007

migrating Livetribune.com on JRuby on Rails2.0

update: I'm here talking about problems I encountered migrating from Rails 1.2.3/1.2.4 to Rails2.0. My app has been on JRuby since almost the beginning and I had no issue with JRuby (this time at least). Being on JRuby motivated me even more to try Rails2.0 as the routing is said to be optimized by caching as I'll explain there.

The current state of JRuby (trunk) is that low level tests tend to be faster than C-Ruby but Rails is still a bit slower. Some people, included me, tend to think this might be due to the request routing of Rails being slow in JRuby. Indeed Rails routing extensively uses regexps and regexps in JRuby are still a lot slower than in C-Ruby. In fact this is because Java regex engines work with char[] whereas Ruby (including JRuby) represents strings as byte[]. So in JRuby we have to translate byte into char[] and back all the time for regexps untill somebody ports on Java a byte based regexp engine (namely the oniguruma lib).

So for a JRuby dev, it's always nice the hear Rails2.0 made some caching optimization on routing. So I wanted to try it. I didn't finish yet to port the Livetribune application (not yet in prod BTW) but I made great progress. In order to help other people in their migration I'll relate here a few tricks I had to do to make it working.

* url_for don't accept anymore symbols to point controllers. You have to write strings instead. So for instance:
map.connect ':locale/topics/:year/:month/:day/:id', :controller => :topics, :action => 'show'
became
map.connect ':locale/topics/:year/:month/:day/:id', :controller => 'topics', :action => 'show'
else rails will complain symbol :topics have no method [].

* the usefull routing_navigator plugin doesn't work anymore out of the box in Rails2.0 . This seems to be due to the self.template_root method call in the RoutingNavigatorController that doesn't exist anymore in Rails2.0. I didn't fin the work arround so I simply dropped the plugin.

* replace @base_path by ActionController::Base.view_paths whenever its used (in the AuthGenerator plugin for instance) as suggested here:
http://dev.rubyonrails.org/ticket/9689
(still I didn't get AuthGenerator fully working yet. Rails still miss its view templates)

* Since by default Rails2.0 sessions are based on a cookie store, either set it back to file store, either explicitly define a secrete that'll be share between the client browser and your server, like:
config.action_controller.session = { :session_key => "_myapp_session", :secret => '449fe2e7daee471bffae2fd8dc02313a' }
in your environment.rb


After this I had almost everything working again. Still some trouble with AuthGenerator, please provide feedback if you get this one fixed.

Also I installed that Rails 1.2.4 version before hoping it would shout for all the potential issues. It never shouted for those I just wrote about, so may be that could have done it better (or may be it's not too late to include those pointers).

Rails is very cool and Ruby is too. But it's in those kind of migration situations you'll think Java was just safer. I mean, I think half those problems would have been statically highlighted by the compiler. For the other half, let's face it you would have used some IOC injection like Spring so you wouldn't have detected it either (that the big hypocrisy of the today's J2EE world).

I still think nothing is approaching Rails in its niche, but that was just to highlight Ruby will never replace Java fully, even as a language. Rails is OK because the compiler safety is being replaced by the community checking quite effectively, but that wouldn't be true in an enterprise world for instance.

Finally I think JRuby really is the expected silver bullet, because it will allow you to closely choose where things get dynamic but unsafe (Ruby) and where thing get static and safe (Java). Moreover, the new optional Ruby type signature that's being imagined for the new JRuby compiler ( http://headius.blogspot.com/2007/09/compiler-is-complete.html )will just allow you to compile your great Ruby lib in Java and then use it from Java (as a jar bytecode) calling it through normal Java typed methods. It means you'll use safely Ruby libs whenever their author would have decided some part of its API deserves more security (and yes, a bit less agility). I mean JRuby will let you have the choice and that's great.

Also I forgot: I had no issue due to the use of JRuby (trunk) with Rails2.0.
Have fun with JRuby and Rails2.0.

Raphaël Valyi.

Thursday, October 04, 2007

JRuby IT survey using Yahoo pipes

Suppose you are all convinced JRuby is the next big thing that will bring an incredible productivity to the enterprise world. What matters from now one is to track the new JRuby experiments popping all arround the world. Every day more and more people are throwing specialized Java libs into brand new web2.0 Rails applications. And there are also of course the JRuby implementation to track. Indeed great expectations, such as speed boost, optional typing, Java/Ruby tight integration, can be put into the recent compiler advances.

So what I did is a simple Yahoo pipe that will crawl the web and gather very recent stories about JRuby usage and progress. You would think you could simply use Google blog search and see the same. Well, that would be a little bit less pertinent. Indeed, contrary to Google, this pipe filters out SVN commits as well as bug reports which tend to pollute Google blog searches. By aggregating lots of sources including the JRuby core developer blogs, you are also sure you won't miss anything important.

So the pipe is there:
http://pipes.yahoo.com/pipes/pipe.info?_id=oCCxOv9w3BGq_aM_1vC6Jw



And here is the output of that pipe (truncated to the 4 last entries only) thanks to Google AJAX Feed API:

Loading



An important thing you could notice is that Technorati and Delicious aren't used. Well actually the problem with those is that they provide feeds ordered by tagging history, not by publication history. So an old story could seem recent in Delicious just because somebody just tagged it.

Anyway, Yahoo pipes looks like a terrific tool for IT survey in general. Analyzing how people build pipes might also provide good semantic information to Yahoo, just like Delicious.

Have fun and feel free to provide feedback or build better clones.

Raphaël Valyi.

Wednesday, August 22, 2007

Hacking the moo.fx Accordion into the ultimate DHTML Accordion widget

For long, I've been looking for a good Accordion widget. First I began with the Yahoo one but unfortunately I ran into serious interference troubles when using it on the same page I was using the Google Ajax search widget.

Then I came to the Moo.fx accordion and that was the good one.
Still, this Accordion has some limitations:
  1. If your page is large, your browser won't scroll properly to the selected accordion tab.
  2. It would be better if the user could click on an expanded tab to collapse it back/
  3. You might want to have a collapse/expand button on each tab.
  4. You might want your accordion to degrade gracefully for non JavaScript enabled browsers (accessibility + SEO optimization) and have links to separate pages instead of tabs in this case.
  5. You want to disable the Mootools Accordion from following those accessibility links because you just want to expand the tab instead. The default Accordion implementation really sucks to this respect.
  6. You might want a tab CSS rollover effect.
I addressed all those issues for the web site I'm building now: Livetribune.com.

Issue #1:
Use the prototype scrolling feature to scroll to the expanded tab (I'm using Moo for Prototype):

myAccordion = new fx.Accordion(myLinks, myDivs, {onActive: function(toggler, element){
setTimeout("Element.scrollTo('" + toggler.id + "');window.scrollBy(0,-150)", 400);
});


Issues #2 and #3:
hack Accordion a bit to allow collapsing back any tab. And also add/remove a class name to the toggle DOM element in order to then use different CSS styles, for instance to display a different button backgound-image when to tab is collapsed or expanded.
Here is my modified Accordion class, I simply changed the showThisHideOpen function :

showThisHideOpen: function(iToShow){
var forceHide = iToShow == this.previousClick;
if (!this.options.alwaysHide){
if (forceHide)
this.previousClick = null;
else
this.previousClick = iToShow;
var objObjs = {};
var err = false;
var madeInactive = false;
this.elements.each(function(el, i){
this.now[i] = this.now[i] || {};
if (i != iToShow || forceHide){
this.hideThis(i);
$(this.togglers[i]).className = $(this.togglers[i]).className.replace(' expanded', '');
} else if (this.options.alwaysHide){
if (el.offsetHeight == el.scrollHeight){
this.hideThis(i);
madeInactive = true;
} else if (el.offsetHeight == 0){
this.showThis(i);
} else {
err = true;
}
} else if (this.options.wait && this.timer){
this.previousClick = 'nan';
err = true;
} else if (!forceHide){
this.showThis(i);
$(this.togglers[i]).className += ' expanded';
}
objObjs[i+1] = Object.extend(this.h, Object.extend(this.o, this.w));
}.bind(this));
if (err) return;
if (!madeInactive) this.options.onActive.call(this, this.togglers[iToShow], iToShow);
this.togglers.each(function(tog, i){
if (i != iToShow || madeInactive) this.options.onBackground.call(this, tog, i);
}.bind(this));
return this.custom(objObjs);
}
}


Issue #4, #5 and #6
We can consider it all together. For good accessibility and degradability, we need our accordion tooggle elements to be standards 'a' links. In that a href element, we put a link to a page that will display approximatively the same content than in the tab, possibly with a restful URL so that's good for SEO optimization.

Also, now that we have those a with their href, we are able to make a CSS rollover using the a:hover CSS pseudo selector to translate the background image of our toggle element. Remember: IE6 only accept the :hover selector on links and moreover the href of those links have to be defined for the :hover selector to apply properly.

But the catch is that if you use links with href as Mootools Accordion tooggle elements, your browser will follow the links and refresh the window instead of collapsing/expanding your accordion tabs. Ending your onActive custom function doesn't fix this nasty behavior. The only way I found was to patch the Accordion widget and add the return false; statement in the initialize function in the onclick trigger of Accordion.js:

initialize: function(togglers, elements, options){
this.now = {};
this.elements = $A(elements);
this.togglers = $A(togglers);
this.setOptions(options);
this.extendOptions(options);
this.previousClick = 'nan';
this.togglers.each(function(tog, i){
if (tog.onclick) tog.prevClick = tog.onclick;
else tog.prevClick = function(){};
$(tog).onclick = function(){
tog.prevClick();
this.showThisHideOpen(i);
return false;
}.bind(this);
}.bind(this));
this.h = {}; this.w = {}; this.o = {};
this.elements.each(function(el, i){
this.now[i+1] = {};
//el.style.height = '0';
el.style.overflow = 'hidden';
}.bind(this));
switch(this.options.start){
case 'first-open': this.elements[0].style.height = this.elements[0].scrollHeight+'px'; break;
case 'open-first': this.showThisHideOpen(0); break;
}
},


for details: have a look to Livetribune. I'm still working on this site, so please be indulgent if you are coming in a bad time. Livetribune will be ready in October.

Hope you can now build your ultimate accordion widget!

Raphaël Valyi.

Very Good Javascript lessons from Yahoo

So that's it, the web2.0 is popping everywhere all over the place and you need to make your sites sexy using Javascript and AJAX.

Then you probably feel JavaScript is a crappy language you never wanted to learn. So you keep learning bits of it as you need to. But doing this you'll probably end up reading a lot of crappy examples too and that will keep you in the vicious circle of thinking JavaScript is only a toy language.

But what if you take some 2 hours and really learn the basic correctly from Yahoo people who really know what they are doing? What if you finally learn what is that damned prototype stuff and how you can prevent libraries to collide their namespaces? I did it yesterday and I'll never regret it. I still prefer Ruby (JRuby) or Java many times over Javascript, but know at least I'll know how to code JavaScript and will be much more interested and effective in making the most of existing frameworks.

So if you can, really watch those screencasts:
Again, I've read lot's of Javascript tutorials or even books before. But nothing approaching the clarity and effectiveness of those lessons (except may be the AJAX in Action book).

Have fun,

Raphaël

Tuesday, July 03, 2007

import your Google bookmarks into del.ico.us!

There are plenty sites explaining how you can export your del.ico.us tags, for example to Google Bookmarks, for instance: http://blog.persistent.info/2006/10/import-your-delicious-bookmarks-into.html
(or export your Del.ico.us tags using the Del.ico.us tool in 'settings' and then import them in Firefox or Google).

But what if you want the contrary Google Bookmarks -> Del.ico.us ?
First of all, why would you do that?
Well, despite of Google offering a richer indexing of your bookmarked entries, Del.ico.us has a number of advantages:
* Del.ico.us is really oriented toward massive tag sharing, so it really helps other people to find out good pages semantically.
* Del.ico.us really uses tags as a plebiscite system to rate the page quality of a page
* Del.ico.us gives nice JSON and GET API's allowing developpers to build semantic mashups based on social tagging.

so now you know why, here is the how to import from Google Bookmarks to Del.ico.us
1) login to your Google bookmark account.
2) go to that link: http://www.google.com/bookmarks/bookmarks.html
and save that html file somwhere on your disk.
3) now login to your Del.ico.us account
4) go to 'settings' on the top right of the page. Now select: 'import / upload' in the middle of the page.
5) now just select the file you saved from Google as the file to upload. Also you probably want to removed the 'imported' tag that's going to be added by default but that's up to you. Uploading might take some time; just come bac to your bookmarks after a minute...
6) finally, sadly Del.ico.us won't share those uploaded tags with other people. That's probably because they want to ensure the quality of the tagging before sharing any tag. So unfortunately, you would now have to share your tags one by one using the buttons in the tag list. Any javascript Firebug hack to automate this would be very welcome.

That's it folks! Keeping in sync Delicious and Google Bookmark in some way might be great.

Tuesday, February 13, 2007

Rails 1.2 and rb-gsl ruby math lib don't play nice together!

Hi,

so Rails 1.2 might be nice, but I got some HUGE headaches when migrating to Rails 1.2. This time, all my ActiveRecord objects were raising a "private method `equal?' called for #" exception when I was using require 'gsl' no matter were in my code. That sucks a lot because ruby is a slow language (yet powerful) so I really needed to use the C native maths (matrix stuff) functions from rb-gsl.

With Rails 1.1.6 everything was OK, but with Rails 1.2, I started to get errors like:

private method `equal?' called for #
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.1/lib/active_support/core_ext/class/inheritable_attributes.rb:127:in `inherited_without_layout'
/usr/lib/ruby/gems/1.8/gems/actionpack- 1.13.2/lib/action_controller/layout.rb:185:in `inherited_without_helper'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.2/lib/action_controller/helpers.rb:120:in `inherited_without_api'
/usr/lib/ruby/gems/1.8/gems/actionwebservice- 1.2.2/lib/action_web_service/container/action_controller_container.rb:84:in `inherited_without_action_controller'
/usr/lib/ruby/gems/1.8/gems/actionwebservice-1.2.2/lib/action_web_service/dispatcher/action_controller_dispatcher.rb:32:in `inherited'
./script/../config/../app/controllers/application.rb:6


The problem with ruby is that each framework attempts discretely to do its own magic by redefining existing methods. But frameworks end up crunching themselves this way. That's pretty much the same knd of trouble I had with gettext (see previous post)

After looking quite a while at the code, I had no idea what object was this Hash:0xb74e20fc object neither why ActiveSupport would care about it. Adding some debug logs into ActiveSupport, I saw that all GSL objects are being handled by ActiveSupport but again, I found no way to prevent such a nasty exception to occur.

At the end, I found a dirty compromise to continue using rb-gsl in my Rails app:

First it implies you patch ActiveSupport (1.4.1 in my case) to look if the method 'equal?' isn't private as it would just occur with rb-gsl:
add this method visibility check line 127 of
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.1/lib/active_support/core_ext/class/inheritable_attributes.rb

if (!inheritable_attributes.private_methods.index('equal?')) && inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)

(hint: you can use that check to log the problematic 'child' object if you want to debug Rails or rb-gsl)

Second, we can't require 'rb-gsl' inside an ActiveRecord object (neither a module included in an ActiveRecord object) anymore because even if we avoid the first illegal access to the private method, a there is a second catch that would break out later: 'can't change a frozen Hash', go understand...

So you'll have to encapsulate your rb-gsl lib inside some wrapping ruby object that will contains require 'gsl' and provide ruby methods to the gsl methods you are interrested in. And eventually your controllers or ActiveRecord object will play with that wrapper to call the gsl math functions.

Yes, overall, doing that really sucks, does anyone have a better idea? Who is the culprit? Rails or rb-gsl? If rb-gsl, how modify it to make it Rails compliant again?

NB: Paul King also noticed this bug with rb-gsl:
http://rubyforge.org/pipermail/mongrel-users/2007-January/002659.html
but he could eventually add a stub empty equal? method to its problematic object. This didn't work at all in my case though.

Friday, February 09, 2007

Rails 1.2 doesn't play nice with gettex

Hi,

After migrating to Rails 1.2, under certain conditions, I had suddenly lots of trouble to save any trivial ActiveRecord object. All was working like a charm before and with Rails 1.2, I started to get that exception:
In the console, I play with a dummy ActiveRecode model with only one sample string field:

>>foo=Foo.new
>>foo.save
>>foo.save SystemStackError?: stack level too deep
from
...
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/active_record/connection_adapters/abstract/database_statements.rb:59:in `transaction' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/active_record/transactions.rb:95:in `transaction' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/active_record/transactions.rb:121:in `transaction' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/active_record/transactions.rb:129:in `save_without_validation' from /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/active_record/validations.rb:752:in `save'


By under certain conditions, I mean that the bug was hardly consistent, it happened only after I my application gained some complexity (Foo excepted) and also using the auth_generator plugin.

Still, I think the problem is coming from gettext (1.9.0) that seems to not behave correctly with Rails 1.2. In any situation, removing gettext always removed that bug.

So in my case I hope gettext deveoppers will react while I'm polishing my app business logic, else I'll be forced to migrate to Globalize.

Hope this helps.

Raphaël.

Prototype 1.5 bug?, at least not backward compatible with auth_generator...

Hi,

I recently upgraded my web app to Rails 1.2 coming along with Prototype 1.5. Also, In that app, I'm using the auth_generator plugin, a wonderfull plugin allowing to create a user base + login system + right policy in a snap. But what makes it very special is that this Rails plugin can display users info in a javascript generated DOM fragment that will behave accordingly to the user specific cookie allowing you to cache all your pages statically, and that's really a big win considering how slow ruby is compared to hitting only Apache for static pages.

Ok, but this is not the point. The point is that auth_generator has got some simple Ajax requests and they do not work anymore with Prototype 1.5 at least with Firefox (2.0.0.1 on Ubuntu Linux Edgy)!
Here is an example of such a request:
new Ajax.Updater('accountinfo', 'http://localhost:3000/auth/logout', {asynchronous:true, evalScripts:true, onLoading:function(request){Element.show('ident_spinner')}}); return false;")
Nothing especiall as you can see (Or please tell me if I'm wrong).
But this freezes on a Prototype exception arround the line 916:

[Exception... "Component returned failure code: 0x80070057 (NS_ERROR_ILLEGAL_VALUE) [nsIXMLHttpRequest.setRequestHeader]" nsresult: "0x80070057 (NS_ERROR_ILLEGAL_VALUE)" location: "JS frame :: http://localhost:3000/javascripts/prototype.js?1170853340 :: anonymous :: line 916" data: no]


I have no idea at all what should be the correct behavior, if that's a Prototype bug, a Firefox bug or an auth_generator bug.

I have no idea of what side effect this could have, but a dummy fix for me consisted in rewritting the lines arround line 916 this way:

for (var name in headers) {
try {
this.transport.setRequestHeader(name, headers[name]);
} catch (e) {
//do nothing
}
}


Hope this helps, if you have a better fix, please go on and comment.

Raphaël

Update: OK, Prototype 1.5 really bombs out with Firefox, others railers discovered the bug and they found no other fix, see here:
http://dev.rubyonrails.org/ticket/6918

Friday, December 22, 2006

Ubuntu trouble: uninitialized constant Mongrel::HttpHandler (NameError)

Hi,

Ok so Rails is the must have framework, and Mongrel the must have server. But may be you just had an error when trying to launch Mongrel on Debian and you are googling desperately for a solution.

So, if after typing:
mongrel_rails

you get:
uninitialized constant Mongrel::HttpHandler (NameError)

The reason on Debian might be your Gem package is outdated, so try to type:
sudo gem update --system

For me this upgraded Gem from 0.8 to 0.9, than I reinstalled Mongrel with:
sudo gem install mongrel --include-dependencies
sudo gem install mongrel_cluster --include-dependencie


and voilà, Mongrel is now working!

Hope this help.

PS: Viva Lula! viva Chavez! fudeu a direita! Com mas de 60% dos votos contra direita, foi uma boa goleada contra burguesia, valeu. (translation: no putsh in Brazil neither Venezuela, South America is going fine).

Tuesday, September 26, 2006

RIGHT WING PUTSCH ATTEMPT IN BRAZIL



I'll try here to inform people that may not know the situation one week before the presidential elections in Brazil. After having been evinced from the power in 2002, those who always ruled Brazil since its colonisation are trying everything to prevent Lula from being elected again.

Opinions pools were pretty clear since several months ago: Lula was summing about 55% of the vote intentions against less than 30% for the right wing candidate. But the oligarchy -still very powerful in Brazil- tried to biased the public opinion. Journals such as Veja are lying more than Bush, while others like the "Folha de Sao Paulo" are obviously biasing the facts against Lula. The problem is that there aren't yet any mass media to balance those partial views from journals owned by the oligarchy.

Among the most scandalous stuff, are: the national lawyers association (OAB) is trying to impeach Lula; while the government was top blame, because they bought vote from opposition politicians to gain governability, the biggest problem is that the press only persecuted the government without even persecuting those corrupted politicians from the opposition who used to be corrupted for decades. Finally one week ago, a tape proving the corruption of Jose Serra -a former rival of Lula- has been discovered. Instead of investigating this case, the press is only focused on linking Lula with irregularities when obtaining that tape, again, they are trying to impeach Lula despite the total lack of proof and its huge popularity. They are thus trying to shunt the democratic process in Brazil because they fear to loose their privileges inherited from the colonnial situation.

Lula is very popular in Brazil, it's the first president who helped the poorest of the poor of Brazil. He is hopefully going to be elected any way. But still, the putsch attempt should be denounced anyway. It's our duty to blame the PSDB party and the mass media they manipulate wherever they go (that's why I'm telling it in english).

The issue is nothing funny. While the brazilian mass media (mainly Globo) are hiding the facts, they are more people killed in 3 months in Sao Paulo than during the whole second war in Lebanon! Corrupted politicians such as Jose Serra (PSDB) are widely responsible for letting that violence run out of control!

I'm only giving my view here. Others may defend the cause better (PT web site, , blogdareeleicao) , and the good results of Lula and popularity can be verified against numbers from impartial organisms. Here are such indicators:
numbers,
details of numbers
popularity pool

Friday, September 22, 2006

Sun accepted the annoying BeanInfo lookup in XMLEncoder as a bug, vote for it!

Hi,

As I described it here, XMLEncoder is giving too much pain when marshalling javabeans in unsigned applets. I've built up a workaround framework, but still, a few BeanInfo meta classes gets looked up on the server (while they don't even exist, so it's only freezing the network and loading the server). Also I can't tell if my framework really scale to any application and if there is no side effect. If there are they would be hard to debug in such a marshalling context.

We should just be able to disable that codebase lookup. Lots of problems with applets indeed come from the code base lookup, among them: problem with ResourceBundle, Services lookup, XMLEncoder, XMLDecoder (less annoying as the workarround is fully efficient). So I reported that to Sun Microsystems and the bug has been accepted. Here is the link.

So if you find it annoying and want a fast and easy marshalling in applets, if you want a faster and smaller JGraphpad CE, please vote for that bug here:
here
(you should create an account if you don't have one).

Raph.

Thursday, September 21, 2006

Applet revival: JGraphpad CE BLACKJAX applet size cut from 450K down to 110K thanks to Pack200!

Pack200 is an underestimated widely deployed java tool to speed up dramatically the download time of applets or webstart applications.

I was already aware of an other 'BLACKJAX' like applet deploying Apache Derby on the client. Then I heard its creator claimed he cut Derby from 2Mb down to 600kb using Pack200..

So Pack200 is a class compressing/deflating tool deployed along with the JRE since version 1.5 (Tiger). How it works: in short applets and webstart apps are still requesting a jar archive on the server. But on the server side, you catch the fact the a different encoding (pack200-gzip) is requested by java 1.5+ clients. When requested, you'll then return the "packed" jar version, else (JRE prior to 1.5) you'll return the normal jar archive. JRE 1.5+ webstart and applet clients will deflate the packed archive on the fly saving a huge amount of server load and bandwidth.

Of course, this transfer win is only worth the CPU overhead on the client side if your archive was large enough. Make your benchmark.

Results are here:
So what about JGraphpad Community Edition? Without plugins (but with JGraph) - that's configured as the minimal online diagram editor - JGraphpad CE is weighting about 450K. Pack200 cut down the archive to 110K which is a very big win! Frankly I didn't benchmarked the extra overhead against the transfer win, but overall the win is HUGE! Overall, the applet startups 3 times faster!

So, yes it means that JGraphpad CE which is by far a more complex app than TinyMCE or FCKEditor which are 'only' online word processors, is still something 2 times smaller to download!!! (And with our "Javascript sugar", it starts up just as fast). Really amazing.

Now consider that JGraphpad CE is architectured so that you can download some plugins in the background (until you really need them) while diagramming with the core JGraph application, it turns JGraphpad CE really credible for diagram online edition.

Limits: JGraphpad CE require java 1.4.2 at least to run. But with java 1.4.2 you'll have to download the classic jar (it's totally transparent however). Only happy java 1.5 and 1.6 users will get their first download time boosted thanks to the Pack200 compression.

Server Side details:

JGraphpad CE has been designed to be server side agnostic. It only two use standard GET and POST HTTP methods to download/upload a diagram from the server. So you can use what you want to handle your uploads. I personally use Python within the MoinMoin wiki (normal file upload feature), but you could use Java, PHP or Ruby, no problem.

Still getting the Pack200 trick properly configured is server side dependent. It's easy to find out how to do with a Java server.

For an Apache server, the trick is really easy, it's explained in that excellent article.

Finally, if you can't o don't want to bother with Pack200, just put the jar in the root of the applet codebase directory as usual and this will work.

I'll soon provide a bunch of official documentation about that new JGraphpad CE release. Don't hesitate to send your feedback.

Friday, September 15, 2006

AJAX + Applet = ? ... => BLACKJAX !

OK, apparently I'm not alone thinking Ajax apps and applets can interact together to provide richer Internet applications, see this other blog also. Indeed:
  • Ajax is better for fast responsive GUI and document centric applications.
  • Applets on the other side provide better graphics, DB connectivity, XML processing or any heavy and maintainable business logic.
Okay, but now we need to name this new way of bridging those technologies. I propose to coin:
"BLACKJAX" standing for:
Background Liveconnect Applet Code Kicking Javascript and XML.

That's indeed a mix between AJAX and Applet. 'Asynchronous' is better replaced by 'background' which means the same thing but makes a funnier acronyme.

A new tech is born, let's hype arround it!
I'm waiting for your comments. Cheers,

Raph.

Tuesday, August 15, 2006

degraded AJAX app + pre-loaded hidden java swing applet, the best deployment option for complex rich clients?

1) When AJAX isn't enough to address complex visualization or complex UI (or: there is no Java2D in javascript)

The trend is definitely to port traditional desktop applications to the web using AJAX. To overcome browser incompatibilities and javascript language flaws, we are using lots of frameworks including the Google Web Toolkit for instance. Those are fine but will only work well within a limited range of use cases as I will explain.

There is a use case indeed where AJAX+frameworks aren't enough: the case where you need superior visualization features like diagram editing or diagram driven modelling for instance (think about the equivalent of the Visio desktop app to model UML online for instance).

Ultimately, the point is that javascript in browsers doesn't allow you to dynamically draw a bunch of lines without consuming too much memory. Yes there are some attempts out there:
but as you can see by yourself, those trick doesn't scale to dozens of animated lines, no way.


2) SVG applications fall into the same deployment trap as java: not everybody has got the required runtime platform

Then what? Oh yes, there are those guys that will tell you to use SVG. SVG is great indeed, but you know what, MS Internet explorer requires an external plugin (Adobe) to display SVG, so basically, your shiny SVG/AJAX app falls into exactly the same deployment trap than java: not everybody can run it because not everybody already has got the required runtime platform.


3) SVG+VML turns into a maintenance nightmare


Then what? There are those who will tell you: OK let's writte an AJAX app the will use SVG on Moz browsers and VML on Internet Explorer. Fisrt of all, unlike SVG, VML is an end of life product that's not that much powerful. And moreover, your AJAX/SVG/VML app has already turned one of those maintenance nightmares!


4) Then a java client is the best, even in 2006. But which one, webstart or applet?


So taking all this into account, the best target runtime for large deployment of complex visualization application is... JAVA. A good old one jre 1.4.2+ is already well deployed indeed and programming java is easy and best of all, java is a language able to handle the complexity.

Java? OK, but do you mean applets or webstart? Well actually I mean applets. While webstart is younger and got a better security sandbox, webstart won't allow you to integrate you application in the browser just like applets. And using all the DHTML browser facilities is very handy, especially in document centric applications like CMS or publishing tools.

Yes but aren't applet slow at startup? Well, yes they are. And you know what, that's almost normal considering all you can do with the java runtime (much more than Flash for instance). Also don't forget that applets are cached after the first use, so even if your huge applet first takes time to download just like any other web app, then only startup time matters.


5) Don't even wait for the applet startup, instantly popup a degraded AJAX app to handle the first user interactions!

OK, Javascript apps start a way faster than applets. So why not instantly popup a simple degarded javascript app to deal with the first user interactions while the heavy business logic is being loaded in a powerful hidden applet? After the applet is loaded, we swap the two and the user now plays in a full featured desktop Swing application...


6) Hidden Applet and degraded AJAX howto

I've read that some people advice to set the applet size to 1*1 pixel to hide it and then makes it larger to display it. However, unfortunately, it seems that this hack makes the resize quite slow in Firefox, giving the feeling that the app is unresponsive.

I've found a better way, style your Ajax Div container and Applet div container this way (using CSS) :
div.visibleAjaxDiv {height: none; overflow:hidden}
div.hiddenAjaxDiv {height: 1px; overflow:hidden}
div.visibleAppletDiv {height: none; overflow:hidden; visibility:visible}
div.hiddenAppletDiv {height:1px; overflow:hidden; visibility:hidden}

then switch the Ajax and Applet div class when appropriate (for instance when the user request a complex interaction in your Ajax app, after you checked that the applet has been fully loaded indeed).


7) Liveconnect as the glue between the AJAX app and the applet


What you want is that the applet will remember what the user first told you in the AJAX app (for instance the name of the diagram to be created) and you also want the AJAX app to remember what the applet told (for instance you hide again the applet after editing your diagram, but then you want the AJAX app to display the picture of the diagram when it has been uploaded...


8) Your applet needs to communicate with the server. XMLEncoder and XMLDecoder are a free lightweight persistence framework for your applet, but it requires some tuning to be used in an unsigned applet.

Hardcoding business object encoders and decoders is the best way to write unmaintainable and unnecessary code. Fortunately, java allows you to persist and read javabeans at no cost.

However, the DefaultPersistenceDelegate and Metadata java code suffer some flaws making them hard to use inside an unsigned applet (security exceptions and heavy network use), but fortunately, I found a workaround for both XMLEncoder and XMLDecoder, see those posts:


9) Conclusion


Yes this way of associating technologies seems a bit tricky and too novel for production use. However, I think this is the best available technology for complex visualization rich clients. Also, I don't consider it's more hacking than traditional AJAX frameworks. No it's not, most of this technology is traditional java programming, one CSS style sheet, two java classes to reuse and only a bit of javascript but less than a full blown AJAX app. Also, if several people start to deploy this way, we could build a framework and spread best practices. I think this is better than waiting forever that a better client runtime is widely deployed... All this is available now. Any comments will be very much appreciated.


10) An example?

Yes, I've got an example. Unfortunately, the degraded AJAX app is reduced to its simpler: only a clickable image launching the app, with a tooltip, image reloading and file save checking, but the Swing app is shiny and based on JGraph (the GPL version of JGraphpad). And it's not hard to follow that proof of concept and build a larger app following those guidelines.
see the example here: visualmodeller

Monday, July 17, 2006

hacking XMLDecoder for unsigned and network friendly applets

Want to avoid that at applet startup?

network: Connecting http://yourHost.com/META-INF/services/javax.xml.parsers.SAXParserFactory with proxy=DIRECT
network: Connecting http://yourHost.com//META-INF/services/javax.xml.parsers.SAXParserFactory
network: Connecting http://yourHost.com/META-INF/services/com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration with proxy=DIRECT
network: Connecting http://yourHost.com//META-INF/services/com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration

When used in an usigned applet (yes there are complex hacks if you are willing to pay for a certificate), XMLDecoder also use to open unecessary sockets to lookup the applet code base on the server. The trouble isn't so important as only something like 4 sockets will be opened. But depending on your server load, this can easily result in a 1 second latence when opening the applet. Indeed, it appears that code base lookup when loading classes is a process that completely prevent others threads from running, be it in a simple other Thread or even in the SwingWorker thread.

Also, since this latence occurs at the applet startup is your applet is to open a server side XML file, then the latence is finally really annoying and make applets look sluggish compared fast and responsive ajax applications.

Fortunately, I found a way to avoid those 4 sockets. Instead of using XMLDecoder, use the following derivative:

(Also, to decode encode side XML files in an applet using an usigned applet, we use AppletFriendlyXmlEncoder, an applet friendly derivative of XMLEncoder. )

The full source of AppletFriendlyDecoder which extends XMLDecoder is to be found on the Visualmodeller wiki here:
http://www.visualmodeller.org/wiki.fcgi/AppletFriendlyXmlDecoder

A demo of it can be found in JGraphpad Community Edition demowed online at Visualmodeller here:
http://www.visualmodeller.org

Thursday, June 15, 2006

hacking XMLEncoder for unsigned and network friendly applets

XMLEncoder is very handy to serialize javabeans without the burden of hard coding encoders and decoders. Unfortunately, the current Sun implementation doesn't work properly with unsigned applet (there are hacks if you are willing to pay for an applet certificate).

Especially using XMLEncoder in an unsigned applet is very likely to result in security exceptions and will run slow and without any background threading solution.

Indeed, the DefaultPersistenceDelegate used by XMLEncoder for common javabeans uses to kill the network by opening dozens of sockets because it looks up for beanInfo classes on the server code base for every type inside your bean.

Fortunately, I worked hard and dicovered a hack: use the following encoder instead of XMLEncoder (you may adapt it a bit to your own beans in order to avoid some more specific beanInfo lookups). It's currently used in the last JGraphpad Community Edition online diagram editor.

Also, to decode server side XML files in an applet using an usigned applet, we use AppletFriendlyXmlDecoder, an applet friendly derivative of XMLDecoder.

The full source of AppletFriendlyEncoder which extends XMLEncoder is to be found on the Visualmodeller wiki here:
http://wiki.visualmodeller.org/AppletFriendlyXmlEncoder

A demo of it can be found in JGraphpad Community Edition demowed online at Visualmodeller here:
http://wiki.visualmodeller.org