Ruby 201: Using call stack information at runtime 5

Posted by jeff Thursday, November 15, 2007 06:04:00 GMT

Stack
Photo credit

This is just a quick tip that you may never need but came in very handy for me this week.

While perusing the log of one of my Rails apps, I noticed that a certain method of one of my models was being called four times when I expected it to only be called once. I did the usual Find in Files to see where I'm calling it, but that didn't really answer the question. A mental walkthough of my code didn't reveal why this method was getting called more than once.

I'm actually at home while I write this, and I can't remember the exact code, but it went something like this:

def something_important
  RAILS_DEFAULT_LOGGER.info "About to do something important"
  if something_exists?
    do_expensive_operation
  end
end

I found a test which was already exercising code which called this method, and sure enough, when I ran that one test, I got multiple log statements emitted into the test log file.

During testing, I tend to use puts statements when I want to see some debug output (although tailing the log file would work too). So I did this:

def something_important
  puts caller.grep(/\/app\//).first
  RAILS_DEFAULT_LOGGER.info "About to do something important"
  if something_exists?
    do_expensive_operation
  end
end

And presto, when I ran the test, it displayed for me the exact line that was calling this method each time. That revealed the source of the problem, which was easily fixed. In this case, seeing the actual call stack was really helpful.

caller is not Rails magic; it's a Ruby method of the Kernel class, which all objects inherit. It returns an array of strings representing the call stack. Now, in a Rails application, the call stack can be really long. In my case, the 15 or so methods at the top of the stack were coming from the Rails framework itself, so that was a lot of noise to weed through.

By doing a grep on the array and choosing the first match, I filtered out everything up until the first entry in the call stack that was coming from a file in the app folder or below. In other words, this revealed the most recent line in my code that triggered this method.

Comments

Leave a response

  1. Dave B   November 15, 2007 @ 06:58 AM

    On this subject of call stacks, last night I presented at the Sydney Rails user group about Rails and Netbeans.

    One of the compelling features I mentioned was the internal debugger, and how it reveals a clickable call stack that takes you directly to the code in question.

    So to do what you've shown here would just require setting a breakpoint (which doesn't pollute the code) and letting it run.

    I guess we could debate which is less work or more convenient, but its certainly something to keep in mind -- we can do this in an IDE, but the approach is slightly different.

    Cheers, Dave

  2. Corbin H   November 15, 2007 @ 04:28 PM

    Dave - Did you actually get netbeans debugger to work with using native ruby or does it only work when using JRuby. I never really got my netbeans install working quite right but it's been a while--maybe it's better now.

    Is there a tutorial somewhere or does it just work out of the box?

  3. MaseratiS   November 23, 2007 @ 07:52 AM

    Really usefull information but it has never happened to me!

  4. kino   May 24, 2008 @ 01:12 AM

    What we have alone been able to show is that our understanding can not take account of our understanding; on the other hand, our sense perceptions are the mere results of the power of our a posteriori knowledge, a blind but indispensable function of the soul.

  5. Ellroy   May 30, 2008 @ 11:19 PM

    Since knowledge of the things in themselves is a posteriori, the never-ending regress in the series of empirical conditions, so far as regards philosophy, can never furnish a true and demonstrated science, because, like our understanding, it would thereby be made to contradict ampliative principles, but our concepts are just as necessary as the Ideal of human reason.

Comment


(won't be published)