Four years ago, Merb was created as an alternative to Ruby-on-Rails. The project was focused on creating an optimized environment that was thread-safe. But in December of 2008, Merb and Rails were on a collision course, set to split the Ruby community in two. Instead of fighting, the two projects made nice, and it was decided that Merb would be merged with Rails for version 3.0.
At the end of August, Rails 3.0 finally arrived. We caught up with Engine Yard’s Yehuda Katz, creator of Merb and now a core Rails committer, to ask him about the new combined projects and to check in on the major changes to Ruby’s partner in all things Web.
SD Times: What did the merge accomplish? What historic Ruby problems are now gone?
Yehuda Katz: Merb was created four years ago to address the fact that Rails wasn’t thread-safe. We decided it would be easier to start over and write something small rather than try to make the existing monolithic thing thread-safe.
Over time, Merb became this full-fledged Rails competitor. During that time, Rails actually got thread-safe, Rails got faster, Rails got Rack, Rails got a lot of the things that Merb had. But because they were starting from an existing thing, they were never really able to catch up to Merb’s aggressive performance targets.
But at some point, we were fighting a lot, and we sat down and said, “We don’t have the exact same priorities, but we have the same goals. We’ll get really aggressive about dealing with the parts of Rails that are too slow or could use better threading.”
So we rewrote Action Pack. Rails has some backwards-compatibility concerns that make it impossible to make the basic experience quite as fast as Merb, but one of the goals of Rails 3.0 was to build a toolkit so that if you have a performance concern, you could build a subset of Rails that would be just as fast as Merb.
Are the speed benefits obvious from the start?
If you just start a Rails 3.0 application from scratch and compare it to Rails 2.3, you may not get a huge speed improvement. But what Rails 3.0 does is it gives you the ability to opt out of expensive features.
Rails has a bunch of HTTP features that make the browser happier. They add an extra millisecond on the server. If you’re writing an API, maybe that doesn’t matter much. In Rails 2.3, you’d have to take all that. In Rails 3.0, you can say, “Give me a controller but don’t give me the HTTP caching stuff.” Rails 3.0 is built so that the default controller is the base, and we add a bunch of modules on top.
So performance was a big focus for Rails 3.0?
To be honest, performance wasn’t the biggest part of our effort in Rails 3.0. We’re aggressively working on performance in 3.1. I think it’ll be a lot like Python: [Python] 3.0 was a lot of new architecture, and 3.1 got fast.
Plug-ins were the real focus. Ruby has this nice benefit of letting you overwrite anything at runtime. It’s easy to write a plug-in that hacks into something and does whatever. But that doesn’t really scale if you have five plug-ins that are all hacking the internals of Action Pack. That could start failing, and that did start failing.
We added a bunch of plug-in APIs and built Rails itself as a plug-in. We had to expose stuff for ourselves, but more importantly, it’s easier to add your own. Action Mailer is a plug-in that happens to be developed together with the core team. If you just install the Rail Ties without Action Pack, Active Record will be wiped out without a trace.
What types of third-party plug-ins does this change help out?
There’s a plug-in called Devise. It’s an authentication plug-in by José Valim. He’s on the core team now. He built something that worked reasonably well in Rails 2.3, but in Rails 3.0, it feels like it’s part of Rails. It’s tightly integrated. It includes OAuth 2 support and Facebook Connect out of the box. If you install Devise and you want to integrate with some existing APIs, the fact that it’s so tightly connected to Rails makes it seem like Rails has OAuth support.
RSpec on Rails existed prior, and there were a lot of shortfalls with that. Once you installed RSpec, you couldn’t install the default generators anymore. You couldn’t install the normal Rails controllers and get testing stubs. If you wanted to drop out another ORM [Object-relational mapper] like DataMapper, someone would have to build the RSpec DataMapper model generator, and people didn’t tend to do that. What ended up happening is when you opted into these alternatives, you ended up losing the reason you used Rails in the first place.
In Rails 3.0, in terms of generators, when you do a controller generator, Rails calls out to the default ORM test framework or engine to generate whatever piece that is. The DataMapper for Rails plug-in has listeners for those hooks, and when Rails asks for a model, it replies with “Here is what you should do.”
When you install DataMapper, it drops directly into place, and it sees the amount of time that was spent in models. Anything Active Record can do, DataMapper can do. We can’t have any backdoor APIs anymore. Any API we use for Active Record is available for DataMapper. Integration with logs and Rake tasks, and a whole slew of things Active Record had built in via backdoor, are now available internally to all plug-ins.
What were the big changes in Ruby 1.9.2? Anything you took advantage of in Rails 3.0?
First of all, the Ruby team did a big service to the Ruby community by allowing you to write a Ruby application that runs in both 1.8 and 1.9. Python has not done that. For Ruby 1.9, while it’s not exactly compatible with 1.8, the work that has to be done to make a program run in both is not large. We’ve been running continuous integration against Ruby 1.9 from the beginning of the Rails 3.0 process. Things have changed over time. There have been a lot of changes in 1.9.2.
The most obvious thing is performance. If you switch to 1.9.2, you should see a big performance gain. There’s a brand new virtual machine and a new compiler. It’s more modern, and it uses real OS threads instead of Ruby implemented threads. It still has a global interpreter lock, so it can’t run programs in real parallel, but it does a much better job of making five database requests at the same time.