Previous page: Daily Crap 2010-02-11
Next page: Daily Crap 2010-02-15


Detecting file changes with Ruby

Update: I think you should use the FSSM library instead of this one.

I have a post on the way about the piece I wrote that PLOrk performed last week in Chicago (a tiny clip of my piece is on YouTube), but I wrote more software to make it go than I realized, so it will have to wait. One bit of Ruby code doesn't require a lot of discussion, though.

I conducted the piece while sitting at my laptop with everyone else. I had two means of communication, both of which involved changing a text file, saving it, and that's it. The idea is nothing new; autotest does it, Archaeopteryx does it, and much more I'm sure. The only thing is that I'd never known of a way to separate that functionality in such a way that I could use it for other scripts.

So I dug into the autotest source (not that it was very hard) and extracted the bits to do with watching files for changes into a very small class, FileMon:

class FileMon
  def initialize(filename)
    raise "File does not exist" unless File.exist?(filename)

    @filename = filename
    @last_mtime = File.stat(@filename).mtime
  end

  def run(sleep=1, &on_update)
    on_update[] # always called once at start
    loop do
      Kernel.sleep sleep until file_updated?
      on_update[]
    end
  end

  def file_updated?
    mtime = File.stat(@filename).mtime
    updated = @last_mtime < mtime

    @last_mtime = mtime

    updated
  end
end

It's used like this:

FileMon.new("myfile").run do
  puts "updated"
end

… which I soon expanded into a shell script (waitforsave):

#!/usr/bin/ruby

require "file_mon"

watched_file = ARGV[0]
first = true
FileMon.new(watched_file).run do
  exit unless first
  first = false
end

Now that's really easy to use anywhere. For example, I've been writing sheet music for class:

while true; do waitforsave assignment.ly; lily assignment.ly; say "done"; done


Comments

Click here to view the comments on this post.