ruby
48 lines · 7 steps
Polling for file changes in Ruby
A DirectoryWatcher that detects added, removed, and changed files by comparing periodic snapshots of a directory tree.
Explained by
highlit
1require 'set'
2
3class DirectoryWatcher
4 def initialize(path, pattern: '**/*', interval: 1.0)
5 @path = File.expand_path(path)
6 @pattern = pattern
7 @interval = interval
8 @snapshot = {}
9 @running = false
10 end
11
12 def watch(&block)
13 @snapshot = scan
14 @running = true
15
16 while @running
17 sleep @interval
18 current = scan
19
20 added = current.keys - @snapshot.keys
21 removed = @snapshot.keys - current.keys
22 changed = (current.keys & @snapshot.keys).select do |file|
23 current[file] != @snapshot[file]
24 end
25
26 unless added.empty? && removed.empty? && changed.empty?
27 block.call(added: added, removed: removed, changed: changed)
28 end
29
30 @snapshot = current
31 end
32 end
33
34 def stop
35 @running = false
36 end
37
38 private
39
40 def scan
41 Dir.glob(File.join(@path, @pattern)).each_with_object({}) do |file, acc|
42 next unless File.file?(file)
43 acc[file] = File.mtime(file).to_f
44 rescue Errno::ENOENT
45 next
46 end
47 end
48end
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Diffing two point-in-time snapshots turns state into a stream of change events.
- 2Modification timestamps are a cheap proxy for detecting that a file's contents changed.
- 3A shared boolean flag lets an external caller cleanly break out of a blocking loop.
Related explainers
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
ruby
class UserAgentParser BROWSERS = [ [/Edg\/([\d.]+)/, "Edge"], [/OPR\/([\d.]+)/, "Opera"],
Parsing user-agent strings in Ruby
regex
pattern-matching
lookup-tables
Intermediate
8 steps
ruby
class LogAggregator BUCKET_FORMAT = "%Y-%m-%dT%H:%M" def initialize(entries)
Bucketing log entries by the minute in Ruby
aggregation
hashing
enumerable
Intermediate
5 steps
ruby
class WeeklySignupsReport DEFAULT_WEEKS = 12 def initialize(weeks: DEFAULT_WEEKS, source: User.all)
Building a weekly signups report in Rails
service object
aggregation
group by
Intermediate
7 steps
ruby
class ApplicationController < ActionController::Base EXPERIMENTS = { checkout_button_color: %w[control blue green], onboarding_flow: %w[control streamlined]
How A/B test cohorts are assigned in Rails
a-b-testing
cookies
hashing
Intermediate
8 steps
php
<?php final class RotatingFileLogger {
How a rotating file logger works in PHP
logging
file-rotation
io
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/polling-for-file-changes-in-ruby-explained-ruby-6abd/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.