Posts Tagged ‘lazy loading’

Lazy Loading Ruby Methods the Smart Way

September 3, 2010

I just spent an hour trying to solve a seemingly simple problem. Luckily, test-driven development is the reason I spotted this problem at all! Here’s where I started:

  class Survey < ActiveRecord::Base
    SUBMISSION_STATII = ['submitting', 'submitted']

    def in_submission?
      SUBMISSION_STATII.include? self.status
    end
  end

I want the in_submission? method to cache its response in an instance variable, so it doesn’t have to run the check every time. I plan on this method getting called a lot. I wrote my code to test, using shoulda and mocha:

  should "cache the result" do
    Survey.SUMISSION_STATII.expects(:include?).with('new').returns(false).once
    survey = Survey.new :status => 'new'

    survey.in_submission?
    survey.in_submission?
  end

This is simply verifying that the status is checked for inclusion only once, even if we call the method multiple times. And here’s the code that I thought should satisfy the test:

  def in_submission?
    @in_submission ||= SUBMISSION_STATII.include?(self.status)
  end

I only added the @in_submission ||= part. This is my standard trick, but in this case it failed. Why? because whenever @in_submission is set to false, it will trigger the full code to run again next time. No “caching” happens. We should check whether the instance variable is defined, not whether it equates to true:

  def in_submission?
    return @in_submission if defined?(@in_submission)
    @in_submission = SUBMISSION_STATII.include?(self.status)
  end

Sure, we lose our sweet one-liner, but we gain an hour of productivity when our tests pass the first try. Or even worse, you didn’t bother to test caching at all, so it’s just slowing down your app and possibly even mucking things up. But you are using TDD, aren’t you?

Advertisements