Now that we’ve shipped a beta for Komodo 3.5 (still only on Mac OS X, other platforms to come), we’re being asked how to debug Rails applications with it. Here’s how.
A Note on Speed
So far I’ve only worked with Webrick. I should start off right now saying that the debugging process is relatively slow. Part of my work was to cut the debugging factor from 100 x to 50 x. Given that I found using the standard Ruby debugger also slowed things down by a factor of 100, I’m pretty happy with the process. Eventually I’ll see if we can do mod_perl-type debugging with Rails apps, so you aren’t running the entire app within a debugger.
Starting Debugging
Debugging the app is pretty easy. I assume you’re going to want to stop in the controller methods, so load the pertinent app/controllers files into Komodo, if they aren’t there already. Set breakpoints in the methods you want to stop in.
Now load the script/server file that you would normally run from the command-line. With that file active, start the debugger (with Command-> or Debug|Go). When the configuration screen appears, make sure the Directory field in the "Debug Configuration" option sheet is set to the top-level directory that contains all the apps folders. For example, if you ran the command rails books in the directory /Users/me/stores this field should be set to /Users/me/stores/books.
I mentioned performance right off the bat because I noticed that in development non-debug mode, it takes about a second to serve a request on my G4.
Continuing Debugging
Wait for the first four lines of output from Rails before you do anything
=> Rails application started on http://0.0.0.0:3000[timestamp] INFO WEBrick 1.3.1[timestamp] INFO ruby 1.8.3 (date) [platform][timestamp] INFO WEBrick::HTTPServer#start: pid=pid port=3000
At this point switch to your web browser, and start interacting with your Rails app. You should be hitting any breakpoints in your controller by invoking the appropriate URLs.
Stopping
Rails apps are servers which will run forever, left to their own devices. You can stop them by pressing the Stop button in Komodo.
Sample Scenario
Let’s assume you’re following the tutorial in "Agile Web Development with Rails" by Dave Thomas and David Heinemeier Hansson, and have built the sample "depot" application.
This scenario assumes you want to debug the add_to_cart method in the Store controller. Let’s assume that the full application is housed in .../stuff/rails/depot.
Load the app/controllers/store_controller.rb file, and set a breakpoint on the first line of the add_to_cart method.
Load the script/server file, and start the debugger, setting the Directory field to .../stuff/rails/depot.
When Webrick is ready, open up a browser and visit
http://localhost:3000/store
Eventually, the page will be rendered. If you don’t have any products, you’ll need to use the admin pages to add some. Once you have some, press the "Add to Cart" button next to one of your products.
In about 10-20 seconds, the Komodo debugger should be stopped in the controller method. Now you can step into or over the few lines of code at work, to see how Rails works, and where your code might be failing. You can use the Self tab to see all the different instance and class variables that contain most of the data for the application.
Following rendering is harder, as the calls to the render action are intertwined with different blocks and yield statements. I recommend setting a breakpoint at the definition of render in ruby-install-dir/gems/1.8/gems/actionpack-1.9.1/lib/action_view/base.rb and following the code from there. A breakpoint in the same file at rhtml_render might also help.
In my own experience I found it easier to convert my rhtml templates into Ruby files by running a command like
erb -x .../rails/depot/app/views/store/display_cart.rhtml > tmp.rb
and then getting the syntax errors:
ruby -c tmp.rb
Most of the template processing Rails does happens atomically inside eval actions, so you can’t really debug it standalone.
I’ve noticed that the bottleneck happens when Rails finishes up a request. It seems to spend a lot of time using objectspace to clean up variables; this might be an opportunity for a significant optimization, even in production mode.
Finally, you should be able to work through the various components in the depot app, hitting breakpoints in your application.
Troubleshooting
1. The debugger doesn’t even start.
Does the debugger start with a step-into command? Are there any syntax errors in the code that are preventing start-up?
Are you pointing to the correct instance of the Ruby? The Komodo debugger requires Ruby 1.8.0 or higher.
As a sanity check, make sure the code will run outside the debugger.
2. Things don’t seem to work correctly.
The Ruby debugger comes with some basic logging, which you can
invoke in three different ways. Send the log file to support@activestate.com, with an explanation of the problem
Use Preferences|Languages|Ruby|Debugger Logging to specify the location of a file to log to.
3. Logging seems to slow things down.
Definitely, but this is configurable while you’re debugging. When you’re stopped at a breakpoint, you can turn logging on and off by using the backdoor command in the Watch window. To turn logging off, enter this command:
!backdoor @debugger__.instance_eval('@logger.level = Logger::ERROR')
To reinstate logging, enter this command:
!backdoor @debugger__.instance_eval('@logger.level = Logger::DEBUG')
You can delete this line from the Watch window once you’ve invoked it.
Yes, the !backdoor command gives you access to the internals of the Ruby debugger while it’s running. You’ll find it documented in the source code of DB/context.rb:debug_log_eval. Eventually I hope to build a proper backdoor API.
4. You get an ActionController::DoubleRenderError exception.
Rails will invoke the render method only once per action. If you cause the render or redirect methods to be invoked while stopped at a breakpoint, it will cause the @performed_render or @performed_redirect instance variables to be set, and Rails will throw an exception as it returns out of the controller method.
The most common way to cause this is by moving the cursor over the word redirect or render while stopped at a breakpoint. This might cause Komodo to evaluate the word and put the result up in a hover tip. This is partially a bug — I’ve put in a request for a longer delay before the word under the cursor is sent to the debugger engine for evaluation. But it’s also an artifact of languages where parentheses or some other notation isn’t needed to invoke a function. The debugger engine has no way of knowing whether the word ‘foo’ is bound to a variable or a function (and in Ruby it can be both simultanteously).
If this happens, bring up the Self tab in the variables window, and check the values of @performed_redirect and @performed_render. If either of these is true (value of "1"), double-click on the name of the variable, and enter an Expression of false.
5. The debugger seems to be hanging.
Press the pause button in the IDE, and bring up the call-stack view in the Debug tab. You’ll probably see that Ruby is deep inside a Rails library. You can repeatedly Step Out at this point, and get an idea of how Rails works, or press Continue and wait for it to reach the controller.
Community
We’d like to hear from you. For community discussion, the best spot is the Komodo beta mailing list, info at http://listserv.activestate.com/mailman/listinfo/komodo-beta. If you found a bug, please report it at http://bugs.activestate.com/Komodo. Finally, you can enter the support queue with an email to support@activestate.com.
I just realized I had forgotten support for Unicode strings.
A good proverbial exercise for the reader is to add it.
Hint: you need to add two characters to pythonlex.udl