Created
December 8, 2015 13:30
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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