I have a model with UUIDs stored in BINARY(16) field in a MySQL table. I would like to be able to transparently convert hexadecimal uuid into binary for the setter method and back when I use the getter method.
what is the 'right' way to proceed?
I have a model with UUIDs stored in BINARY(16) field in a MySQL table. I would like to be able to transparently convert hexadecimal uuid into binary for the setter method and back when I use the getter method.
what is the 'right' way to proceed?
I looked at the source code for ActiveRecord 2.3.5 (mysql_adapter.rb). Looking at the NATIVE_DATABASE_TYPES hash makes it clear that it does not support the BINARY(16) data type:
NATIVE_DATABASE_TYPES = {
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
:string => { :name => "varchar", :limit => 255 },
:text => { :name => "text" },
:integer => { :name => "int", :limit => 4 },
:float => { :name => "float" },
:decimal => { :name => "decimal" },
:datetime => { :name => "datetime" },
:timestamp => { :name => "datetime" },
:time => { :name => "time" },
:date => { :name => "date" },
:binary => { :name => "blob" },
:boolean => { :name => "tinyint", :limit => 1 }
}
Also note that :binary is not what you want, because that creates a BLOB column.
If you have the inclination, I would recommend extending ActiveRecord to support the BINARY(16) type.
Update: after some searching the following blog post by Matthew Higgins seems to be promising ("While arbitrary SQL statements can be executed in migrations, an alternative is to extend the MySql adapter to support new types of columns."): http://www.strictlyuntyped.com/2008/07/mysql-lovin-part-2-adding-new-column.html
If you get this to work, I would hope that you share what you come up with. Like Matthew, I would like to see ActiveRecord have a cleaner API for adding column types.
You override the setter and getter:
class User < ActiveRecord::Base
def uuid=(value)
@uuid = write_attribute(:uuid, value.scan(/../).map {|n| n.to_i(16)}.pack("C*"))
end
def uuid
@uuid ||= read_attribute(:uuid).unpack("C*").map {|n| sprintf("%02x", n)}.join
end
end
Of course, you'd want a BINARY column, because you're sending raw bytes to the DB. A migration such as this one:
class AddUuidToUsers
def self.up
execute "ALTER TABLE users ADD uuid BINARY(16)"
end
end