Sit back, relax, and enjoy the code.

Rails Gotcha: ActiveRecord Caches Associated Records by Default

Posted: April 23rd, 2009 | Author: gabe | Filed under: Rails | Tags: , , , , | 2 Comments »

ActiveRecord will cache the results of association method calls by default, unless you tell it not to.

(This applies to Rails 2.3.2 and perhaps earlier versions.)

From the documentation:

project.milestones             # fetches milestones from the database
project.milestones.size        # uses the milestone cache
project.milestones.empty?      # uses the milestone cache
project.milestones(true).size  # fetches milestones from the database

Normally, this is great. However, if you’re not aware of this default caching, you might see some strange behavior in your app or tests and have no idea what’s going on. This default caching behavior had me and Abel stumped until we read about it in the docs.

Working with the same concept as the example in the documentation, where a Project has many Milestones, here’s a more explicit example of the caching behavior in action:

project_1 = Project.find_by_id(1)  
project_2 = Project.find_by_id(1)  
# Load the same Project into two variables
 
project_1.milestones.length   
# - Hits the db's Milestones table.  
# - Caches milestones object on project_1.
# - Returns 0. 
 
Milestone.create(:project_id => 1, :name => 'New Milestone')  
# Adds a milestone to the project. 
# But we don't do it through the project_1.milestones association
# because that _would_ update project_1.milestones's cached value
 
project_1.milestones.length   
# Returns 0 (not 1, like you'd expect) 
# because project_1.milestones was cached when 
# previously requested above.
 
project_2.milestones.length  
# Returns 1, because project_2.milestones 
# hasn't been requested/cached yet.
 
# Note: project_2.milestones is 
# a COMPLETELY DIFFERENT IN-MEMORY OBJECT 
# than project_1.milestones.
# Taking this point further, here's an explicit example:
 
project_2.milestones << Milestone.create(:name => 'Another Milestone')
# Put another milestone on the project through 
# project_2's milestones association.
 
project_1.milestones.length
# Still returns 0, because project_1 already cached it's copy of 
# the milestones association back when there were 0.
 
project_2.milestones.length 
# Now returns 2, because only project_2 knows about the new milestone.
 
project_1.milestones(true).length  
# Returns 2, because ActiveRecord updates the cache 
# when association(true) is present.

Another funny something to note about the association caching behavior is that even when ActiveRecord uses a cached value, it still emits SQL to the log file. So, don’t let that trip you up either.

  • Share/Bookmark

Quick Hits: Setting the User Agent Header in Webrat

Posted: March 31st, 2009 | Author: Brad | Filed under: Rails, Testing, ruby | Tags: , , , , | 1 Comment »

If you’ve read the new PragProg beta e-book on RSpec, you may have read that you can set HTTP headers for your Webrat request like so:

Given /^I am browsing the site using Safari$/ do
  header "User-Agent" , "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us)"
end

Like me, you may have found out the hard way that this doesn’t work. Webrat does not automagically apply these new HTTP headers to your request – they certainly don’t make it to my controller. What worked for me:

Given /^I am browsing the site using Safari$/ do
  headers["User-Agent"] = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us)"
end
 
When /^I visit my precious site$/ do
  get '/my/precious/path', my_query_string, headers
end

In the code above, headers is a method call that returns all the HTTP headers for your request. Just tack headers on as the third argument of your request, and you’re good to go.

  • Share/Bookmark

Better Rails Searching with Named Scopes using Scope Builder

Posted: December 22nd, 2008 | Author: gabe | Filed under: Rails | Tags: , , | No Comments »

When it comes writing elegant search code in a Rails app, Named Scopes immediately come to mind.  And for good reason: they’re a fantastic way to express, well, scopes, for your searches.  In your Person model, you might have named scopes like by_last_name, by_age, and by_sex.  But, what do you do when you want to give your users a search form and let them find people by any combination of last name, age, and sex?

Read the rest of this entry »

  • Share/Bookmark

Quick and Dirty Messaging

Posted: November 22nd, 2008 | Author: Brad | Filed under: Great Minds, Javascript, Programming, Rails | Tags: , , , , , , , , , , | 1 Comment »

Our Rails Rumble 2008 entry, Great Minds (you can have a look at the latest version or the original Rumble version), required a messaging system. Such systems are easy to do wrong, and we knew we’d need something that would stay solid under unknown load during Rumble judging.

The solution was quick & dirty (as most solutions are during Rails Rumble), but the results worked, and allowed us to qualify for judging and reach a respectable 28th place finish in the “Completeness” category.

Check out the details after the fold.

Read the rest of this entry »

  • Share/Bookmark