# Copyright (c) 2007 Kelly McCauley # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. namespace :migration do desc "Prints out the list of migrations" task :list => :environment do migration_files.each {|f| puts f} end desc "Prints out the commands needed to renumber the given migration. Usage: FROM=number TO=number" task :renumber => :environment do if ENV['FROM'].blank? raise 'No FROM value given. Set FROM=number.' end from_num = ENV['FROM'].to_i if ENV['TO'].blank? raise 'No TO value given. Set TO=number.' end to_num = ENV['TO'].to_i return if from_num == to_num min_num = from_num min_num = to_num if to_num < from_num min_num -= 1 if !current_migrations.key?(from_num) raise "There is no " << sprintf('%03d', from_num) << " migration" end # Create the commands puts 'Cut and paste the following...' puts "----- BEGIN -----\n\n" puts "rake db:migrate VERSION=#{sprintf('%03d', min_num)}" puts "cd #{File.join('db','migrate')}" mv_cmd = 'mv ' svn_mv_cmd = 'svn mv ' if !current_migrations.key?(to_num) # The target migration number is not taken, so just move it. # cmd = '' # if current_migrations[from_num][1] # cmd << svn_mv_cmd # else # cmd << mv_cmd # end # cmd << sprintf('%03d', from_num) << '_' << current_migrations[from_num][0] << ' ' # cmd << sprintf('%03d', to_num) << '_' << current_migrations[from_num][0] # puts cmd print_cmd(to_num, current_migrations[from_num]) elsif from_num > to_num to_num.upto(from_num - 1) do |i| next unless current_migrations.key?(i) # cmd = '' # if current_migrations[i][1] # cmd << svn_mv_cmd # else # cmd << mv_cmd # end # cmd << sprintf('%03d', i) << '_' << current_migrations[i][0] << ' ' # cmd << sprintf('%03d', i + 1) << '_' << current_migrations[i][0] # puts cmd print_cmd(i + 1, current_migrations[i]) end cmd = '' # if current_migrations[from_num][1] # cmd << svn_mv_cmd # else # cmd << mv_cmd # end # cmd << sprintf('%03d', from_num) << '_' << current_migrations[from_num][0] << ' ' # cmd << sprintf('%03d', to_num) << '_' << current_migrations[from_num][0] # puts cmd print_cmd(to_num, current_migrations[from_num]) elsif from_num < to_num (from_num + 1).upto(to_num) do |i| next unless current_migrations.key?(i) # cmd = '' # if current_migrations[i][1] # cmd << svn_mv_cmd # else # cmd << mv_cmd # end # cmd << sprintf('%03d', i) << '_' << current_migrations[i][0] << ' ' # cmd << sprintf('%03d', i - 1) << '_' << current_migrations[i][0] # puts cmd print_cmd(i - 1, current_migrations[i]) end # cmd = '' # if current_migrations[from_num][1] # cmd << svn_mv_cmd # else # cmd << mv_cmd # end # cmd << sprintf('%03d', from_num) << '_' << current_migrations[from_num][0] << ' ' # cmd << sprintf('%03d', to_num) << '_' << current_migrations[from_num][0] # puts cmd print_cmd(to_num, current_migrations[from_num]) end puts "cd #{File.join('..','..')}" puts "rake db:migrate" puts "\n----- END -----" #puts files.join("\n") end desc "Prints out the commands needed to close all gaps in the migration numbering." task :close_gaps => :environment do highest = current_migrations.keys.sort.last idx = 1 # Create the commands cmds = [] first_idx_to_move = nil 1.upto(highest) {|i| if current_migrations.key?(i) if idx != current_migrations[i][2] first_idx_to_move = idx if first_idx_to_move.nil? cmds << fmt_cmd(idx, current_migrations[i]) end idx += 1 end } if first_idx_to_move.nil? puts "No migration numbering gaps to close" else migrate_to_version = first_idx_to_move <= 1 ? 0 : first_idx_to_move - 1 puts 'Cut and paste the following...' puts "----- BEGIN -----\n\n" puts "rake db:migrate VERSION=#{mn_fmt(migrate_to_version)}" puts "cd #{File.join('db','migration')}" cmds.each {|c| puts c} puts "cd #{File.join('..','..')}" puts "rake db:migrate" puts "\n----- END -----" end end private # Checks to see if subversion is installed def has_subversion? unless @has_subversion @has_subversion = false `svn --version` rescue nil @has_subversion = true if !$?.nil? && $?.success? end return @has_subversion end def migration_dir unless @migration_dir @migration_dir = File.join(RAILS_ROOT, 'db', 'migrate') end return @migration_dir end def using_subversion? unless @svn_dir @svn_dir = File.join(migration_dir, '.svn') end return File.exists?(@svn_dir) end def migration_files files = Dir.entries(migration_dir) files.delete_if{|x| x !~ /\.rb$/} files.sort! return files end def current_migrations unless @current_migrations @current_migrations = {} migration_files.each {|f| f = f.to_s use_svn_mv = false #if has_subversion && using_subversion if has_subversion? && using_subversion? file_path = File.join(migration_dir, f) out = `svn status -v #{file_path}` use_svn_mv = true if out !~ /^\?/ end num, name = f.split(/_/, 2) num = num.to_i # puts "#{num} #{name} #{use_svn_mv}" @current_migrations[num] = [name, use_svn_mv, num] } end return @current_migrations end def mn_fmt(mn) sprintf('%03d',mn) end def print_cmd(to_number, m_data) puts fmt_cmd(to_number, m_data) end def fmt_cmd(to_number, m_data) name = m_data[0] use_svn_mv = m_data[1] from_number = m_data[2] cmd = '' if use_svn_mv cmd << 'svn mv ' else cmd << 'mv ' end cmd << mn_fmt(from_number) << '_' << name << ' ' << mn_fmt(to_number) << '_' << name return cmd end end