読者です 読者をやめる 読者になる 読者になる

renamemodule.rb

むしゃくしゃして作った。モジュール名変えられるならなんでもよかった。今は公開している。

実行すると、変更するモジュール名を聞いてきて、カレントディレクトリ以下の*.ml{i,l,y,}ファイルの中のモジュール名を変更します。変更点は、対話的に表示され、確認できます。すべて変更する場合はEnterをずーっと押してれば大丈夫です。(変更の最後の確認は、yかnを入れないと行われません。)

sedでいいような気もしてきた。

require 'pathname'
require 'fileutils'

class Pathname
  def each_children(ptn=/.*/, exc=nil, &blk)
    children.each { |path|
      if exc && exc !~ path.basename.to_s
        if path.directory?
          path.each_children(ptn,exc,&blk)
        else
          if ptn =~ path.to_s
            yield(path)
          end
        end
      end
    }
  end
end

def gets_or_exit()
  x = gets()
  if x then
    x.chomp()
  else
    exit 0
  end
end

def yes_no(prompt, yes, no) 
  print prompt
  x = gets_or_exit
  if yes.include?(x)
    true
  elsif no.include?(x)
    false
  else
    yes_no(prompt, yes, no)
  end
end

def rename_src(old,new,path)
  old_ptn = /\b#{old}\b/
  renamed = false
  
  lines2 = []
  
  lines = path.readlines
  lines.each { |l|
    if l =~ old_ptn
      puts "#{path.to_s}:"
      puts "  #{l.chomp.strip}"
      puts "        =>"
      puts "  #{l.chomp.strip.gsub(old_ptn, new)}"
      if yes_no("Replace?(Y/n) :", ["y","Y",""], ["n","N"])
        renamed = true
        l.gsub!(old_ptn, new)
      end
    end
  }
  
  if renamed
    lines
  else
    nil
  end
end

def rename_file(old,new,path)
  if path.basename.to_s =~ /^#{old}\.ml(l|y|i)?$/
    newname = "#{path.dirname}/#{new}.ml#{$1}"
    path.rename(newname)
  end
end

def module_name(f)
  f[0].chr.upcase + f[1,f.length-1]
end

while true
  print "Old file name: "
  old = gets_or_exit()
  print "New file name: "
  new = gets_or_exit()
  
  break if yes_no("#{old}(#{module_name(old)}) => #{new}(#{module_name(new)}), OK?(Y/n): ", ["y","Y", ""], ["n","N"])
end

renamed = {}

Pathname.new(".").each_children(/\.ml(l|y|i)?$/, /\.svn/) {|path|
  content = rename_src(module_name(old), module_name(new), path)
  if content
    renamed[path] = content
  end
}

puts "Following files will be updated: "
renamed.keys.each {|path|
  puts "  " + path.to_s
}

if yes_no("OK?(y/n)", ["y","Y"], ["n", "N"])
  renamed.each {|path,lines|
    FileUtils.cp(path.to_s, path.to_s + "~~")
    path.open("w") {|f|
      lines.each {|l|
        f.puts l
      }
    }
  }
end

ちなみに、これ作ってるときに型チェッカが欲しいと思ったのは、

  1. Hash#keysかhash#keyか迷った
  2. Array#include?かArray#includeか迷った
  3. Hash.eachの引数が、keyのみかvalueも含むのか迷った
  4. パラメータ数を変えたら再帰呼び出しを修正するの忘れた

くらいか。

大きくなると、これがうざくなってくるハズ。

※ちょっと修正