define_method and ruby 1.8

The following code is not thread-safe (in Ruby 1.8):

def quote_string(s)
  self.class.instance_eval do
    define_method(:quote_string) do |s|
      sleep 0.1
      s
    end
  end
  quote_string(s)
end

The reason is block argument s is bound to method’s argument. When new method is called all threads share the same variable, so if you call this method in several threads, it wouldn’t return argument anymore.

Test script is here. When it’s run, it outputs

ruby 1.8.8dev (2009-09-14) [i386-darwin10.0.0]
1 fail [“another test”, “another test”]
2 fail [“another test”, “test”]

ActiveRecord has the following code

# Quotes strings for use in SQL input in the postgres driver for better performance.
def quote_string(original_value) #:nodoc:
   if @connection.respond_to?(:escape)
     self.class.instance_eval do
       define_method(:quote_string) do |s|
         @connection.escape(s)
       end
     end
   elsif PGconn.respond_to?(:escape)
     self.class.instance_eval do
       define_method(:quote_string) do |s|
         PGconn.escape(s)
       end
     end
   else
     # There are some incorrectly compiled postgres drivers out there
     # that don’t define PGconn.escape.
     self.class.instance_eval do
       remove_method(:quote_string)
     end
   end
   quote_string(original_value)
end

So if you use ActiveRecord with postgres and ruby 1.8, make sure do not use threads ;-)