Wrong way, Right way - Part 2 - Work with eager loading, not against it
Part of a series about common Ruby mistakes that make things slower or are just plain wrong. Not the style of the code (indentation, methods names etc), but the actual code itself. All examples use Ruby 1.9.2 and Rails 3.0.x.
Part 2 - Work with eager loading, not against it
The Wrong Way
class Post < ActiveRecord::Base
def last_comment
comments.order(:position).last
end
end
Post.includes(:comments).each do |post|
puts post.last_comment
end
Whats wrong? The last_positioned_comment instance method on Post is adding an order to comment query, which negates the includes(:comments) part of the Post select query. As a result, you’ll get one query for all posts, one query for all comments, and one query for each post to get the last comment. (pattern: 1P + 1C + 1*P)
The Right Way
class Post < ActiveRecord::Base
def last_comment
comments.sort_by(&:position).last
end
end
Post.includes(:comments).each do |post|
puts post.last_comment
end
Whats better? Because no extra sort is added to the comments, the ones that were preloaded still match. So you end up with only two queries, one for all posts, and one for all comments. (pattern: 1P + 1C). To end up getting the proper sorting, you simply use Ruby’s sort_by method along with symbol to proc syntax to create a nice clean implementation that works with eager loading.
Questions? Comments? Let me know what you thought.