July 26, 2007
Older: Customizing Logger
Newer: I Can Has Command Line?
Last Modified File in Directory
The code below recursively loops through a directory and returns the file that was modified most recently. You can even pass in an extension to limit the possible returns to.
require 'find'
def last_modified_file(dir, o={})
options = {:ext => nil}.merge(o)
files = []
Find.find('.') do |f|
if options[:ext]
files << f if File.extname(f) == options[:ext]
else
files << f
end
end
files.max { |a, b| File.mtime(a) <=> File.mtime(b) }
end
puts last_modified_file('.') # last modified file
puts last_modified_file('.', :ext => '.as') # last modified file with .as extension
puts last_modified_file('.', :ext => '.rb') # last modified file with .rb extension
puts last_modified_file('.', :ext => '') # last modified file with no extenstion
I’m using it on an ActionScript project. I got tired of rebuilding the class every minute to check for errors and see if the code was working properly. This method is part of a script that checks if any .as files have changed in the directory since the last build and if so rebuilds the main document class into a swf. I tried to slim it down more but couldn’t really think of anything. Anyone have any better ideas? If not, just take this post as an encouragement to check out the Find class.
12 Comments
Jul 27, 2007
Something like this might work:
This uses a Swartzian Transform so that mtime is only called once per file, rather than twice per comparison.
Jul 27, 2007
Looks like I spoke too soon. No sooner did I submit this when I remembered sort_by, which does a “Swartzian Transform” under the hood:
Jul 27, 2007
Did you test your code ? Because you don’t use the ‘dir’ argument in your function.
But thanks for this tips.
Jul 27, 2007
Yes he does, in:
Dir["#{dir}**/*#{options[:ext]}"] ^ | here
Too bad I don’t have irb next to me to check, but I would :
- put a “.” between * and {options[:ext]}
- not use a hash for only one option
- check if by sort_by mtime we don’t get the last one first, so that we would have to ask last file from the list.
def last_modified_file(dir, ext=nil) Dir["#{dir}**/*#{ext && '.'<<ext}"].sort_by { |p| File.mtime(p)}.last end
My 2 cents.
Jul 27, 2007
Sorry for the bad presentation! (a Preview button could be nice, though)
Still needs to be tested though!
Jul 27, 2007
Building projects when files change is the kind of thing Rake and Make was built to do. But I guess you know that :-)
Jul 27, 2007
Still, you need to put some code in your .task files.
But I guess you know that :-)
Jul 27, 2007
Yes, I know. I’m just wondering why you would choose to build your own instead of using Rake, when the same can be achieved with Rake by simply saying:
file “project.swf” => Dir.glob(“*.as”) do |t| … end
Jul 30, 2007
Alternatively
def last_modified_file(dir, ext=nil) `cd "#{dir.to_s}"; ls -At "*#{ext.to_s}" | head -1`.chomp end
Use the OS Luke.
Jul 30, 2007
Alternatively
Use the OS Luke.
Jul 31, 2007
It sounds like you’ve got the triggering of the build covered. But lest you havn’t, it’s worth knowing about stakeout, an OS X-only app that watches files for modifications.
As topfunky suggests you can use it to trigger a rake task:
$ stakeout "rake my:task" watch/this/file.rb
You can hook it up to Growl to be notified of the result of the triggered action.
Aug 01, 2007
@Andy – Holy sweetness. Thanks for that link. I never quite finished the script because I was having some problems daemonizing the script. I’ll look into stakeout.
Sorry, comments are closed for this article to ease the burden of pruning spam.