CodeBuddy: See your Ruby stack come alive!

Alex Rothenberg, Daniel Higginbotham and I just published a new gem called CodeBuddy that displays a Ruby stack trace inside your browser like this:

The idea is that you can see both the stack trace and code snippets from the corresponding files at the same time, to make it easier to understand what was going on. You can move up and down the stack just by pressing the up/down arrow keys, or by clicking on a line in the stack trace:

As you move, the code snippet in the center of the screen will update, showing you the path the Ruby interpreter took through your application’s code.

Use it in your Rails app

If you’re working on a Rails application, then just add the “code_buddy” gem to your Gemfile like this - to avoid security issues CodeBuddy will only function in the development environment:

group :development do 
  gem "code_buddy" 
end 

And then install it using Bundler:

$ bundle install

Now whenever an exception occurs in your Rails app, the Rails exception page will appear as usual, but each file in the stack trace will now appear as a link:

When you click any of these links CodeBuddy will appear, displaying code from the selected file.

If you’re using Rails 2.3.x, you can use CodeBuddy by adding these two lines to your config/environments/development.rb file:

config.gem 'code_buddy' 
config.middleware.use "CodeBuddy::ShowApp"

Run it as a Sinatra app

If you’re working on a Ruby project that is not a Rails application, or if you want to look at a stack trace generated by “puts caller” or in some other way you can run CodeBuddy as a standalone Sinatra app, like this:

$ gem install code_buddy
$ code_buddy
  == Sinatra/1.1.0 has taken the stage on 4567 for development with backup from...

Now if you open http://localhost:4567 you’ll get a form like this:

Here you can copy/paste a Ruby stack from your console or elsewhere and click “submit” to see it inside CodeBuddy. As long as the file paths in the stack can be found on your machine they will appear properly in CodeBuddy. You can even paste a stack containing relative paths, as long as you run the code_buddy command from the directory the paths are relative to.

This form is also available when CodeBuddy is running inside your Rails app if you click the “Paste Stack” link near the top right corner. By the way, the reason we disabled CodeBuddy outside of the development environment is that this form would be a huge, gaping security hole if CodeBuddy were active in the production or other server environment. This is because you can type any file path and line number you’d like into the form, and CodeBuddy will display the contents of the specified files. This is definitely not something you want to make available to anonymous users on the Internet in production! In the future we may provide options for running CodeBuddy in non-development environments, such as on an integration or staging server, if you’re sure it won’t present a security risk.

Detailed example

Let’s create a new Rails 3 app together, and try out CodeBuddy. I’ll create a new application from scratch called “code_buddy_example:”

$ rails new code_buddy_example
      create  
      create  README
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb

... etc ...

$ cd code_buddy_example/

And let’s go ahead and add CodeBuddy to the development environment right away in the Gemfile:

source 'http://rubygems.org' 
 
gem 'rails', '3.0.3' 
group :development do 
  gem 'code_buddy' 
end 
 
etc...

And now I’ll install it using bundler:

$ bundle install
Fetching source index for http://rubygems.org/
Using rake (0.8.7) 
Using abstract (1.0.0) 
Using activesupport (3.0.3) 
Using builder (2.1.2) 

... etc ...

Using coderay (0.9.6)
Using json_pure (1.4.6)
Using tilt (1.1)
Using sinatra (1.1.0)
Installing code_buddy (0.0.6)
Using thor (0.14.6) 
Using railties (3.0.3) 

... etc ...

Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem...

Your bundle was installed to `/Users/pat/.rvm/gems/ruby-1.8.7-p302`

You can see the CodeBuddy gem was installed, along with a few others CodeBuddy depends on like Sinatra and Coderay. We use Coderay to provide the Ruby syntax color highlighting.

Now before I go any farther, let’s take a look at the Rack middleware stack:

$ rake middleware
(in /Users/pat/rails-apps/code_buddy_example)
use ActionDispatch::Static
use Rack::Lock
use ActiveSupport::Cache::Strategy::LocalCache
use Rack::Runtime
use Rails::Rack::Logger
use CodeBuddy::ShowApp
use CodeBuddy::ShowExceptions
use ActionDispatch::RemoteIp
use Rack::Sendfile
use ActionDispatch::Callbacks
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::MethodOverride
use ActionDispatch::Head
use ActionDispatch::BestStandardsSupport
run CodeBuddyExample::Application.routes

You’ll notice CodeBuddy appears twice. First a Rack middleware component called “CodeBuddy::ShowApp” was added by the gem. This displays the CodeBuddy page whenever the user accesses a URL matching “/code_buddy”. Behind the scenes, CodeBuddy::ShowApp actually runs the Sinatra app I mentioned above, right inside your Rails application. Kind of cool!

Second, you can see another Rack middleware component called “CodeBuddy::ShowExceptions.” CodeBuddy replaces the standard Rails ActionDispatch::ShowExceptions middleware component with a modified version, using “middleware.swap.” ActionDispatch::ShowExceptions is what displays the Rails exception pages, and the CodeBuddy version is identical, except that each line in the stack trace is displayed as a link to a “/code_buddy/... ” URL.

Now let’s finish our silly sample app by generating a controller to display a default home page:

$ rails g controller welcome
      create  app/controllers/welcome_controller.rb
      invoke  erb
      create    app/views/welcome
      invoke  test_unit
      create    test/functional/welcome_controller_test.rb
      invoke  helper
      create    app/helpers/welcome_helper.rb
      invoke    test_unit
      create      test/unit/helpers/welcome_helper_test.rb
etc...    

I’ll edit app/controllers/welcome_controller.rb and add some code to immediately throw an exception:

class WelcomeController < ApplicationController 
  def index 
    raise 'exception' 
  end 
end

Now I’ll set this as my home page in config/routes.rb:

root :to => "welcome#index"

And finally I’ll delete my static home page:

$ rm public/index.html

Now if I run my server and access http://localhost:3000 I’ll immediately run into the exception... and clicking any of the links in the stack trace will take me to CodeBuddy!

It’s actually quite interesting to follow the stack down from the welcome controller’s action all the way through the rack middleware stack... all the way to the script/server command. Using Webrick from my laptop’s command line I get 82 different levels in my stack trace! Amazing - who knew so much code was executed before the Ruby interpreter ever reached my application!

Feedback welcome

Alex, Daniel and I would love to hear if the gem works for you, and if you have any suggestions - what else could we show in CodeBuddy? How else could it be used? What features is it missing?