Fixing “uninitialized constant MysqlCompat::MysqlRes”

This is an old problem that affects an old Ruby gem: mysql (2.8.1). Apparently, it has a little bug somewhere that makes it look for a file in the wrong folder. I get this error always after installing a new app. The last time has been some minutes ago, after installing TracksApp 2.1, which is based on the old Rails 2.3.

The MySQL page about Ruby adapters speaks about two of them. One called MySQL/Ruby, and the other (after a huge name creation effort)… Ruby/MySQL. They are respectively provided by the gems mysql (2.8.1) and ruby-mysql (2.9.10). As you may have noticed, the first gem is the old version of the second. That is also confirmed by the dates they got updated, which is: 2.8.1, on 2009-08-21 and 2.9.10, on 2012-07-12.

Both adapters are authored by Tomita Masahiro. I do appreciate the effort he did, and his generosity in making them available to me, no doubt about that. But in my opinion, the name change was a mistake. I suppose he did it to tell the world they were two very different adapters (the old one C-based and the new one Ruby-based). Nonetheless, they really were two subsequent versions of the same thing.

The problem with TracksApp is exactly that. If Tomita Masahiro had stuck to the mysql name, then gem “mysql” in TracksApps would have referenced the latest version (what today is instead ruby-mysql) and all would have worked fine. But now, even with ruby-mysql installed, that gem requirement in TracksApp makes my system install the old mysql gem. That in turn makes Rails prefer mysql over ruby-mysql (why? lexical order?) and due to the known bug all my apps break.

My solution is to manually uninstall mysql each time it gets installed by some app. In fact I don’t know how to make my global Rails environment ignore mysql (2.8.1) even if some app requires it. I’ve tried with “bundle install –without mysql”, but mysql is a gem, not a group, so it does not work… My fix works because ruby-mysql perfectly replaces mysql and it’s just a drop-in, sharing the same adapter name (mysql).

Clearly, TracksApp is a new version of an old software, even if it is still based on Rails 2.3. If I was developing it, I’d have updated the Gemfile…

 

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.