Sunday, February 05, 2006

Rails MySQL Enum Patch

Andrey Tarantsov posted a patch on the Ruby on Rails list that can help you with adding Enum functionality to Rails. Enum functionality is not added by default.

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


--

6 Comments:

At 4:33 AM, Blogger Unknown said...

If you wonder, I've turned my changes into a plugin. Here it is:

svn://andreyvit.firstvds.ru/webdevel/rails/plugins/enum_columns/trunk

 
At 11:13 PM, Blogger dubek said...

Check out the enum-column plugin on RubyForge.

dubek.

 
At 4:27 PM, Blogger rutvij said...

dude... kindly mention the version of rails for which you have made a patch...

 
At 4:31 PM, Blogger rutvij said...

dude... kindly mention the version of rails for which you have made a patch...

 
At 4:31 PM, Blogger rutvij said...

dude... kindly mention the version of rails for which you have made a patch...

 
At 3:45 AM, Blogger Tejuteju said...

very informative blog and useful article thank you for sharing with us, keep posting Ruby on Rails Online Training Bangalore

 

Post a Comment

<< Home

eXTReMe Tracker