Ian Bicking is not particularly impressed by Rails. He says there’s nothing special about the framework, nothing that makes it different from other Web programming libraries. As a longtime user of this kind of frameworks, I understand his criticism; I even agree with some of them. As a recent Rails enthusiast, I disagree with his comment as a whole.
As readers of this blog probably know, there is something I value above all other things in a programming language or library: simplicity. More than any other feature, that is what defines the power of a tool to me. Simplicity, in this case, is not merely ease-of-use, but a set of characteristics that make the tool useful in the long term.
So the short answer to Bicking’s contention is: Ruby presents a kind of simplicity no other framework I’ve used (or at least know of) does. As such a short answer is not so interesting, I’ll comment on some specific aspects of Bicking’s post.
First, I agree with him that dynamic programming languages are superior to static languages. I’ve used dozens of languages while developing for the Web, and dynamic languages are much more productive than static ones — even PHP, with all of its problems.
Python, the languages Bicking seems to favor, is as dynamic as Ruby. As a Smalltalk fan, I favor the syntax of none of them, but I love the dynamic features of both. I resisted the thought of learning Ruby for a long time (as my pal Guaracy would surely confirm), but I ended up falling in love with it because of its power. Ruby is built upon the same principles that attracted me to Smalltalk in first place, and doesn’t suffer from the limitations of the latter (limitations in the environment, not in the language itself).
From the beginning, something was clear about Rails to me: Rails was successful in Ruby (not in Python, Perl, or PHP) because Ruby favors the natural emergence of such cohesive and complete libraries. Bicking almost recognizes this, but backs off believing it has something to do with moment or luck. He says:
[Those pieces] fit together really well in Rails, which is perhaps what it offers that Python doesn’t have. Well, I’m sure some framework out there has this, but it’s hard to say, there’s so damn many.
Which are those other frameworks, I don’t know. I’d say that the various frameworks for Delphi and ASP.NET are in the right direction (as heretical this opinion may sound for open source zealots), but they are not there yet. Maybe because those platforms don’t use dynamic languages. I’m not, of course, talking about RAD development.
The pieces Bicking mentions are the three main components of Rails: the object publisher, the object-relational mapper, and the templating system.
There are literally thousands of separate implementations of those components in any programming language you can think of, as Bicking himself notes. But the tight integration between them, found in Rails, is another matter.
Talking about the object publisher, Bicking points that there are many of them implemented in Python. What he doesn’t say is that none is as obvious and intuitive as the one in Rails, although he recognizes that efforts to create MVC frameworks tend to become either too restrictive or too ambitious. This is the point he missed about Rails. Because of Ruby, Rails can achieve an almost perfect balance here.
Object-relational mappers are also present in most widely-used languages. Any programmer who wrote one knows how hard is to make the right choices for the complicated interactions between those two data manipulation models. A dynamic languages makes the path smoother, but is no silver bullet. I wrote object persistence frameworks in both static and dynamic languages, and they were never an easy job. SQLObject, mentioned and created by Bicking himself, is an excellent tool, fitting nicely in Python’s philosophical approach to programming, and I agree that it and the one present in Rails are similar. But, once again, Rails seems to make things simpler and more intuitive. Rails propitiates a very fine control over the mapping, without losing its simplicity. Many things are automatically detected, but the programmer can step at any time to override the default behavior.
As for the templating system, I strongly disagree with Bicking. Saying that Ruby’s templating system looks like ASP is that same as saying that Cheetah, considered one of the best templating engines for Python, looks like ASP as well. Ruby’s templating engine has its limitations, but those limitations are present in most similar engines, including Cheetah.
Before answering Bicking’s last point, I want to clarify something. I’m not criticizing Python. I have not given up on Python because I’m using Ruby now. I can only be in two states regarding a programming language I’ve used: either I like them or I hate them — sometimes, for pure aesthetic reasons. I don’t like Perl and that’s it. Perl is more powerful than many other programming languages, but the philosophy behind it never appealed to me. Between “there’s more than one way to do it” and “the principle of least surprise”, I choose the latter.
In Python, you can obviously create everything Rails offers. In fact, a friend of mine is writing a post this very moment about using SQLObject, Cheetah and mod_python to achieve something very similar to Rails. But, as mentioned before, integration is still not as tight and intuitive as in Rails. Python 2.4 introduced some interesting features that will make the task even easier. But even if an equivalent package is developed for Python, I still will use Ruby whenever possible — not because it was the first good framework I used, or because I don’t like Python. It’s just that Ruby encapsulates a philosophy that really resonates with mine where programming is concerned.
Going back to Bicking’s last point, he asserts that Rails probably doesn’t scale when the project’s complexity increases, failing in the same way other existing frameworks will fail. I’ve developed hundreds of Web applications in my programming career, and even with my limited experience in Rails, I can easily see it’s really different from the other frameworks I used. Rails is not a silver bullet. No library or tools is. But Rails works.
Recently, I had to solve a problem in Python: dynamically insert some aspects in a class as it was being defined. I didn’t want to use a complete AOP framework, because I needed something simple. Using Python 2.3, you can create a function that modifies a class as needed. Not elegant, but works. But Python doesn’t offer anything like Ruby’s solution for this problem, mixins. The decorators in Python 2.4 were introduced to handle this very problem with a more compact, elegant and comprehensible syntax. The other hacks (metaclasses, for example) require a considerable effort on the part of the programmer, and are not scalable or flexible enough. Take a look at PEAK‘s source and you will see what I mean. To introduce a simplified decorator syntax in Python 2.3, this library had to capture the context (the frame, in fact) in which a class was defined and modify it, something that goes strongly against the Python way of programming. Interesting and useful, but also depends on details that can change from a release to another.
This is the fundamental difference between Ruby and Python, and which applies to Rails as well. In Ruby, things like that are not only possible, they’re obvious. When the applications has to scale, that makes the whole difference. Bicking asks:
Eventually you’ll need to tweak a generated form just a little does that mean you have to throw away all the automated aspects and code it by hand? (Emphasis his.)
Not at all. When Rails needs to scale, Ruby gives it the power to change just the behavior needed and keep the rest of Rails’ functionality in place.
A simple example: one of Rails’ core functions creates a drop-down list in HTML from a collection of objects. This list has an optional argument indicating that the first element in the drop-down list should be blank. I needed to change this dummy element to present a specific text instead of just displaying an empty text. The method, however, was private to Rails. Solution: Ruby. The language, like Smalltalk, allows a programmer to dynamically modify an object, even its private methods, without resorting to inheritance. I redeclared the method, and Rails started to use my modification as if it had been always there, and I didn’t have to use substitute classes or any other contorted solution.
A more complex example, based on Bicking question: how to deal with more complex interfaces? Rails’ automatic scaffolding for leaf tables only works on simple fields (which is more than most frameworks do by themselves). As I needed something more complex, it was easy to create a method whose arguments describe a form specification to build, including sub-objects. For almost every form I need to do, that’s enough. Few forms combine so much logic in them that they need special handling. Before someone says this application I’m developing is way too simple, I will mention it’s complex enough to need the creation of dynamic tables in the database driven by users’ specifications with complex relationships between them. Rails absorbs those tables as if they had been in the application model from the start.
Complex input requirements? Actions on updates? Authentication? Joins? Rails handles almost everything for free. Where it fails, Ruby provides a sustainable path for growth.
To sum up this long post, as I said in another entry, simplicity is power. Rails is simple. Behind Rails, there is a language that makes of simplicity its hallmark. That’s what makes Rails special, I’d say.