Lazy Loading Ruby Methods the Smart Way


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

Tags: , , , , , ,

3 Responses to “Lazy Loading Ruby Methods the Smart Way”

  1. Vladimir Says:

    great post!

    Can you write some about programming Ruby using TDD? I had not find good articles about that.

    Sorry for my english ;-(

    • Jaime Bellmyer Says:

      Hi Vladimir-

      Ruby TDD is one of my favorite subjects. I have quite a few articles on TDD with Shoulda and Mocha, but I recently started using Rails 3 with Cucumber, RSpec, and Shoulda macros. I need to get more comfortable with it, then I’ll be able to start writing intelligently.

      And your english is great! It’s way better than my spanish, which is the only other language I know.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: