How to remotely debug Rails in Apache + Passenger + RubyMine

I’m now a Ruby on Rails programmer and the past week I’ve been frantically learning how to develop on my MacBook Air.

My configuration

  1. Mac OS X Mountain Lion
  2. Xcode 4.4
  3. Apache 2.2.22
  4. MySQL 5.5.27
  5. Phusion Passenger 3.0.15
  6. Ruby 1.8.7
  7. Rails 2.3.12

My IDE

I wanted an IDE like Zend Studio, whose remote debugging support for PHP I simply love.

After some research I bought a license for RubyMine 4.5, because I was impressed by its integration with Ruby and Rails and its excellent debugger.

Unfortunately, RubyMine’s offers remote debugging configurations that (at the moment) don’t allow to hook into a running instance. That means that you can’t remotely debug on your own application stack, unless your Rails server is run by a command line script.

Well… luckily enough, I’ve just found another way that allows me to remotely debug using my own stack.
In fact, RubyMine’s local debugger does support Passenger Standalone as a Rails server, through a command line like this:

/usr/bin/ruby /usr/bin/passenger start -a 0.0.0.0 -p 3000 --max-pool-size 1 --spawn-method conservative -e development

in conjunction with the following file (config/initializers/rubymine_passenger_debug.rb)

# generated by RubyMine
debug_port = ENV['RUBYMINE_DEBUG_PORT']

if debug_port
  puts "Preparing to launch debugger for #{$$}"
  $:.push(*ENV['RUBYLIB'].split(":"))
  require 'ruby-debug-ide'

  Debugger.cli_debug = ("true" == ENV['RUBYMINE_DEBUG_VERBOSE'])
  Debugger.start_server('127.0.0.1', debug_port.to_i)
end

And it works like a charm.

My method

This example is based on the Notebook app created in the RubyMine tutorial Discover RubyMine in a Few Simple Steps.

(1/6) Edit /etc/hosts

I make an alias in my Mac such that notebook.dev corresponds to 127.0.0.1 (localhost).

#...
127.0.0.1    notebook.dev

(2/6) Edit …/apache2/extra/httpd-vhosts.conf

I make a virtual host for my “remote” development site. Note that the notebook folder is under Sites. On the other hand, my “local” development site will be under RubymineProjects. To synch them you need to deploy, i.e. delete the notebook folder in Sites and copy again there the one in RubymineProjects.

#...
NameVirtualHost *:80

<VirtualHost *:80>
    # this is the default entry !!!
    ServerName localhost
</VirtualHost>

<VirtualHost *:80>
    ServerName notebook.dev
    ServerAlias www.notebook.dev
    
    ServerAdmin cappuccino.e.cornetto@gmail.com
    ErrorLog "/private/var/log/apache2/notebook.dev-error_log"
    CustomLog "/private/var/log/apache2/notebook.dev-access_log" common

    DocumentRoot "/Users/andrea/Sites/notebook/public"
    <Directory "/Users/andrea/Sites/notebook/public">
        # This relaxes Apache security settings.
        AllowOverride all
        # MultiViews must be turned off.
        Options -MultiViews
    </Directory>
</VirtualHost>

(3/6) Edit …/apache2/httpd.conf

This is where Passenger is configured into Apache. I configure my instance like RubyMine does when it starts Passenger Standalone for local debugging. This is a minor drawback… I think I can live with it + remote debugging.

#...
LoadModule passenger_module /Library/Ruby/Gems/1.8/gems/passenger-3.0.15/ext/apache2/mod_passenger.so
   PassengerRoot /Library/Ruby/Gems/1.8/gems/passenger-3.0.15
   PassengerRuby /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby
   
   # when RubyMine executes Passenger Standalone for debugging, these are the settings...
   PassengerMaxPoolSize 1
   PassengerSpawnMethod conservative
   SetEnv RAILS_ENV development

#...

(4/6) Create …/notebook/lib/tasks/rdebug.rake

I create a simple rake task to start the server with debugging enabled.

task :rdebug do
  system("touch tmp/restart.txt")
  system("touch tmp/debug.txt")
end

(5/6) Add RubyMine – Run/Debug Configurations – Ruby remote debug

I add a new Ruby remote debug configuration. Remote host is the IP of my “remote” development site. The remote port can be any that is not already used. The local port is decided by the dialog box, so you’ll have a different number each time you add a new configuration. Remote and local root folders must match the same app tree such that the mapping works when debugging.

(6/6) Edit …/notebook/config/environments/development.rb

I add this block at the end of my development configuration. It is the keystone. Note that remote_host and remote_port must match the corresponding values in the Ruby remote debug configuration.

#...
remote_host = '127.0.0.1'
remote_port = 7000

debug_flag = File.join(RAILS_ROOT, 'tmp', 'debug.txt')
if File.exists?(debug_flag)
  
  require 'ruby-debug-ide'
  Debugger.start_server(remote_host, remote_port)

  File.delete(debug_flag)
end

Debug sequence

This is the sequence I use for remote debugging. Apart from the first step, the rest is the same used for local debugging.

  1. From a Terminal window: rake rdebug
  2. From RubyMine – (notebook) Debug
  3. From the browser – Reload a page and hit a breakpoint
  4. RubyMine gets the focus and waits for you to debug

And it works like a charm.

Is Rails clever or smart?

When generating a Rails project you can specify the database adapter. If you select mysql, then Rails 3.0.0 translates mysql to mysql2, which is (or should be) the current adapter for MySQL in Ruby.

I smell a hack here, like a solution belonging elsewhere.

Problem: A new MySQL adapter is available, so how do we make Rails compatible with it?

Clever solution: We translate mysql to mysql2, so that any time a user requests the old mysql adapter, the new mysql2 adapter will be used instead.

Smart solution: We add mysql2 alongside mysql, so that Rails users will decide which adapter they want.

I think that Rails implemented the clever solution because there are Rails developers that write applications in IDEs, which are released infrequently. Those developers would have to wait a lot to get the new mysql2 adapter accessible from their IDEs. So, a transparent translation is very effective in this case, because an IDE dialog for generating a project will execute the “rails new” command under the hood, requesting the mysql adapter. The problem is that there’s no way to use the old mysql adapter, if a Rails developer really wants to, except for hacking Rails itself.

Anyway, I think that Rails could have been smarter. When the user issues the “rails new” command, these are the possible combinations:

  1. mysql2 is installed but mysql is not
    If the user specifies “–database=mysql”, Rails should inform: “using available mysql2 adapter instead of specified mysql”, thus translating mysql to mysql2. This takes care of the fact that mysql2 is meant to replace mysql.
    If the user specifies “–database=mysql2”, Rails should use the specified adapter.
  2. mysql is installed but mysql2 is not
    If the user specifies “–database=mysql”, Rails should use the specified adapter.
    If the user specifies “–database=mysql2”, this should be considered an error and Rails should stop and inform the user. This takes care of the fact that mysql2 has been released after mysql.
  3. neither mysql2 nor mysql are installed
    If the user specifies “–database=mysql” or “–database=mysql2”, this is an error and Rails should stop and inform the user.
  4. both mysql2 and mysql are installed
    If the user specifies “–database=mysql” or “–database=mysql2”, Rails should use the specified adapter.

Browsing Rails code, I see that they were implementing mysql2 alongside mysql, but eventually translated the latter to the former.

NetBeans 6.9.1 + Ruby 1.9.2 + Rails 3.0.0 + Debugging

This fix is for anyone that has NetBeans 6.9.1, Ruby 1.9.2, Rails 3.0.0, already installed, and decides to add support for debugging on Windows XP.

That was my configuration, but it didn’t work properly. A debug session always ended with an error about a non existing script/rails file. After some research, today I discovered this fix that eventually allows me to cleanly debug a Ruby on Rails 3.0.0 project.

ONCE
  1. Download DevKit 4.5.0 from http://rubyinstaller.org/downloads/
  2. Install DevKit as described at http://github.com/oneclick/rubyinstaller/wiki/Development-Kit
  3. Enter the command
  4. Enter the command
  5. Edit the file (Ruby folder)/lib/ruby/gems/1.9.1/gems/ruby-debug-ide19-0.4.12/lib/ruby-debug-ide.rb as follows This one isn’t required, but I find wrong labels very distracting
  6. Edit the file (Ruby folder)/lib/ruby/gems/1.9.1/gems/ruby-debug-ide19-0.4.12/bin/rdebug-ide.rb as follows See also: http://netbeans.org/projects/ruby/lists/users/archive/2010-09/message/25
PER PROJECT
  1. Edit the file (Project folder)/Gemfile as follows
  2. delete the file (Project folder)/Gemfile.lock
  3. Enter the command

That’s all. I hope it’ll work for you too 🙂