Skip to content

Instantly share code, notes, and snippets.

@shitpoet
Created December 8, 2015 13:30
Show Gist options
  • Save shitpoet/fd2fcff88564c1a03b63 to your computer and use it in GitHub Desktop.
Save shitpoet/fd2fcff88564c1a03b63 to your computer and use it in GitHub Desktop.
Script to automatically generate migrations for Ruby on Rails, based on changes in schema.rb file
#!/usr/bin/env ruby
require 'fileutils'
help = view = gen = apply = edit = del = restore = false
letters = ''
ARGV.each do |arg|
if arg[1] == '-'
help ||= arg == '--help'
view ||= arg == '--view'
gen ||= arg == '--generate'
apply ||= arg == '--apply'
edit ||= arg == '--edit'
del ||= arg == '--delete'
restore ||= arg == '--restore'
else
letters += arg[1..999]
end
end
letters.chars.each do |arg|
arg = '-'+arg
help ||= (arg == '-h') || (arg == '-?')
view ||= arg == '-v'
gen ||= arg == '-g'
apply ||= arg == '-a'
edit ||= arg == '-e'
del ||= arg == '-d'
restore ||= arg == '-r'
end
puts 'view' if view
puts 'generate' if gen
puts 'apply' if apply
puts 'edit' if edit
puts 'delete' if del
puts 'restore' if restore
def parse_tabs(fn)
tab_lines = []
line_num = 0
text = File.open(fn).read
#text.gsub!(/\r\n?/, "\n")
tab_name = ''
text.each_line do |line|
if /create_table/ =~ line
m = (/^.*create_table \"(.*)\".*$/.match line )
tab_name = m[1]
puts "parsed tab #{tab_name} @ #{line_num}"
end
tab_lines[line_num] = tab_name
#print "#{tab_name} #{line_num} #{line}"
line_num += 1
end
tab_lines
end
def last_mig_file
Dir.chdir File.expand_path 'db/migrate'
migfiles = Dir.glob('*.rb')
lastfile = nil
lasttime = Time.new(1999,01,01)
migfiles.each do |migfile|
f = File.open(migfile)
#puts 'seach last mig... '+f.path+' '+f.mtime.to_s
if f.mtime > lasttime
lastfile = f
lasttime = f.mtime
end
end
Dir.chdir File.expand_path '../..'
if lastfile
#puts 'found last mig: '+lastfile.path+' '+lasttime.to_s
'./db/migrate/'+lastfile.path
else
''
end
end
# f = last_mig_file
#class ChangeOrderOfProductChars < ActiveRecord::Migration
# def change
# change_column :iproduct_chars, :order, :integer, null: true
# end
#end
if help
puts 'north.rb <[email protected]>'
puts ' -g, --generate : generate migrations'
puts ' -a, --apply : apply generated migrations'
puts ' -v, --view : view last migration'
puts ' -e, --edit : edit last migration'
puts ' -d, --delete : delete last migration'
puts ' -r, --restore : restore schema.rb (for testing)'
end
if gen
puts 'copy schema'
FileUtils.cp File.expand_path('db/schema.rb'), File.expand_path('db/schema_north.rb')
`rake db:schema:dump`
tab_lines = parse_tabs('db/schema.rb')
#puts tab_lines
puts 'generate migrations'
migrations = []
diff = `diff -bBE db/schema.rb db/schema_north.rb`
puts ''
puts diff
puts ''
tab_name = ''
line_num = 0
diff.each_line do |line|
ss = line.split
if ss.length >= 2
s = ss[2]
field = (/.*['\"](.*)['\"].*/.match s)
if not field.nil?
field = field[1]
s = ss[1].strip
type_name = s[2..s.length-1]
sss = line.split ','
field_opts = sss[1..sss.length-1].join(',').strip
else
field = ''
type_name = ''
field_opts = ''
end
end
if line[0] == '<' and field.to_s != ''and tab_name.to_s != ''
#puts "del field #{field}"
migrations.push({action: 'remove', line_num: line_num, tab_name: tab_name, field: field, type_name: type_name, field_opts: field_opts})
puts "migration remove_#{field}_to_#{tab_name} #{field}:#{type_name}"
elsif line[0] == '>' and field.to_s != '' and tab_name.to_s != ''
#puts "add field #{field} type #{type_name}"
migrations.push({action: 'add', line_num: line_num, tab_name: tab_name, field: field, type_name: type_name, field_opts: field_opts})
puts "migration add_#{field}_to_#{tab_name} #{field}:#{type_name}, #{field_opts}"
elsif line[0] != '-'
line_num = line.to_i
tab_name = tab_lines[line_num]
puts "at #{line_num} tab #{tab_name} "
end
end
# generate change commands from remove+add
migrations.each do |m|
migrations.each do |m2|
if (m[:tab_name] == m2[:tab_name]) && (m[:field] == m2[:field])
if (m[:action] == 'add') && (m2[:action] == 'remove')
m[:action] = 'change'
m2[:action] = ''
puts "CHANGE"
elsif (m[:action] == 'remove') && (m2[:action] == 'add')
m2[:action] = 'change'
m[:action] = ''
puts "CHANGE"
end
elsif m[:line_num] == m2[:line_num] && m[:field] != m2[:field] && m[:type_name] == m2[:type_name] && m[:field_opts] == m2[:field_opts]
if (m[:action] == 'add') && (m2[:action] == 'remove')
m[:action] = ''
m2[:action] = 'rename'
m2[:new_field] = m[:field]
puts "RENAME"
elsif (m[:action] == 'remove') && (m2[:action] == 'add')
m2[:action] = ''
m[:action] = 'rename'
m[:new_field] = m2[:field]
puts "RENAME"
end
end
end
end
# generate and apply commands
migrations.each do |m|
a = m[:action]
tab_name = m[:tab_name]
field = m[:field]
type_name = m[:type_name]
field_opts = m[:field_opts]
if a == 'add'
code = "rails g migration add_#{field}_to_#{tab_name} #{field}:#{type_name}"
puts code
if gen
system(code)
if field_opts != ''
fn = last_mig_file
first_line = ''
File.open(fn, "r").each_line do |line|
first_line = line
break
end
File.open(fn, "w") do |file|
file.write(first_line)
file.write(" def change\n")
if field_opts == ''
file.write(" add_column :#{tab_name}, :#{field}, :#{type_name}\n")
else
file.write(" add_column :#{tab_name}, :#{field}, :#{type_name}, #{field_opts}\n")
end
file.write(" end\n")
file.write("end\n")
end
end
end
elsif a == 'remove'
code = "rails g migration remove_#{field}_from_#{tab_name} #{field}:#{type_name}"
puts code
system(code) if gen
elsif a == 'change'
code = "rails g migration change_#{field}_in_#{tab_name} #{field}:#{type_name}"
puts code
if gen
system(code)
fn = last_mig_file
first_line = ''
File.open(fn, "r").each_line do |line|
first_line = line
break
end
File.open(fn, "w") do |file|
file.write(first_line)
file.write(" def change\n")
if field_opts == ''
file.write(" change_column :#{tab_name}, :#{field}, :#{type_name}\n")
else
file.write(" change_column :#{tab_name}, :#{field}, :#{type_name}, #{field_opts}\n")
end
file.write(" end\n")
file.write("end\n")
end
end
elsif a == 'rename'
new_field = m[:new_field]
code = "rails g migration rename_#{field}_of_#{tab_name} #{new_field}:#{type_name}"
puts code
if gen
system(code)
fn = last_mig_file
first_line = ''
File.open(fn, "r").each_line do |line|
first_line = line
break
end
File.open(fn, "w") do |file|
file.write(first_line)
file.write(" def change\n")
file.write(" rename_column :#{tab_name}, :#{field}, :#{new_field}\n")
file.write(" end\n")
file.write("end\n")
end
end
end
end
end
if view
puts 'view'
lastfile = last_mig_file
system('cat', lastfile)
end
if edit
puts 'edit'
lastfile = last_mig_file
system('editor', lastfile)
end
if del
puts 'delete'
lastfile = last_mig_file
system('rm', lastfile)
end
if restore
# restore schema
puts 'restore schema'
FileUtils.cp File.expand_path('db/schema_north.rb'), File.expand_path('db/schema.rb')
end
if apply
# rake db:migrate here
puts 'apply (not implemented)'
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment