Over the last few months Rails 3 has really begun to take shape. We’ve been hard at work building, refactoring, building, and then refactoring all over again, and I’m pretty pleased with how things are going. There’s still a lot of work to be done (we’re working on it, these things just take time :P ), but I wanted to pause for a bit and talk about some of my favorite features.
I know a lot of you are eager for information on Rails 3. While this isn’t a whole lot of code… think of it more like a movie trailer, and keep an eye out here, on the rubyonrails.org blog and on my personal blog for more information as we get closer to the release.
1. New Bundler
A lot of you have had trouble with the various efforts to make it possible to declare gem dependencies in your application and ensure that they’re available on your development and production environment. Merb had an early solution to this problem that got about 90% of the way there, but didn’t handle a large number of gems very well. This was exposed by the Engine Yard Cloud team, who had some trouble with the Merb gem bundler with a large number of potentially interdependent gems.
The new bundler handles virtually all issues we could think of around bundling gems in Rails applications, and then some.
Rubygems Resolution
This problem arises when you have multiple gems that depend on overlapping versions of some other gem. For instance, let’s say you depend on ActiveMerchant 1.4.2 and Rails 2.3.2. ActiveMerchant depends on activesupport >= 2.3.2. Rails 2.3.2 depends on activesupport = 2.3.2. Now let’s say you have Rails 2.3.2 and Rails 2.3.3 on your system.
If you go ahead and require "activemerchant", it will see the >= 2.3.2. Since you have 2.3.3 on your system, it will pull it next. Next, you gem "action_controller", "= 2.3.2". Since ActionController depends on activesupport = 2.3.2, it’ll see that 2.3.3 was already activated and fail. You’ll get the dreaded Gem::LoadError: can't activate activesupport (= 2.3.2, runtime), already activated activesupport-2.3.3.
This problem arises because gems are currently resolved linearly instead of doing an algorithmic dependency resolution. It turns out that doing dependency resolution with overlapping arbitrary ranges over a large set of potential dependencies is a hard problem. It took us a number of iterations over about a year to get it right, but our current solution is simple, elegant, reasonably fast, and most importantly, correct.
Limit Network Dependencies for Deploy
This is something we felt very strongly about with the Merb bundler, and most people seem to be coming around. The basic idea is that you should store your app’s dependencies inside your application, not require a connection to Rubyforge, Github, and who knows what else at deploy time.
In addition to the possible risk of a network resource being down, there is the risk that you declared a dependency on something like nokogiri, and a new version was released to Rubyforge since staging, which breaks your code. You could solve this by always hardcoding all of your versions, but it’s nicer to be able to declare your conceptual dependencies (”I want Nokogiri — any version”). Once you pull the latest versions of your declared dependencies into your application, they should stay put, rather than possibly changing at deploy time.
Consistent Environment
By bundling specific .gem files inside of your application, you ensure that every developer, staging, and production are all running with the same environment.
Native Gems
By storing just the .gem file in the application, we can have a command that compiles the native gems for the architecture in question.
2. ActionController Architecture
In Rails 3, ActionController::Base is built on top of ActionController::Metal, a stripped down version of ActionController with support for simple Rack semantics. We then include a series of modules to add support for things like callbacks, rendering, layouts, helpers, and on and on. This means that it’s easy for you to start with ActionController::Metal and pull in just the features you want, paying just the performance cost you want.
We even implemented deprecated 2.3 features as a module that gets mixed in to ActionController::Base. If you aren’t using any deprecated features, you can simply stop including the module and get some performance back. You also know that you’re not using any features you shouldn’t be.
3. Responder
Since we got RESTful controllers, it has been very common to see idiomatic controllers that have actions looking like this:
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
flash[:notice] = 'User was successfully created.'
format.html { redirect_to(@user) }
format.xml { render :xml => @user, :status => :created, :location => @user }
else
format.html { render :action => "new" }
format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
end
end
end
In Rails 3, this will be shrunk down to:
respond_to :html, :xml # class level def create @user = User.new(params[:user]) flash[:notice] = 'User was successfully created.' if @user.save respond_with(@user) end
What we do is move the idiomatic logic into a new object called a Responder. By default, we use the Responder that ships with Rails, but you can use your own subclass the same way you would set a controller-wide (or application-wide) layout. That way, you can package up your own RESTful idioms into objects and use those.
4. ActiveModel
You’ve probably heard that things like validations are now available to any Ruby object by simply including a module: it’s true. Take a look:
class Person
include ActiveModel::Validations
validates_presence_of :name
attr_accessor :name
def initialize(name)
@name = name
end
end
Person.new.valid? #=> false
Person.new.errors #=> {:name => ["cannot be blank"]} # localizable of course
Person.new("matz").valid? #=> true
This is pretty cool, and provides a lot of ActiveRecord functionality for other ORMs. What’s really exciting to me, however, is that ActiveModel provides an API that can be used by ORM plugins to hook directly into ActionPack. If an object complies with the ActiveModel API, it will work seamlessly with the rest of Rails, just as well as ActiveRecord works in Rails 2.3.
This requires:
- Object#to_model: A method that returns an ActiveModel compliant version of the object. In most cases, this will return
self - Model#new_record?: true if the record has not yet been persisted
- Model#valid?: true if the record is valid (or the object has no concept of validations)
- Model#errors: a valid errors object. The errors object should respond to #[](field) and #full_messages
- Model#class.model_name: a valid naming object that responds to
singular,plural,element,collection,partial_path, andhuman. This is used in routing, URL generation, and a number of other areas, and is possibly the most important integration point.
If your object implements these APIs, it will work with ActionPack with the same level of support as ActiveRecord. Pretty exciting!
5. Performance
Over the past several weeks, I’ve spent a lot of time working on reducing Rails’ overhead. There’s still a lot to be done, but I was able to reduce the overhead of Rails between two and five times, with especially good gains on Ruby 1.9. While this does not improve the time spent in your code, it reduces the overhead in calling partials, rendering collections, rendering templates, and rendering the entire response. This will be especially apparent for action-cached pages where most of the cost of the request is in Rails, not your request.
I’ve also started to look at general bottlenecks, like stylesheet_link_tag (surprising!) and URL generation in Paperclip, two things that bubbled up in a recent performance analysis of a real application.
Most of the improvement came from finding bottlenecks and then caching them, which can be tricky because of the possibility of dynamic changes. For instance, it is possible to modify the view_paths during any request, so it’s hard to cache view lookup, since someone might prepend a path to the lookup order. In this case, my current approach has been to take a slower path, which means that doing dynamic things in Rails 3 is noticeably more expensive than the same operations in Rails 2.3 (where none of this was cached). I’ll have some details on what kinds of things to avoid for optimal performance in a later post.
For now, until the next batch of awesomeness, there are my top upcoming features of Rails 3. Feel free to comment with questions on these or other Rails 3 features and topics. As always, I’m happy to help however I can.

One other feature that I'm excited about and wasn't mentioned here is the fact that "javascript helpers" will produce HTML with HTML 5's data attributes that then the javascript framework can hook into and unobstrusively apply the intended behavior. This makes the output cleaner, and easier to swap out the javascript framework (JQuery anyone?)
Thanks for the update. Looking forward to Rails 3!
@harold yep! I only talked about features that are already done and available today. JS is coming in the next month or so :) I'll be sure to talk about it then.
@Harold That'll be really nice ;)
By the wayn are data attributes supported by non-HTML5 browsers? I mean does it raise an error if I use one in a HTML4 document?
No, they will work fine in older browsers.
I'll guess, html validators will complain about the unknown data attributes, won't they?
Someone could write a custom DTD that allows data attributes, and then you'd get that gold-star for your website.
It's already damned easy to swap in jQuery and do unobtrusive JavaScript. It's great that it will be "supported" now, but it's already available ….
I'm excited about mountable apps coming to rails 3 and greater support for rack. The possibility of creating an app from a bunch of small, pluggable parts is really interesting.
@Gimenez That is something that will be awesome, too. Going along with that, it would also be interesting if helpers put out microformatted HTML as a sort of API for an app.
@Jonathan helpers *will* be putting out microformatted HTML ;) That's the idea
That's badass.
+1 to the Responder. The old style always bugged me.
Not so big on the paradigm of naming things "Active*", (beyond say ActiveRecord) but I guess I'll live with it.
Me neither — but that's Rails culture at this point. Doesn't seem like a good thing to quibble about.
Can you describe how ActiveModel and ActiveORM will work together? Are there plans to provide more support for the many new "NoSQL" alternative data stores? The ActionPack integration steps outlined above are wonderful, but other pain points like the ActiveRecord-centricity of Rails plugins still exist.
ActionORM was replaced with the to_model part of ActiveModel. The price you pay for speaking about things early ;)
DHH talked about a new router dsl in which I'm also very interested to see what that will become. Maybe not that important, but I just love these kind of ruby tricks :)
It's on the way. Josh and DHH are meeting to discuss it later this week.
Awesome stuff!, any word on ActionArgs? http://stackoverflow.com/questions/1269569/cleani...
adelcambre just did a proof of concept port last night — http://github.com/adelcambre/rails-action-args/tr...
adelcambre just did a proof of concept port last night — http://github.com/adelcambre/rails-action-args/tr...
Nice!!! Will be sure to try it out. This helps heaps in DRYing us stuff.
Top stuff. One thing that I haven't heard much about is rails initialisation in V3. Can you shed some light on what work is being done on it?
We're working on that right now. There's a lot of small improvements. For one, we're moving global state in general into an Application object, cleaning up dependency loading, and unifying customizable path logic. You can also hook in and add your own initializer at any POINT in the init process easily now (or delete specific initializers). This is all heading toward having multiple apps in a single process.
love the Reponder & new Bundler
Mountable apps will be the killer feature, hands down!
Some word about Orchestra, Yehuda? :D
nachokb
the world of mountable apps has been rather quiet – is it still a planned feature ?
All the infrastructure should be in place by 3.0, but I suspect it'll be a plugin that glues it together for the moment.
We're working on it! I think it's likely we'll get it in before 3.0.
All this sounds great. But what will be more pleasing to eyes would be reading a Rails 3 Beta date. What is the ETA for rails 3.0 Beta. You should also blog once in a while about the features completed and features under progress.
Such blogs would be helpful to.
RailsGirl
It's coming ;)
I would also be interested in a release date. :-). Or how about an approximation? For those of us who've built a platform on merb especially it would be interesting to know.
Hehe :)
Yes we are all so keen!
I'd happily start a new project on a Beta. But if it's not til mid 2010, I should probably get over it :)
Mid 2010 would be extremely pessimistic for a beta.
I'll love mountable apps with a localization system
The Bundler. Can it be used outside of Rails?
Yes. I believe any ruby program can use it.
http://github.com/wycats/bundler/tree/master
Great news :) the end of dependencies nightmare is coming :)
Yep, all you need is a Gemfile and you can run gem bundle in any directory. Don't even need a framework, can just require "vendor/gems/environment" and you're set!
Interesting article! Thanks!
trackback: http://www.symbiosoft.net/blog/five-favorite-thin...
Hi Yehuda,
What's the current core position on tag helper generation of non self-closing tags for those of us who choose to use HTML4 rather than XHTML or HTML5? There have been a number of tickets opened in Trac and Lighthouse over the years which have been shut down, citing lack of "strictness" or "purity" in open tags. This means those of us using HTML4 have either needed to patch Rails, use a plugin, or manually write the HTML generated by helpers like image_tag, stylesheet_link_tag etc.
It would be great if Rails core had shifted from their previous position of "if you're using HTML4, you're stuck in the dark ages".
Cheers,
Nathan de Vries
Looks very Exciting.. All the new features will save lots of extra work to be done….. Great concepts.. I was looking for it.. eagerly waiting for Rails 3. :)
Oooh, the new responder stuff looks great. I wanted to try and do something similar on my own, but I figured it would eventually be implemented in rails and I didn't want to cause maintainability problems. Good thing I waited!
Great stuff all; I'm looking forward to playing with responders and the ActiveModel bits. The Bundler has started me thinking about ways to distribute pre-built gems for multiple architectures … but that starts feeling a bit rpm/apt/yum-ish, so my brain runs screaming in the other direction.
You mentioned "…things that bubbled up in a recent performance analysis of a real application." It would be great to see a post about how you approach app perf analysis.
Javascript validations?
Hi, I'm interested in implementing the API in (5) but I was wondering, what's the proper way to tell ActionPack about this new ORM and should this API be implemented as a module? Anyway, examples would be greatly appreciated.
Thanks in advance,
-Conrad
Thanks for useful post as usual.
I would like to see all other new stuffs that left in this post and will get released as a part of rails 2.3
Thanks,
Sandip R~
I'm interested in the modularity, like using Sequel instead of ActiveRecord, etc. The Responder looks nice too.
Can't wait for Rails 3.
Not necessary. HTML5 validators won't complain, and the HTML5 doctype will work on all browsers. I think people have just been burned so hard by browsers that they're afraid to even THINK that this stuff will work. But it does.