Skip to content

Instantly share code, notes, and snippets.

@ka2n
Created June 2, 2017 09:31
Show Gist options
  • Save ka2n/93016cd9a34b4ce8ea55e496fdc3385d to your computer and use it in GitHub Desktop.
Save ka2n/93016cd9a34b4ce8ea55e496fdc3385d to your computer and use it in GitHub Desktop.
Rails: update integer enum to string
require 'active_support/core_ext/string'
require 'pp'
current_ts_prefix = '20170602000'
migrations = []
[
{
model: 'Document',
table: 'documents',
column: 'source',
index: [[:user, :source, :slug], unique: true],
default: 'enum1',
null: false,
enums: %w(enum1 enum2)
},
].each.with_index do |conf, i|
str_col_name = "#{conf[:column]}_str"
migrate_case_sql = "CASE #{conf[:enums].map.with_index { |k, i| "WHEN `#{conf[:column]}` = #{i} THEN '#{k}'" }.join(' ')} END"
migrate_case_reverse_sql = "CASE #{conf[:enums].map.with_index { |k, i| "WHEN `#{str_col_name}` = '#{k}' THEN #{i}" }.join(' ')} END"
define_missing_class = "class ::#{conf[:model]} < ActiveRecord::Base; end unless defined? #{conf[:model]}"
# add column
filename = "add_#{str_col_name}_to_#{conf[:table]}"
classname = filename.camelize
migrations << [
"#{current_ts_prefix}0#{i}1_#{filename}.rb", <<-EOS
# #{classname}
# - #{conf[:column]}の文字列版のカラムを#{str_col_name}として追加
class #{classname} < ActiveRecord::Migration[5.0]
def change
add_column :#{conf[:table]}, :#{str_col_name}, :string, default: #{conf[:default].nil? ? 'nil' : "'#{conf[:default]}'"}, null: #{conf[:null] ? 'true' : 'false' }
end
end
EOS
]
# migrate data
filename = "migrate_#{conf[:table]}_#{conf[:column]}_to_#{str_col_name}"
classname = filename.camelize
migrations << [
"#{current_ts_prefix}0#{i}2_#{filename}.rb", <<-EOS
# #{classname}
# - #{conf[:column]}のデータを#{str_col_name}に変換してUPDATEする
class #{classname} < ActiveRecord::Migration[5.0]
#{define_missing_class} # クラスが無い場合があるのでその場で宣言
def up
#{conf[:model]}.update_all("`#{str_col_name}` = (#{migrate_case_sql})")
end
def down
#{conf[:model]}.update_all("`#{conf[:column]}` = (#{migrate_case_reverse_sql})")
end
end
EOS
]
# rename column
filename = "rename_#{conf[:table]}_#{str_col_name}_to_#{conf[:column]}"
classname = filename.camelize
migrations << [
"#{current_ts_prefix}0#{i}3_#{filename}.rb", <<-EOS
# #{classname}
# - integerの#{conf[:column]}を削除して、stringの#{str_col_name}を#{conf[:column]}にリネームする
class #{classname} < ActiveRecord::Migration[5.0]
def up
#{conf[:index].present? && conf[:index][0].length > 1 ? "remove_index :#{conf[:table]}, #{conf[:index][0]}" : '' }
remove_column :#{conf[:table]}, :#{conf[:column]}
rename_column :#{conf[:table]}, :#{str_col_name}, :#{conf[:column]}
#{conf[:index].present? ? "add_index :#{conf[:table]}, #{conf[:index].map(&:to_s).join(', ')}" : '' }
end
def down
#{conf[:index].present? && conf[:index][0].length > 1 ? "remove_index :#{conf[:table]}, #{conf[:index][0]}" : '' }
rename_column :#{conf[:table]}, :#{conf[:column]}, :#{str_col_name}
add_column :#{conf[:table]}, :#{conf[:column]}, :integer, default: #{conf[:default].nil? ? 'nil' : conf[:enums].index(conf[:default])}, null: #{conf[:null] ? 'true' : 'false'}
#{conf[:index].present? ? "add_index :#{conf[:table]}, #{conf[:index].map(&:to_s).join(', ')}" : '' }
end
end
EOS
]
end
migrations.each do |fname, body|
fpath = File.join('db', 'migrate', fname)
File.open(fpath, 'w') { |f| f.write(body) }
pp "#{fpath} created."
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment