evan_tech

Previous Entry Share Next Entry
02:20 am, 5 Oct 05

ruby on rails

Ok, you've all heard of Ruby on Rails now. And, if you're like me, you probably have already relegated it to the [x] 2.0 mental back burner.

But I've been using Ruby for a long time, and I worked on webapps for quite a while for LiveJournal, so maybe I can cut through the hype for you. (evan_tech started in 2003 and my earliest posts mention anticipation of Ruby 1.8, so it was already old hat for me by that point.)

First, let's start with the hype, and where it comes from.

My roommate, who is a designer, recently asked me what language I was programming in. When I answered Ruby, he responded with, "Ruby on Rails?" It turns out he didn't know that the two were separate things, or whether they were dialects of the same language, or what. It turns out that this is the experience of a lot of people -- Ruby is still relatively obscure, and even programmers think it's just some mishmash of Perl and Python. So for many people who are writing about Rails, using it is their first time using Ruby at all.

I play with and write about a lot a lot of different programming languages here, and I'm happy to say that even this many years later I still regularly return to Ruby. It's not without its flaws, but for whatever reason* Matz managed to just nail a certain design space and aesthetic and make the language really work, really flow. The people who write about the language talk about how it's optimized for fun, and that really rings true for me.

So I believe a good portion of the Rails hype is simply people discovering Ruby, and seeing this for themselves: "Oh, they way they use blocks, they're wonderful!" / "Such elegance where they do [x], [y], [z]!" This is fine, but it's not specific to Rails.

It also happened, for whatever reason*, that Rails itself (which, in case you're in the position of my roommate, is a framework of libraries and utilities for writing web applications in Ruby) is just plain really well done. It seems clear to me that it was made by someone who really knows Ruby's strengths and someone who has written a lot of web apps and simply has some good design. You'll note that the above criteria (aside from the Ruby strengths) are not really language specific, and as proof there is now the Django project, which is a sort of Rails for Python done by a guy I'm pretty sure will be making a great project. But for whatever reason* this didn't come out of the Python community until recently.

So you couple the joy of Ruby coming out of nowhere with the joy of a well-designed framework hitting people at the same time and you can see the hype has some foundation. It's a prototypical "killer app" for the language. Mix into that the word "ajax" and "web API" a few times and you have yourself a genuine hype machine.

So what's really going on here?

There are some separate libraries which are made to work well together, and on top of that some conventions that make writing the code really straightforward.

The first is their database-object mapping library, which provides an abstraction over your database with very little code if you follow their rules. For example, the "id" field in each table should be the primary key -- if you want something else, you have to add a line of code.

(As an example of playing to Ruby's strengths, this line of code is a bit of metaprogramming so it's very straightforward and clean: in your class defintion -- not even in an initializer! -- stick in "set_primary_key 'mykeyname'". The class definitions here, as in a few other places in Rails, look more like abstract statements about the properties of what's going on rather than real code. Fancier stuff, like input validation, is done in a similarly expressive way.)

This model/view separation is one of the things LiveJournal really lacked, as all of our efforts to add APIs when memcached came around really showed. Since with this all of your database accesses go through a class, it's relatively easy to add things like cacheing or schema changes later. I already ran into the utility of this when I was importing my old data (the Hebrew RTL stuff) into my database: in making the web UI, I had already written (Rails had already given me) an API for my script to write into the database.

My initial skepticism ("this will be slow") is addressed easily: you can write any accessor functions you want into your model all the way down to SQL. But they map a bunch for you already, including "where" clauses: if I need to, I can write Entry.find(:all, :where => 'age > 3', :limit => 10) and I get back a nice array of Entry objects instead of SQL rows.

You can read more about the database mapping on this overview page. (Just ignore the jargon like "business objects", yikes.) They do many more interesting things, and the exposition is brief, so it's worth a scan.

The second framework does all the "web logic" stuff that all web apps seem to eventually grow into. Like: you need a templating language for generating pages. And then you want to map magic URLs to do different parts of code, so you start with mod_rewrite, grow beyond that, extend to mod_perl and eventually you're providing a completely virtual URL space to the outside world.

So Rails provides nice integration of these things: you get to write your rewrite rules in Ruby, of course, and paths map to actual method calls, and a given method automatically uses the associated template. The gee-whiz demos that get all the blog links are how you can say "give me a UI for editing a list of [x]" in basically a single line of code -- it provides the "new", "edit", "delete", "list", etc. operations ("CRUD") and pages for you. These are nice for demos and prototyping but are clearly not what you use in a real app. But once you want to move beyond that you can say "generate that UI code for me" and you see that the one-liner function is only providing a few lines of code underneath and it's trivial to add/remove/edit these operations and others.

It sorta all comes together when in the templating language you can say "create a link to the 'bar' method on this object" and it generates the correct URLs and parameters. Or you can say "paginate this list of items" and all of the URLs are created the right way. (Here's overview documentation of this and more.)

My primary concerns are these:

Are there apps where these designs don't work? I've been brainstorming for a while and I haven't convinced myself either way. You'd think a more complicated view and algorithm like the LiveJournal friends page would break everything, and it's possible you're right, but it seems to me that in those cases you'd drop down to a bit of direct SQL (hopefully wrapped in the same model/view separation API anyway, so you could do stuff like generate Atom feeds of your friends page easily) and that the other 98% of your app (like editing memories or userpics or even posting new entries) is the standard CRUD.

The other concern is: how much do you pay in speed? We're ok with paying a bit so we can write in a high-level language, but how much do these layers of abstraction and database reflection, etc. cost you?

But that doesn't scare me too much. As a final example of how end-to-end this seems to be, they even have a mailing library, that lets you hook up automated email sending as well as incoming email processing, again through all of these nice APIs. And in case you need a few more observations of how well-done this is, consider: built-in unit testing, including UI unit testing; a "console" script that loads up all of the database access into a Ruby shell so you can diagnose and edit the data interactively; and even a breakpointing service so you can set breakpoints in your code, click around in the webapp, and have it drop you to a shell in the context of where it was interrupted to let you interactively diagnose what's going on.

So, to summarize: yeah, I think it really is worth the hype. I'm impressed.

* I'm using this phrase to denote "this wasn't necessarily the way things had to turn out, but it just happens that [x]".