Rails is not compatible with databases that support custom data types (e.g. Postgres' ENUM)

(This is adapted from this Stackoverflow question I asked some days ago)

I’m building my first Rails app and I ran into a weird problem. I’m using Postgres, which supports the ENUM type that is very useful for me. Rails doesn’t support this, however, so I’m using many “executes” in my migrations. They all run successfully and work correctly. My database is just the way I want when I examine it through pgAdmin. I’m using 3 ENUM types in the users table at the moment:

gender ('female', 'male')
interested_in ('men', 'women', 'men_and_women')
relationship_status ('single', ..., 'divorced')

For the application side I’m using the classy_enum gem. Then I tried updating the user profile to check if everything was working. At first I erased the interested_in from the edit profile form because it had some different logic for it. Everything worked fine. Then I added interested_in, updated the logic for the new type, and submitted the form again. I got this error:

PG::InvalidTextRepresentation: ERROR: invalid input value for enum interested_in: "0" :
UPDATE "users" SET "interested_in" = $1, "remember_token" = $2, "updated_at" = $3 WHERE "users"."id" = 1

It’s trying to save 0 to an enum field that doesn’t include this option and then it obviously fails. So, the first time I saw a similar error I thought “where on earth is this 0 coming from??” but it turned out Rails was converting the value to an integer before saving it, and whenever you convert a string with non-numeric characters the result is 0. That’s what I supposed was happening again. So I went to the console and typed:

2.0.0p247 :001 > User.columns_hash['interested_in']. type
=> :integer

When I run the same command for gender and relationship_status I get nil, which makes sense cause Rails doesn’t understand the ENUM type:

2.0.0p247 :007 > User.columns_hash['gender']. type
=> nil

In pgAdmin all field types are correct, none is an integer.

I then had a hunch it could have something to do with the name **int**erested_in, which starts with “int”. I then changed the name to is_interested_in but no luck, so I gave up on the hypothesis. After spending some hours puzzled by the problem, dropping my whole database, migrating it again, dropping again, loading it from structure.sql all with no success, I decided to give it a shot again and changed the name of the type to user_interested_in (because that doesn’t even start with “i”). Again, no luck. More hours passed by and since I couldn’t find anything online or come up with any other hypothesis, I checked carefully the names of the other enum types and saw that they didn’t include “int” at all in any position of the string. I then changed the type name from interested_in to attracted_to and guess what? It worked.

User.columns_hash['interested_in']. type
=> nil

My guess was that there was some bug in Rails. It seemed like it was trying to be smart and guessing the column type with some logic like:

model_column_type = :integer if db_column_type.include?("int")

That would be kind of crazy. Why wouldn’t it check for a full match? Didn’t make any sense to me.

But then I checked Rails source and that was it. That’s exactly what it does:

# rails / activerecord / lib / active_record / connection_adapters / column.rb
require 'set'
module ActiveRecord
  # :stopdoc:
  module ConnectionAdapters
    # An abstract definition of a column in a table.
    class Column
      TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].      to_set
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].      to_set
...
      def simplified_type(field_type)
          case field_type
**when /int/****i # <-------------- HERE**
            :          integer
when /float|double/            i
:float
          .
          .
          .
          when /boolean/            i
:boolean
          end
        end
    end
  end

That’s a serious problem for databases that support the creation of types. Does the regex really have to be so inclusive? One thing is not offering support, not offering an interface to creating custom typed fields, but this is not just that. This is being incompatible. Couldn’t the regex be more restrictive?