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