Hey! Watch it!...or how to monitor files in Elixir
File watchers and Elixir
I’m working on a feature that reloads a config file if the file is updated. The easiest way I could figure out was to watch the file, and reload the code. Thinking in JavaScript, I’d expect something like:
1
2
3
4
// fake
Dir.watch('configs/', function(changedFile){
console.log(`${changedFile} was updated`);
})
But if you’ve been working with Elixir for a bit, you’ll know there’s gonna be a whole bunch of message passing to get this working. In the end, we wind up with something like:
1
2
3
4
5
6
7
8
9
# Starting the process
:fs.start_link(:my_watcher, Path.absname("config"))
# Subscribing `self` to receive events.
:fs.subscribe(:my_watcher)
receive do
{_watcher_process, {:fs, :file_event}, {changedFile, _type}} ->
IO.puts("#{changedFile} was updated")
end
Getting Started and the FS library
I picked the fs
library since it’s what Phoenix uses. In this post, we’ll be using master in a mix project. Follow along!
A new project!
Create a new mix project with mix new watch_it
, and add fs to your dependencies in mix.exs.
1
2
3
4
5
#...
defp deps do
[{:fs, github: "synrc/fs"}]
end
#...
And start the fs
application with:
1
2
3
def application do
[applications: [:logger, :fs]]
end
Then install everything with mix deps.get
And kick the tires in iex -S mix
(this will compile a bunch).
1
2
3
4
5
:fs.start_link(:my_watcher, Path.absname("config"))
:fs.subscribe(:my_watcher)
flush
# {#PID<0.129.0>, {:fs, :file_event},
# {'/Users/StevenNunez/code/watch_it/config/fake_config.exs', [:created]}}
I don’t know about you, but I’m excited. What happened?
:fs.start_link(:my_watcher, Path.absname("config"))
created a process that watches a directory for changes. We give the process a reference of :my_watcher
. We’ll use it later. The next line is where the magic is.
:fs.subscribe(:my_watcher)
finds the :my_watcher
process, then notifies self
whenever a file changes.
We can add a bit of pattern matching to make this nicer. (Make sure you’re exiting iex -S mix
between examples)
1
2
3
4
5
6
:fs.start_link(:my_watcher, Path.absname("config"))
:fs.subscribe(:my_watcher)
receive do
{_watcher_process, {:fs, :file_event}, {changedFile, _type}} ->
IO.puts("#{changedFile} was updated")
end
Conclusion
I plan on using this library a lot! Using this with separate process responding to changes to the file system is super powerful. Phoenix uses it for it’s live reloading. Maybe we can replace some JavaScript build tools ;-)
comments powered by Disqus