public
Last active

FSSM based FileWatcher for Rails

  • Download Gist
1_README.md
Markdown

Rails 3.2 ships with a simple FileWatcher that only reloads your app if any of the files changed.

Besides, it also provides a mechanism to hook up your own file watcher mechanism, so we can use tools like FSSM that hooks into Mac OS X fsevents. This is an example on how to hook your own mechanism (you need Rails master, soon to be Rails 3.2):

1) Copy the 2_file_watcher.rb file below to lib/file_watcher.rb

2) Add the following inside your Application in config/application.rb

if Rails.env.development?
  require "file_watcher"
  config.file_watcher = FileWatcher
end

3) Add to your Gemfile

group :development do
  gem "fssm"
end 

4) Profit!

More information about this behavior can be read on ActiveSupport::FileUpdateChecker

Note: the approach in Rails is actually quite fast and will probably be fine for all applications out there. In fact, if you are using Windows (or any operating system that does not have filesystem events supported by FSSM), the Rails implementation is even better as it is checks for timestamps just when required instead of having a pooling thread. In other words: before using this code and adding new dependencies to your project, make sure it really makes a difference.

2_file_watcher.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
# From: https://gist.github.com/1471391
# Copyright José Valim 2011 MIT-LICENSE
 
require "active_support/core_ext/array/wrap"
require "active_support/core_ext/array/extract_options"
require "fssm"
 
class FileWatcher
def initialize(files, dirs={}, &block)
@block = block
@last_update_at = Time.at(0)
@updated_at = Time.at(0)
start_monitor(files, dirs) { |base, relative| update(base, relative) }
end
 
def updated?
raise "Error on FileWatcher. FSSM thread is dead." unless @thread.alive?
@updated_at > @last_update_at
end
 
def execute_if_updated
if updated?
execute
true
else
false
end
end
 
def execute
@last_update_at = @updated_at
@block.call
end
 
private
 
def update(base, relative)
timestamp = File.mtime(File.join(base, relative))
@updated_at = timestamp if timestamp > @updated_at
end
 
def compile_glob(exts)
array = Array.wrap(array)
return "**/*" if array.empty?
"**/*.{#{array.join(",")}}"
end
 
def start_monitor(files, dirs, &block)
exprs = Hash.new { |h,k| h[k] = [] }
 
files.each do |file|
exprs[File.dirname(file)] << File.basename(file)
end
 
dirs.each do |dir, exts|
exprs[dir] << compile_glob(exts)
end
 
files.freeze
dirs.freeze
 
@monitor = FSSM::Monitor.new
 
exprs.each do |dir, globs|
@monitor.path dir do
glob "{#{globs.join(",")}}"
update(&block)
create(&block)
end
end
 
@thread = Thread.new { @monitor.run }
end
end

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.