背景
ruby2.1とrails4を使ってアプリを作っている。DBはMySQLを使っていて、わけあってあるテーブルの列をdouble型にしたいと思って。 カラムの列を変更するmigrationを作ろうと思った時に、そもそもdouble型のカラムを作るにはどうするんだっけ、ってなって、 Webを調べてみて、解がみつかったので試したところ、ちょっと不都合が生じて、それじゃあモンキーパッチを書いてみよう、という話です。
MySQLのdouble型を利用するmigration
まず、MySQLのdouble型の列を使うには、
add_column :table_name, :column, :float, limit: 53のようなmigrationを書くとできる。(これは、既存のテーブルにカラムを追加するmigration)
型に:floatを指定して、:limitに53を指定する。参考にしたのは、
- What is Rails' migration equivalent to MySQL's 'double' datatype? - Stack Overflow
- Rails Migrations and MySQL Types
問題点
上述のmigrationでカラムを追加すると、確かにdouble型のカラムになる。が、問題点が1つ。bundle exec rake db:migrateした時に、db/schema.rbが更新されるんだけど、このファイル内の当該カラムには、limit: 53が指定されない。結果、bundle exec rake db:test:prepareで、テスト環境のスキーマを更新した時に、当該カラムの型はfloatとして作成されてしまい、期待どおりにデータが扱えなくなってしまう。
ついでにいうと、本来double型を使いたいのに、:float, limit: 53って書くのもの、ちょっと違和感ありますね。
モンキーパッチ
db/schema.rbを手で直すってのも、あれなんで、なんとかならないものか、思って調べ始めたら、次のようなページを発見。
このページのハックを適用すると、#add_column :table_name, :column, :float, limit: 53 # が↓のように書けるようになる。 add_column :table_name, :column, :doubleとなり、問題点の2つ目に対する解になりそう。が、db/schema.rbの問題は解決しないなぁ。もうちょっと調べるか、と思ってrailsのソースをみてたら、ActiveRecord::ConnectionAdapters::Columnというクラスのextract_limit(sql_type)というメソッドをみつけた。このメソッドは、どうやらSQLの型をみて、db/schema.rbを吐き出すときに:limitに何を指定するかを判断するものっぽい。各RDBMS用のアダプターでこれらをオーバーライドしているし。
ということで、次のようなモンキーパッチをconfig/initializers/add_double_to_activerecord.rbとして、作っておくことに。
module CustomColumnTypes def double(*args) if args.last.is_a? Hash args.last[:limit] = 53 args.last[:null] = true end float *args end end module ColumnWithDouble def extract_limit(sql_type) case sql_type when /double/i 53 else $1.to_i if sql_type =~ /\((.*)\)/ end end end module ActiveRecord module ConnectionAdapters class Table include CustomColumnTypes end class TableDefinition include CustomColumnTypes end class Column prepend ColumnWithDouble end end end
これでdb/schema.rbにも無事にlimit: 53が出力されるようになった。
課題
上のパッチでは、ActiveRecord::ConnectionAdapters::Columnに対して振る舞いを変えているんだけど、僕がdoubleをサポートしたいのはMySQLの場合のだけだから、実はうまくない。なので、MySQL用のアダプタをイジる方法に変えるべきだなぁ、と思ってます。
0 件のコメント:
コメントを投稿