Wild Man's Shore

Wednesday, January 18, 2006

TurboGears vs Rails

I first glanced at Ruby a few years ago, round about the time I first glanced at Python. The two were, at least superficically, similiar. As I was more heavily into web development at the time Python looked the better option because of Zope, WebWare and other packages; Ruby then (as far as I know) just had plain CGI.

Now Ruby has stormed the web development world with the Rails platform, leaving Pythonistas dazed and confused. Nevertheless we rallied round two excellent packages, TurboGears and Django, while the Zopeheads worked on their interesting but rather esoteric Zope 3. And Python is not short of web development platforms; I wrote a large document management application using Quixote + Durus, about which more later. But Ruby, once an obscure Japanese cousin to Python, is now the darling of the tech media.

Rails has been around now for about 15 months, 3 times longer than TurboGears (though not TG's individual components) and so I decided that as a more stable platform it would be worth a look. Plus, and this is something I forgot to mention in my last post about Java, you should always look for possibilities to beef up your resume with buzzwords. Even if I just wrote a little wiki for handling work projects in Rails I could put that on my CV. Such is the benefit of hype.

Others have written on the differences between Ruby and Python; I'll not add to that here, except that it was in a way harder to switch between Ruby and Python than between Python and Java, because the small differences in syntax keep catching you out. Anyway, on to Rails.

The immediate first impression is that TurboGears is more verbose(or explicit, if you like) than Rails. For example:

RoR:

class NewsController
@articles = Article.find_all end

end


TG:

import cherrypy
import turbogears
from turbogears import controllers
import model

class News(controllers.Controller):

@turbogears.expose(template="news.templates.index")
def index(self, **kw):
return dict(articles = model.Article.select())


One thing to note is how you have to tell TG what methods to expose to the web, which template to use, and what data goes in the template. In RoR this is decided for you; every method by default is exposed, any data in the method goes in the template, and the name of the template is the same as that of the method (in this case, index.rhtml). You can override these, but a lot of assumptions are made on your behalf.

Let's look at how each system handles the model:

RoR:

class Article < ActiveRecord::Base
end

TG:

class Article(SQLObject):

title = StringCol(length=200)
maintext = StringCol()
created = DateTimeCol(default = datetime.now)
category=ForeignKey("Category")
author = ForeignKey("User")


Again RoR's ActiveRecord works implicitly: it looks for a corresponding database table and maps rows to Article objects. With SQLObject you define the columns in Python code and then the object creates the corresponding database table as needed (in this case, Article.createTable()). You can do things the RoR way in SQLObject, but in general this is only done with legacy databases.

This is the basic difference in philosophy between the two platforms. The Pythonic way is "explicit over implicit". Everything is out for show: you know what modules are imported, you know what methods are exposed, you know what columns are defined and so on. It may take more keystrokes but the extra code let's you know what is happening when things go wrong. The Ruby way (or at least the Rails way) is the opposite: take the burden off the developer, don't bother them with the petty details that get in the way and add to the line noise.

RoR does make for more readable code. However, there is a caveat: when things go wrong, or when you want to do something different, it's a lot more work to fix the problem than if it's always obvious what's going on. In that way RoR is the anti-J2EE; it assumes everything for you. I can see the advantages, but I also find it annoying and presumptious: I want to decide upfront what methods to expose and how my data model should look. In fact, Zope has inspired such hatred amoing Pythonistas for just that reason: things like acquisition doing mysterious business behind the scenes that cannot be explained, which is lovely when it works but a source of immense frustration when it doesn't.

Finally, one should also consider features available in TG that are not in RoR and vice versa. TG has the Toolbox, an over the web admin application that comes with a set of goodies like a relational model designer and data browser, localization tool, and so on. More importantly, it has good internationalization support, including localized date formatting and the like. This is really, really important if you work in Europe: in Finland,for example, I often have to develop sites that are available in Finnish and Swedish (Swedish is Finland's second official language) as well as for example English, Estonian and Russian. I'm not sure how well RoR supports internationalization.

Both have form widgets, though in different ways: RoR just uses simple functions in templates, whereas in TG you create form and widget objects and insert them into your templates. RoR uses ASP or PHP style text-based templating, TG uses Kid, an XML-based template engine that ensures well-formed HTML or X(HT)ML. RoR has scaffolds, TG has DataControllers. Which features you need and which is the better way of doing things is again down to requirements and preference.

In summary, both are well-designed and fun to work with. Both kick Java's fat corporate ass. They have different approaches to the same problem, but that's OK:I like the Pythonic way of TurboGears but I can see that others prefer RoR's magical implicitness. RoR has better marketing which makes it easier to persuade your boss to let you use it. It is also more stable, being in existence 3 times longer than TG, but TG makes use of well-designed, mature components like CherryPy and SQLObject. Python has more libraries than Ruby, Ruby has nice features for closures and the like. As always, YMMV.

My next comparison will be TurboGears vs the various Python web development frameworks.

12 Comments:

At 2:32 PM, Blogger Mic said...

Resume based development... hehehehe... thats funny cause its true...

RoR is amazingly easy to pick up, so you can slap it on your resume with confidence. hehehehe.

 
At 2:36 PM, Blogger Miles said...

Rails' emphasis on convention only seems magical for a very short period. Once you've used it for a week or two it becomes very intuitive and no more difficult to debug or understand than a more implicit approach would be. It's also more change-resistant, since you don't have to duplicate information in several different places the way you do in most Java approaches and, apparently, TurboGears.

You should really try to write a small application or two in both frameworks before you evaluate them. You'll come to the wrong conlusions from a superficial examination.

 
At 3:48 PM, Blogger Eric Hudon said...

Great post showing both strenghts and weaknesses of each other. It always depends on you needs. I like TG because of its explicit mapping and I like RoR because of its implicit magic.

Learn both, use the right tool for the right job.

Customer will be happy to have alternatives.

 
At 10:58 PM, Blogger Dan Jacob said...

Miles, I have been playing around with RoR tutorials and I agree, it is very intuitive. However, I would disagree that TurboGears forces you to duplicate information; it just forces you to be more explicit in the way you do things. That's just a difference in philosophy. Also, TG feels easier for me because I have much more Python development experience than Ruby, so I'm comfortable with the Pythonic way of doing things.

I think most Java developers look at RoR and say "wow, I can replace my 6000 lines of Java and 2000 lines of XML with 30 lines of Ruby". Python developers, on the other hand, are used to such productivity and conciseness and so are more critical .

 
At 3:38 AM, Blogger Dan McCormack said...

"However, I would disagree that TurboGears forces you to duplicate information; it just forces you to be more explicit in the way you do things."

I know nothing about TG in general, but in the example you gave above, you've duplicated your table schema both in the database itself and in the model class. If you add or change a column, you have to make this change in both places. That's certainly better than having to go through the entire project updating code, but it's still unnecessary redundancy.

 
At 5:10 AM, Blogger Dan Jacob said...

Using SQLObject I rarely write any SQL when (re)defining tables; I define the data model in a Python class and then call createTable() to create a corresponding table in the RDMBS and other methods such as addColumn() to modify the table. SQLObject takes care of data definition differences in MySQL, PostgreSQL and other databases. In contrast, I find having to write all my table definitions in SQL a real pain when working with ActiveRecord.

 
At 8:38 PM, Blogger David said...

Dan: Railers are not writing SQL by hand any more. They're using migrations. Which also works when you have data you care about. And when multiple developers work on the same application with their own database instance. And when your production database needs updating as well as your development database. See http://media.rubyonrails.org/video/migrations.mov for more.

 
At 4:03 PM, Blogger Rick Copeland said...

David: I think Dan's point is that, in the original post, the Rails model is given implicitly, while TG is given explicitly. What's not present is the SQL that was required to create the tables for Rails to inspect.

Dan: I also develop in TurboGears and have found no need to write any SQL. The DDL schema, in fact, can be generated quite simply from TG using "tg-admin sql sql" at the command line, while the database itself can be created using "tg-admin sql create". So there's really no duplication unless you force it. And, of course, as the original post said, you can do things "the Rails way" in TurboGears for legacy databases. This would (I believe) correspond to Railers migrating legacy databases.

 
At 6:01 AM, Blogger T.G. said...

sorry but for the sqlobject part you're wrong.
(from the sqlobject documentation.)

class Person(SQLObject):
_fromDatabase = True

(ok, your post is a bit old but ...)

 
At 8:51 AM, Blogger masukomi said...

just a note that there are a number of localization plugins for Rails that make it easy to handle anything from simple localization of the pages all the way to localization of every record in the database.

 
At 4:29 PM, Blogger kreiggers said...

The example of the Ruby on Rails controller is incorrect. That controller does not expose any methods to the web -- you still would have to write an index method just as you did with TurboGears:

def index
@articles = Article.find_all
end

 
At 7:57 PM, Blogger rodrigo said...

I didn't tried TurboGears, but from what I've read, I would prefer the Rails philosofy of Convention over Configuration.

But the Rails major drawback is localization! Mainly because of lack of good support for localization and utf8 in Ruby. It is a pain to try to localize an application in Rails, as using ',' for decimal separating, for instance. I think that we should define our locale once in a configuration file and do nothing else. At most installing a plugin. But no! After all plugins I've searched for Rails such as Globalize, GLoc, simple localization and so on, none of them offered such a simple solution for using my brazilian locale and for translating the Rails error messages with accents... It is really a pain! I think it must be much simpler in TurboGears.

But, if localization was not a requirement to me, I would have no doubt on choosing Rails instead of TurboGears...

 

Post a Comment

<< Home