Ruby on Rails
Why Ruby is More Than Fast Enough (and a very brief tutorial)
First a message to that one guy who added this blog to his RSS feeds: don’t worry it isn’t dead already, it’s owner was just away on some stressful vacation, had visitors, started on a new job and was celebrating his 30th birthday!
Then a message to whomever might read it: the next statement might seem to blatantly contradict the title of this post.
Ruby is really, really slow. While the benchmarks from the Computer Language Shootout might be misleading or biased or whatever, Ruby is so slow that from time to time you really don’t need a benchmark to notice it.
Solving Sudoku
With me on my vacation I had Donald E. Knuth’s paper about the Dancing Links algorithm. I wanted to use it to create my own little Sudoku generator in Ruby. Perhaps a geeky vacation activity, but if its fun solving a Sudoku, why shouldn’t it be fun solving Sudoku?
Knuth’s algorithm is really extremely beautiful. Implementing it gave me a bit of the same feeling as studying a Bach Fugue at the piano. What an amazing ability of abstract imagination Knuth must be in possession of!
Implementing the algorithm in Ruby didn’t make it any less beautiful, but it did make it seriously slow. The algorithm needs an initial matrix of circularly linked lists to be setup, before it can work its magic. To solve Sudoku this involves setting up a matrix of 3.241 linked Nodes. While 3.341 is generally a really small number for a computer, it’s a pretty big number for Ruby, and on my good, old, trusty iBook G4, setting up this matrix in Ruby takes time. Perhaps not a huge amount of time in the grand perspective of things, but enough time that a web-based Sudoku generator running on Ruby on Rails would feel slow.
Of course I could just try to hide the slow nature of the generator by some Ajax trickery, or I could generate a huge batch of Sudokus overnight and feed them to a database, but the first solution is a shoddy excuse for not just creating something that’s fast enough from the outset, and the other option was just not what I wanted for my own little online Sudoku generator.
More to Ruby than Ruby
So why am I still claiming in the title of this post, that Ruby is more than fast enough?
Because all this led me to discover how insanely easy it is to extend Ruby with C. And while C might lack objects, classes and all the niceties of a dynamic OOP language like Ruby, it sure has speed.
Programming Ruby: The Pragmatic Programmers Guide provides an excellent guide to extending Ruby in C, and with the help of this guide and the README.EXT file in the Ruby source directory, I was able to quickly create a Sudoku extension for Ruby.
I started out by creating a quick implementation of the Dancing Links algorithm in C. Since I had already implemented the algorithm once in Ruby, it was quite easy getting a command-line Sudoku solver up and running in C – this one just as blazingly fast as the Ruby version was slow. Now it was just a matter of including ruby.h, setting up a few interface functions, letting ruby generate a makefile for me and running “make” – and bam! – I had my Sudoku extension.
A Tiny Tutorial
Just to give you an idea how easy this whole process is, here’s a small tutorial on creating an incredibly useful “Hello World” extension to ruby:
First we make our “Hello world” C program in an empty folder:
hello_world.h:#include <stdio.h>hello_world.c:
void say_hello();
#include "hello_world.h"
void say_hello() {
printf("Hello, World!\n");
}
int main() {
say_hello();
}
Compile and run to check that your new fantastic hello world program works.
Time to make it a Ruby extension. Remove the int main() function from hello_world.c and lets start interfacing. Here are the c files:
hw_extension.h:#include "ruby.h"hw_extension.c:
#include "hello_world.h"
#include "hw_extension.h"
VALUE cHello;
static VALUE h_init(VALUE self) {
return self;
}
static VALUE h_say_hello(VALUE self) {
say_hello();
return Qnil;
}
void Init_HelloWorld() {
cHello = rb_define_class("HelloWorld",rb_cObject);
rb_define_method(cHello, "initialize", h_init, 0);
rb_define_method(cHello, "say_hello", h_say_hello, 0);
}
The Init_HelloWorld() function is needed to define our new Ruby classes and methods. Our class is defined as “HelloWorld”, HelloWorld.initialize is set to the h_init function and HelloWorld.say_hello is set to our h_say_hello method.
Now we need to tell Ruby to create a makefile for us. For that purpose we make a config file:
extconf.rb:require "mkmf"
dir_config("HelloWorld")
create_makefile("HelloWorld")
Now run “ruby extconf.rb” and Ruby will search the directory for .c files and add them to the make files instructions. When that’s done all there’s left to do is type “make” at the command line and we’re good to go.
Fire up irb and lets test our new extension:
> require "HelloWorld"
=> true
> h = HelloWorld.new
=> #<HelloWorld:0x1bafbc>
> h.say_hello
Hello, World!
=> nil
Y-ay! We now have a lighting fast hello world extension and all your worries about your hello world programs running way to slowly in Ruby should be a thing of the past! At least the excellent extendibility of Ruby made my worries about the speed of my Sudoku generator go away.
I put up the current Rails driven version of my Sudoku generator at sudoku.mathias-biilmann.net It currently generates very hardcore Sudokus with few hints and no testing to see if they are solvable without backtracking. But it works, it’s fast and I hope to get time to improve upon it during the months to come.
[Update – The Sudoko game is now far more enjoyable. It generates fairly easy aesthetically pleasing Sudokus, and let you challenge a friend to beat your time at any Sudoku you manage to solve.]
RDoc widget
Stumbled across this wonderful RDoc Dashboard widget today.
With this widget all of the Rails and Ruby APIs is just a tap on F8 away. Incredibly nice for an API addict as me!
The widget really suffers from the lack of one feature, though: You cannot copy and paste anything from the RDoc documents.
I fiddled a bit with the source and figured out how to fix that. Here is the recipe:
Browse to the widget in the ~/Library/Widgets folder and select ‘Show Package Contents’ from the context menu.
Open the file RDoc.css in your favorite text-editor.
Under #docoDiv uncomment the line:
-apple-dashboard-region: dashboard-region(control rectangle);Add the line:
-khtml-user-select:text;
Save the file, and enjoy copying and pasting from the APIs!
Ruby on Rails and the X in front of HTML
Ruby on Rails seems to do its best to push developers towards XHTML. Every generated scaffold is marked up XHTML-style and all the useful helper-functions produce valid XHTML. But in spite of this, I think it is worth thinking twice about which doctype to use.
It is still not a very well known fact, but Internet Explorer has no real support for XHTML. It will happily display pages with XHTML doctypes, as long as they are fairly compatible with HTML, but it will use its ordinary rendering engine and just think about XHTML as a slightly weird form of HTML.
Firefox , Opera and Safari will think the same about the XHTML documents sent from a standard Rails application, but in contrast to IE these two browser actually have real support for XHTML documents, you just need to tell them to use it. The way you tell them that, is by setting the content-type in the HTTP-headers to 'application/xhtml+xml'. Do this, and suddenly XTHML supporting browsers will no longer think about your website as a big soup of tags, but as XML that better be well-formed and computer-readable.
XHTML Problems
As long as you keep serving XHTML as ‘text/xhtml’, it easily seems like the only difference between good old HTML and the doctypes with a X in front, is just the required lowercase and the closing of all tags. But start pushing XHTML supporting browser to use their XML rendering engines, and the differences will suddenly become far more visible.
Roger Johansson has written about the differences here
One important difference, that will be a showstopper for some, is that those wonderful javascript libraries which we Rails developers have come to love and trust, won’t work without modifications, when content is actually served as XHTML.
It shouldn’t come as a surprise, but in the XHTML DOM, there is no such thing as .innerHTML – an attribute that prototype.js depends heavily on.
And a solution
Of course you can always go on serving XHTML as text/html, but it seems backwards to specify one doctype in your documents, and ask the browsers to use the render for another doctype in your HTTP-headers.
This is the reason that this site is done in HTML 4.01, but this brings me back to Ruby on Rails helper functions. All these generate valid XHTML, but sometimes that means invalid HTML. I browsed the Rails source for a while, and found a nice solution for that. In your application helper add this:
def tag(name, options = nil, open = true)
"<#{name}#{tag_options(options.stringify_keys) if options}" + (open ? ">" : " />")
end
This overrides the tag function, and for me these 3 lines have been enough to solve all HTML 4.01 validation problems with Rails.
[Update – I’ve since changed the site from HTML to Transitional XHTML after all, in order to play around with MicroFormats a bit]
