Skip to content

Instantly share code, notes, and snippets.

@sczizzo
Last active March 16, 2016 21:56
Show Gist options
  • Save sczizzo/e27d9f38f893cbeba4a8 to your computer and use it in GitHub Desktop.
Save sczizzo/e27d9f38f893cbeba4a8 to your computer and use it in GitHub Desktop.
Watch files, in Ruby
require 'docopt'
require 'pathname'
require 'set'
USAGE = <<DOCOPT
Julio.
Usage:
julio [--interval=<seconds>] <includes>...
julio (-h | --help)
julio (-v | --version)
Options:
--interval=<seconds> Interval between loops, in seconds [default: 1].
-h --help Show this screen.
-v --version Show version.
DOCOPT
def stat f
stat = File::Stat.new(f)
{
inode: [ stat.ino, stat.dev_major, stat.dev_minor ],
size: stat.size,
mtime: stat.mtime
}
rescue Errno::ENOENT
nil
end
def was_appended? old_stat, new_stat
return false if new_stat.nil?
return new_stat[:size] > 0 if old_stat.nil?
return new_stat[:size] > old_stat[:size]
end
def was_truncated? old_stat, new_stat
return false if new_stat.nil?
return false if old_stat.nil?
return new_stat[:size] < old_stat[:size]
end
def was_replaced? old_stat, new_stat
return false if new_stat.nil?
return false if old_stat.nil?
return new_stat[:inode] != old_stat[:inode]
end
def watch includes, known
now_known = Set.new
includes.each do |i|
Dir.glob(i).each do |entry|
now_known.add Pathname.new(entry).cleanpath
end
end
created = now_known - known
deleted = known - now_known
return created, deleted, now_known
end
def report known, created, deleted, stats
deleted.each do |d|
puts 'report deleted:%s stat:%s' % [
d.inspect, stats[d].inspect
]
end
created.each do |c|
puts 'report created:%s stat:%s' % [
c.inspect, stats[c].inspect
]
end
new_stats = {}
known.each do |f|
new_stats[f] = stat(f)
if was_appended? stats[f], new_stats[f]
puts 'report appended:%s stat:%s' % [
f.inspect, new_stats[f].inspect
]
elsif was_replaced? stats[f], new_stats[f]
puts 'report replaced:%s stat:%s' % [
f.inspect, new_stats[f].inspect
]
elsif was_truncated? stats[f], new_stats[f]
puts 'report truncated:%s stat:%s' % [
f.inspect, new_stats[f].inspect
]
end
end
return new_stats
end
def tail interval, includes
known = Set.new
created = Set.new
deleted = Set.new
stats = Hash.new
loop do
created, deleted, known = watch includes, known
stats = report known, created, deleted, stats
sleep interval
end
end
begin
args = Docopt::docopt(USAGE)
rescue Docopt::Exit => e
puts e.message
end
if args.nil?
# help mode, nop
elsif args['--version']
puts 'VERSION'
elsif includes = args['<includes>']
interval = args['--interval'].to_i
if interval < 1
puts 'WARNING: Invalid <interval> provided, falling back to default...'
interval = 1
end
tail interval, includes
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment