January 30th, 2008
Another week of edge Rails changes, featured on the Rails Envy podcast. This weeks’ report covers changes from 21 Jan 2008 to 27 Jan 2008 (the day the Rails Envy podcast was recorded).
Eager loading :includes does pre-loading
The current gem Rails behavior when loading associations with something like
Author.find(:all, :include => [:posts, :comments])
is to make a big query with multiple joins that fetches all the associations in the same query (for you RDBMS geeks, the “cartesian product”).
Frederick Cheung (fcheung on the Rails Trac) has contributed an impressive optimization to this by pre-loading the associations rather than trying to eager loading with one big query. So a find like this:
Author.find(:all, :include => [:posts, :comments])
would first load all the authors, then all the posts, and then all the comments in 3 smaller, faster queries.
The main benefit of this (depending on your data): Running X simple queries (to fetch authors, then posts and comments) rather than one mega-query that joins all the associated tables is faster most of the time. The result set is often smaller as well.
More details can be found at ticket #9640 on the Rails Trac.
Related changeset: http://dev.rubyonrails.org/changeset/8672
composed_of aggregations can now be used in finder conditions
You can now use value objects that you’ve previously specified that your model is composed_of in the finder conditions hash. E.g. if you have a Customer that has a balance aggregation:
class Customer < ActiveRecord::Base
composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
end
You can now pass a Money value object to Customer#find:
Customer.find(:all, :conditions => { :balance => Money.new(20, "USD") })
Convenient.
Related changeset: http://dev.rubyonrails.org/changeset/8671
New helper: label_tag
A label_tag helper has been missing for quite awhile since the label helper was added. No longer:
label_tag 'name'
# => <label for="name">Name</label>
label_tag 'name', 'Your name'
# => <label for="name">Your name</label>
Related changeset: http://dev.rubyonrails.org/changeset/8685
New ActiveSupport class: ActiveSupport::TimeWithZone
A new ActiveSupport::TimeWithZone class has been introduced to make timezone support in Rails easier. Ryan Daigle has a good writeup on this.
A word of warning though: this is still work in progress, as Geoff Buesing, the core team member responsible for these timezone changes, has more to add on how his plans for the timezone puzzle in the comments.
Related changeset: http://dev.rubyonrails.org/changeset/8696
map.root can be easily aliased
map.root now accept a single symbol as an argument to declare an alias. Just a little something to keep your routes a little more DRY, e.g.:
map.new_session :controller => 'sessions', :action => 'new'
map.root :new_session
Related changeset: http://dev.rubyonrails.org/changeset/8738
As usual, let me know of any inaccuracies or any suggestions you may have in the comments!
January 28th, 2008
Prototype’s Try.these is a really useful bit of code (though I doubt it sees much application outside of JavaScript libraries). I have this evil bit of code somewhere, for example:
var results = Try.these(
function() { return response.responseText.evalJSON(true); },
function() { return eval('(' + response.responseText + ')'); }
);
Evilness personified in its evaled glory, but that’s besides the point here.
Just for fun, I tried to do this in Ruby, just to see how easy it was, and came up with this try method:
module Kernel
def try(*these)
raise ArgumentError, 'try requires at least 2 arguments' if these.size <= 1
fallback = these.pop unless these.last.respond_to?(:call)
these.each { |candidate| begin return candidate.call rescue next end }
fallback || raise(RuntimeError, 'None of the given procs succeeded')
end
end
Which you can (ab)use like this:
try(
Proc.new { open('http://finance.yahoo.com/foo/') },
lambda { open('http://finance.google.com/bar/') },
proc { open('http://finantiq.com/flomp/') },
:fallback
)
Real world applications would be, like the example above, retrieving content from several unreliable websites to retrieve currency rates.
You can find the specs for Kernel#try here: http://pastie.org/144339.
So the question now is, is there a better way to implement this, or a more idiomatic Ruby way?
January 25th, 2008
Songza’s quite nice, especially after us non-US users lost access to Pandora. Now I can finally listen to some boy band music when I feel like it (because I don’t have any in my library).
January 25th, 2008
Funniest bug report I’ve seen on the Rails issue tracker: http://dev.rubyonrails.org/ticket/10919. Be sure to read the comments, and the resolution:
I appreciate that penis enhancements are the norm for most of the commenters here, but their use is definitely not widespread enough to justify fixing this.
January 23rd, 2008
It’s time again for your weekly dose of what’s new in edge Rails. This weeks’ report covers changes from 14 Jan 2008 to 20 Jan 2008 (the day the Rails Envy podcast was recorded).
Route recognition is faster
Rails’ route recognition has been optimized and is significantly faster especially for applications using many resources (i.e. via map.resources in your config/routes.rb). Big thumbs up to Oleg Andreev (aka oleganza) who also wrote a detailed post about it. You can also find out more about how this works in the code comments.
Related changesets: http://dev.rubyonrails.org/changeset/8674 and http://dev.rubyonrails.org/changeset/8652.
render :partial and forms
Instead of doing:
<% form_for(:person) do |f| %>
<%= render :partial => 'form', :locals => { :form => f } %>
<% end %>
you can now do:
<% form_for(:person) do |f| %>
<%= render :partial => f %>
<% end %>
Convenient. This also works with your custom form builders:
<% form_for(:person, :builder => LabellingFormBuilder) do |f| %>
<%= render :partial => f %>
<% end %>
The partial template rendered is then people/_labelling_form and the local variable in the partial is labelling_form.
Related changeset: http://dev.rubyonrails.org/changeset/8646.
Callbacks refactored into ActiveSupport::Callbacks
All that duplicated callback code in ActiveRecord, ActionController::Dispatcher, and in test case setup and teardown methods have been extracted into the ActiveSupport::Callbacks module.
Less duplication means more robust code so yay!
Related changeset: http://dev.rubyonrails.org/changeset/8664.
ActiveRecord test suite gets a makeover
The ActiveRecord test suite has become something of a maze jungle in recent times, with no clear hierarchy of how things are organize. It’s not that easy to find what you are looking for especially for newcomers trying to contribute to ActiveRecord. Thankfully, someone (John Barnette - the foxy fixtures guy) has stepped up and cleaned things up quite a bit. Fixtures, test cases, fixture models, etc. are now in their own directories and from looking at the directory hierarchy one should be able to know where to put what at a glance.
Related ticket: http://dev.rubyonrails.org/ticket/10742.
Bugfixes
* redirect_to nil no longer explodes (but raises a friendlier ‘cannot redirect to nil’ exception) (related changeset: http://dev.rubyonrails.org/changeset/8633).