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.

Notes

  1. k776 posted this