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

ファイルを監視するやつ ー 続き

http://d.hatena.ne.jp/walf443/20070301/1172762706
の続き。

ファイルを監視するスクリプトを手軽に書きたいときに良いかなと思ってGem化しようと思ってRubyForgeに登録しようと思ったけど,既にほとんど同じようなやつがあった。Gem化されてないのが残念。

http://rubyforge.org/projects/file-observer/

4月以降触られてないようなのでGem化するパッチをつけたメールを出すか登録してしまうか考え中。登録するとしたらどんな名前にしようか困る。

内容はObservableを使ってなくて,File::Stat::ObserverManagerをThreadを継承したクラスにした感じ。この前書いたやつはThreadを使ってるあたりが怪しくてObservable#notify_observersで監視してる全部のファイルのupdateメソッドが呼び出されてるのとかが効率悪いので,こちらの方がおそらく良いコードだと思われます。

これらを踏まえて以下のように修正してみた。

       def observe(method)
-        return unless method.to_s =~ /.*time.*/ # TODO: とりあえずtime系のみサポート

-        threads = []
-        @observer_peers.each do |observer| # @observer_peers come from Observable
-          threads << Thread.new do
-            last = Time.now
-            loop do
-              begin
-                sleep(@interval)
-                current = File.__send__(method, observer.file)
-                if current > last
-                  changed
-                  notify_observers(observer.file, current)
-                  last = current
-                end
-              rescue Exception
-                next
-              end
+        last_result_of = {}
+        @observer_peers.each do |observer|
+          last_result_of[observer.file] = File.__send__(method, observer.file)
+        end
+
+        loop do
+          begin
+            sleep(@interval)
+
+            current_result_of = {}
+            @observer_peers.each do |observer|
+              current_result_of[observer.file] = File.__send__(method, observer.file)
             end
+            unless (temp_touple = current_result_of.select {|key, val| val != last_resul[key]}).size.zero?
+
+              temp_touple.each do |hash|
+                changed
+                notify_observers(hash.first, hash.last)
+              end
+              last_result_of = current_result_of
+            end
+          rescue Interrupt
+            return
+          rescue Exception
+            next
           end
         end
-
-        threads.each {|t| t.join }
-      rescue Interrupt
-        return
       end
     end
   end

loop内の動作が複雑になった代わりにThreadを使わずに済むし,time系以外でも使えるようになったのでこちらの方が良いのではないかと思います。その代わりミリ秒単位とかの監視には向かないです。Observableの使いどころはこれであっているのだろうかイマイチ自信がない。

それにしてもRubyForgeのUnixnameは長さ制限がきつい。あれのおかげでライブラリがキャッチーな名前になる傾向があるのだと思う。