Enum patch for Rails


#1

Hello!

I’ve modified ActiveRecord a bit to support enum columns. This is tested
only with MySQL, and maybe needs some changes for other adapters (if
they use another syntax or quoting style).

I believe this patch should be tested well, so I haven’t (yet) tried to
post it to the official Trac. It is also not solid-as-a-rock when it
comes to quoting ENUM values.

After applying this thing, rake db_schema_dump properly handles enum
columns, and also it’s possible to write migrations like this:

add_column :users, :level, :enum, :limit => [‘viewer’, ‘author’,
‘admin’]

Observe that I’m (over)using the limit field to specify ENUM values.

Here’s the patch.

Index:
activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb


activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
(revision 3486)
+++
activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
(working copy)
@@ -235,7 +235,15 @@

   def type_to_sql(type, limit = nil) #:nodoc:
     native = native_database_types[type]
  •    limit ||= native[:limit]
    
  •    # this is a special case, because data type differs
    
  •    case type
    
  •      when :enum
    
  •        fail ":enum column limit must be specified" if limit.nil?
    
  •        fail ":enum column limit must be an array" unless 
    

limit.is_a? Array

  •        limit = limit.collect {|n| "'#{n}'"}.join ","
    
  •      else
    
  •        limit ||= native[:limit]
    
  •    end
       column_type_sql = native[:name]
       column_type_sql << "(#{limit})" if limit
       column_type_sql
    

Index:
activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb


activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
(revision 3486)
+++
activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
(working copy)
@@ -18,7 +18,7 @@
@sql_type = sql_type
# have to do this one separately because type_cast depends on
#type
@default = type_cast(default)

  •    @limit   = extract_limit(sql_type) unless sql_type.nil?
    
  •    @limit   = extract_limit_or_enum_values(sql_type) unless 
    

sql_type.nil?
@primary = nil
@text = [:string, :text].include? @type
@number = [:float, :integer].include? @type
@@ -44,6 +44,7 @@
when :text, :string then String
when :binary then String
when :boolean then Object

  •      when :enum          then Symbol
       end
     end
    

@@ -61,6 +62,7 @@
when :date then self.class.string_to_date(value)
when :binary then self.class.binary_to_string(value)
when :boolean then self.class.value_to_boolean(value)

  •      when :enum      then value.intern
         else value
       end
     end
    

@@ -77,6 +79,7 @@
when :date then
“#{self.class.name}.string_to_date(#{var_name})”
when :binary then
“#{self.class.name}.binary_to_string(#{var_name})”
when :boolean then
“#{self.class.name}.value_to_boolean(#{var_name})”

  •      when :enum      then "#{var_name}.to_s.intern"
         else nil
       end
     end
    

@@ -135,6 +138,26 @@
$1.to_i if sql_type =~ /((.*))/
end

  •    def extract_enum_values(sql_type)
    
  •      paren_string = $1 if sql_type =~ /\((.*)\)/
    
  •      return [] if paren_string.nil?
    
  •      values = []
    
  •      paren_string.split(",").each do |item|
    
  •        item.strip!
    
  •        item = item[1..-2] if item[0] == ?' && item[-1] == ?'  # 
    

remove quoting

  •        values << item
    
  •      end
    
  •      values
    
  •    end
    
  •    def extract_limit_or_enum_values(sql_type)
    
  •      if type == :enum
    
  •        extract_enum_values(sql_type)
    
  •      else
    
  •        extract_limit(sql_type)
    
  •      end
    
  •    end
    
  •    def simplified_type(field_type)
         case field_type
           when /int/i
    

@@ -157,6 +180,8 @@
:string
when /boolean/i
:boolean

  •        when /enum/i
    
  •          :enum
         end
       end
    
    end
    Index:
    activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
    ===================================================================
    — activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
    (revision 3486)
    +++ activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
    (working copy)
    @@ -107,7 +107,8 @@
    :time => { :name => “time” },
    :date => { :name => “date” },
    :binary => { :name => “blob” },
  •      :boolean     => { :name => "tinyint", :limit => 1 }
    
  •      :boolean     => { :name => "tinyint", :limit => 1 },
    
  •      :enum        => { :name => "enum", :limit => [] }
       }
     end