わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

Ruby 1.9移行のための簡易lintを作ってみた

そろそろRuby 1.9に移行しようと思います.ここのところ,1.8と1.9の非互換性について目にする機会がありましたので,それらの情報をもとに,lintと称するにはおこがましいのですが簡単な検証プログラムを作ってみました.
Rubyスクリプトファイルについて,以下のいずれかに該当すれば,指摘します.

  • マジックコメントがない.
  • $KCODEを使用している(コメントやリテラルでも).
  • ASCII以外の文字が含まれている(コメントと=begin〜=endは除外).
  • メソッドsize, lengthを使用している.

コマンドライン引数に,対象となるファイル名かディレクトリ名を並べて実行します.なければ,カレントディレクトリを見ます.ディレクトリの場合は,"*.rb" のみです.
ファイル名の代わりに -h か --help を指定すると,ヘルプメッセージを出力して終了します.注意書きが無意味に贅沢です.
「マジックコメントのチェックが単純すぎ*1」「文字列の中の # 以降もコメントとみなす」「文字数を知るメソッドをアドバイスしていない*2」「Ruby 1.9で動作確認していない」など,実用に当たっての問題点は山ほどありますが,自分の書いてきたrbファイルで試して,誤検出がなくなるまでバグをつぶしたので,リリースします.(同日12:40ごろ2か所修正)

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

def ruby19lint(file)
  puts "[#{file}]"

  # スクリプト取得
  content = open(file).read

  # マジックコメントなし
  if /coding[:=]/ !~ content
    puts "  マジックコメントがありません。"
  end

  # $KCODEの使用
  if content.include?("$KCODE")
    puts "  $KCODEが含まれています。"
  end

  # ASCII以外の文字
  content_lines = content.gsub(/=begin.*?=end/m, "").split(/\n+/).map {|line| line.sub(/\#.*/, "")}
  unless content_lines.join.gsub(/[\x00-\x7f]/, "").empty?
    puts "  ASCII以外の文字が含まれています。文字列の連結には注意してください。"
  end

  # size, lengthの使用
  %w!size length!.each do |word|
    if content.include?(".#{word}")
      puts "  メソッド#{word}を使用しています。"
    end
  end
end

#### main ####

if __FILE__ == $0
  if ARGV.empty?
    # 引数なし: カレントディレクトリの*.rb
    files = Dir.glob("*.rb")
    if files.empty?
      puts "*.rbファイルがありません"
      exit
    end
  elsif /^--?h/ =~ ARGV[0]
    # -h, --help
    print <<EOS
使用方法: ruby #{$0} ファイルまたはディレクトリ ...
これを実行しているRubyのバージョンは#{RUBY_VERSION}です。

EOS
    print <<'EOS'
[Ruby1.9用スクリプトを書くときの注意]
  2行目に「# -*- coding: utf-8 -*-」(マジックコメント)を書きましょう。
  外部エンコード指定は、1行目(shebang)に「 -Eutf-8」を付け加えるか、
  「Encoding.default_external = "utf-8"」という代入文を書きましょう。
  文字列のバイト数を知るメソッドには、bytesizeを使いましょう。
EOS
    exit
  else
    # その他: ディレクトリ名ならファイルを探し,そうでないならそのまま
    files = ARGV.map {|file| test(?d, file) ? Dir.glob("#{file}/**/*.rb") : file}.flatten.compact
  end

  # 一つずつチェック
  files.each do |file|
    unless test(?f, file)
      puts "#{file}というファイルはありません"
      next
    end
    ruby19lint(file)
  end
end

自身を検証してみると.「$KCODEが含まれています。」と「ASCII以外の文字が含まれています。文字列の連結には注意してください。」が出ます.

参考にしたもの:

*1:スクリプトファイルの文字コードがマジックコメントと合致しているか検証していない,マジックコメントでない coding: もマジックコメントとみなす,など.その一方で,「# vim:set fileencoding=euc-jp:」のvi形式マジックコメントも検知します.自分自身,viで書くことはまずないのですが.

*2:いろいろ調べましたが,Ruby 1.8/1.9で共通して使用できる,文字列strの文字数を知るメソッドとして,str.split(//).size より簡潔に書けるものは見当たらないもんで.