Skip to content

Introducing Wt::Ruby, a Qt-like api for developing web applications

Friday, 16 January 2009  |  richard dale

Before going to last years Akademy I had planned to use the week to try and start helping out with Ruby support for KDevelop4. In the end I got sidetracked by two things; playing with the Nokia N810 and finding out about a web application development library called 'Wt'. Koen Deforche gave a talk about Wt and I was impressed the way he described how web development usually sucked, and why a widget based desktop style api was better than the usual web page with embedded code approach.

After the talk, Pau Garcia i Quiles introduced me to Koen and Wim Dumon who are the two main Wt developers. Pau has used QtRuby and Korundum a lot and so I know he likes Ruby, and yet he thinks the best web toolkit is Wt, which usually involves coding in C++. Hmm, WTF - there must be something interesting with this stuff I thought?

They helped my get Wt built using cmake and linking against the boost libs, and I had a go and running the Wt headers through the kalyptus bindings generator. When I found out that Wt uses boost instead of the Qt core classes I was a bit concerned as basically boost is a complete nightmare for producing language bindings against. It has load of compile time generated C++, and pushes the limits of 'C++'ness, which is a big turn off for me compared with the Qt style of api that I found translates very nicely to non-C++ languages.

I think I got the code generation for a 'Smoke' libary for Wt fixed by the end of Akademy. It turned out that although Wt uses boost, mainly for signals and slots, it fortunately doesn't have a very boost-like api itself. Instead, it is modelled on the Qt4 api with very similar classnames, just with 'W's instead of 'Q's in them. When someone asked Koen at his talk why Wt didn't use the QtCore classes instead of boost, he said it was for licensing reasons, rather than technical ones. Wt is dual licensed GPL/commercial and the commercial version sells for about 500 euros I believe. So the extra cost of a Qt C++ license for the customers would just be too much. Maybe the recent Qt license change to add LGPL will mean that we will see more 'niche products' like Wt built on top of Qt.

A week or so after I got back to Gran Canaria my big toe started hurting. It felt like I had bashed it on something, but I couldn't remember where I'd done that. It got more and more painful and after another week I couldn't even walk and had to go and see a doctor. About the second question the doctor asked was 'Do you drink a lot of beer?', like she was telepathic or something. Maybe it was my english accent, I don't know. The problem she diagnosed was that I had gout in my big toe possibly caused by beer drinking. So how an earth do I get some illness, which I thought had gone out of fashion in the 18th century? I thought that to have 'la gota' properly I should really be wearing a three cornered hat and a curly wig. So I ended up stuck in my flat with a seriously painful toe, a bunch of pills to take and no alcohol for a month. I didn't feel like listening to music or reading a book with my toe hurting like hell. Nothing to do but eat 'gout friendly' stuff, which seems to be just bread, cheese and water. What to do? The solution was go into a hacking frenzy of course, and get the Wt::Ruby hello world working after a week of doing absolutely nothing but either hacking or sleeping. Whenever, I woke up in the night in so much pain I couldn't sleep, I'd just get up and start coding.

After this initial burst of activity I've been working on Wt::Ruby on and off, gradually solving the problems of dealing with boost slots, translating the examples to Ruby and so on. But the trouble with language bindings is that they aren't much use until they are pretty much done, and so there isn't a lot of point in telling anyone much about them. Finally, I think the project is pretty much ready for a first release. The code is hosted on 'gouthub' :-). Here is what hello world in Wt::Ruby looks like:

#!/usr/bin/ruby
require 'wt'

#
# A simple hello world application class which demonstrates how to react
# to events, read input, and give feed-back.
#
class HelloApplication < Wt::WApplication

  #
  # The env argument contains information about the new session, and
  # the initial request. It must be passed to the WApplication
  # constructor so it is typically also an argument for your custom
  # application constructor.
  #
  def initialize(env)
    super(env)
    setTitle("Hello world")                                # application title

    root.addWidget(Wt::WText.new("Your name, please ? "))  # show some text
    @nameEdit = Wt::WLineEdit.new(root) do |e|             # allow text input
      e.setFocus                                           # give focus
    end

    button = Wt::WPushButton.new("Greet me.", root) do |b| # create a button
      b.setMargin(Wt::WLength.new(5), Wt::Left)            # add 5 pixels margin 
    end

    root.addWidget(Wt::WBreak.new)                         # insert a line break
    @greeting = Wt::WText.new(root)                        # empty text

    # Connect signals with slots
    button.clicked.connect(SLOT(self, :greet))
    @nameEdit.enterPressed.connect(SLOT(self, :greet))
  end

  def greet
    # Update the text, using text input into the @nameEdit field.
    @greeting.text = "Hello there, " + @nameEdit.text
  end
end

=begin
 Your main method may set up some shared resources, but should then
 start the server application (FastCGI or httpd) that starts listening
 for requests, and handles all of the application life cycles.

 The block passed to WRun specifies the code that will instantiate
 new application objects. That block is executed when a new user surfs
 to the Wt application, and after the library has negotiated browser
 support. The block should return a newly instantiated application
 object.
=end
Wt::WRun(ARGV) do |env|
  # You could read information from the environment to decide whether
  # the user has permission to start a new application
  HelloApplication.new(env)
end

You can run this application from the command line when developing it, and then you can install exactly the same source in an Apache2/Fastcgi based site for the released version.

$ ./hello.rb -- --docroot `pwd` --http-address localhost --http-port 4000

If you are familiar with Qt and especially QtRuby, it should be pretty much self explanatory. A slightly different way to create a new application, a slightly different way to connect signals and slots, but all very comfortable and familiar really. And yet this is writing a web application. In Rails something as simple wouldn't take long, but after you've generated your project you will have an auto-generated mountain of different files. This Wt::Ruby app is just a single very simple source. Another problem with Rails is that it is only really good for a certain sort of web page based app, and once you try and do something heavy with JavaScript widgets like Google's gmail it doesn't really work. With Rails you can add JavaScript/AJAX-y stuff to your basic .html.erb page like seasoning, but you can't build desktop-like JavaScript apps with it.

Now the basics are there, I think there are a whole lot of things we can do fairly easily to combine Wt::Ruby with Rails stuff and create a pretty nice RAD web development environment. For instance, I've just tried out creating a Wt::WStandardItemModel model from ActiveRecord and showing it in a Wt::Ext::TableView and that works beautifully.

If you are going to FOSDEM this year, I'm hoping to give a short 25 minute talk in the Ruby track on Sunday, but it hasn't been confirmed yet. In the meantime, if you want to checkout the code and try it out that would be great. I would really like to hear from someone other than myself who has got it all working OK before the first actual release.