トドちゃん in Ruby

[結] 2006年8月 - 結城浩の日記に登場するトドちゃんをカウントするPerlスクリプトインスパイヤされて*1、トドちゃんをカウントするスクリプトRuby版。かっとなって作った。今は反省している

使い方

このスクリプトを、パスの通った場所に'todo'というファイル名で保存して、実行権限をつける。Windowsの場合は、内部でrubyコマンドを実行するtodo.cmdファイルとかを作っておけばいいっぽい。

$ todo

を実行すると、カレントディレクトリ以下のファイルを再帰的にリストアップして、トドちゃんを探し出してくれる。多分読み取り不可のファイルがあったらエラー吐いて死にます。仕様です(・ω・)そのうち直すかも。

$ todo -d

あるいは、

$ todo --desc

を実行すると、トドちゃんの数のほかに、トドちゃんが各TODOの内容をしゃべります。TODOの書式は

TODO: ほげほげ

みたいな。コロンの前後に空白文字が入ってもOK。

また、特定のファイルからのみ検索したい場合は、空白文字で区切って

todo foo.txt bar.txt baz.rb hogehoge.pm fugafuga.py piyopiyo.js

と検索したいファイルを列挙していけば出来ます。ディレクトリのパスを指定すれば、ディレクトリの中にあるファイルは全て検索対象になります。もちろん-dオプションは指定可能。

[]#!/usr/bin/env ruby[]

[]require[] []'[][]kconv[][]'[]
[]require[] []'[][]optparse[][]'[]

[]def [][]speak[][]([][]str[][])[]
 []puts[] []"[][]ε(    v ゚ω゚) < #{str}[][]".[][]tosjis[]
[]end[] []#def speak[]

[]module [][]Todo[]
  []class [][]Application[]
    []def [][]self.run[][]([][]argv[][])[]
      []self[][].[][]new[][]([][]argv[][])[]
    []end[] []#def self.run[]
    
  []private[]
    []def [][]initialize[][]([][]argv[][])[]
      []@config[] []=[] []parse_options[][]([][]argv[][])[] []# Hash[]
      
      []execute[][]([][]argv[][])[]
    []end[] []#def initialize[]
    
    []def [][]execute[][]([][]argv[][])[]
      []print[] []"[][]Searching files...[][]".[][]tosjis[]
      []files[] []=[] []Array[][].[][]new[]
      []dirs[] []=[] []Array[][].[][]new[]
      []argv[][].[][]each[] []do[] []|[][]arg[][]|[]
        []if[] []File[][].[][]exist?[] []arg[]
          []if[] []File[][].[][]directory?[] []arg[]
            []dirs[] []<<[] []arg[]
          []else[]
            []files[] []<<[] []arg[]
          []end[] []#if[]
        []end[] []#if[]
      []end[] []#argv.each[]
      
      []dirs[] []<<[] []'[][].[][]'[] []if[] []files[][].[][]size[] []==[] []0[] []&&[] []dirs[][].[][]size[] []==[] []0[]
      
      []while[][]([][]dir[] []=[] []dirs[][].[][]pop[][])[]
        []Dir[][].[][]glob[][]("[][]#{dir}[]/*[]")[] []do[] []|[][]f[][]|[]
          []if[] []File[][].[][]directory?[] []f[]
            []dirs[] []<<[] []f[]
          []else[]
            []files[] []<<[] []f[]
          []end[] []#if[]
        []end[] []#Dir.glob[]
      []end[] []#while[]
      []puts[] []"[][]finished[][]"[]
      
      []todos[] []=[] []Array[][].[][]new[]
      []files[][].[][]each[] []do[] []|[][]file[][]|[]
        []line_num[] []=[] []1[]
        []File[][].[][]open[][]([][]file[][],[] []'[][]r[][]').[][]each[] []do[] []|[][]line[][]|[]
          []if[] []/[][]TODO[][]\s[]*:[]\s[][]*(.*)[][]/[] []=~[] []line[]
            []todos[] []<<[] []{[]  []:file[] []=>[] []file[][],[]
                                []:line_num[] []=>[] []line_num[][],[]
                                []:desc[] []=>[] []$~[][][[][]1[][]]}[]
          []end[] []#if[]
          []line_num[] []+=[] []1[]  
        []end[] []#File.open.each[]
      []end[] []#files.each[]
      
      []if[] []([][]@config[][][[][]:desc[][]])[]
        []todos[][].[][]each[] []do[] []|[][]todo[][]|[]
          []speak[] []"[][]#{todo[:desc]} at #{todo[:file]}:#{todo[:line_num]}[][]"[]
        []end[] []#todos.each[]
      []end[] []#if[]
      []speak[] []"[][]#{todos.size}頭[][]"[]
    []end[] []#def execute[]
    
    []# returns Hash[]
    []# :option_name => option_value[]
    []def [][]parse_options[][]([][]argv[][])[]
      []opts[] []=[] []OptionParser[][].[][]new[]
      []ret[] []=[] []Hash[][].[][]new[]
      
      []opts[][].[][]on[][]("[][]-d[][]",[] []"[][]--desc[][]")[] []do[]
        []ret[][][[][]:desc[][]][] []=[] []true[]
      []end[] []# opts.on[]
      
      []opts[][].[][]parse![]
      
      []ret[]
    []end[] []#def parse_options[]
    
  []end[] []#class[]
  
[]end[] []#module[]

[]Todo[][]::[][]Application[][].[][]run[][]([][]ARGV[][])[]

*1:というかパクリ