Class: Rerun::Watcher

Inherits:
Object
  • Object
show all
Defined in:
lib/rerun/watcher.rb

Constant Summary collapse

InvalidDirectoryError =
Class.new(RuntimeError)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}, &client_callback) ⇒ Watcher

Create a file system watcher. Start it by calling #start.

Parameters:

  • options (:directory) (defaults to: {})

    the directory to watch (default “.”)

  • options (:pattern) (defaults to: {})

    the glob pattern to search under the watched directory (default “*/”)

  • options (:priority) (defaults to: {})

    the priority of the watcher thread (default 0)



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/rerun/watcher.rb', line 29

def initialize(options = {}, &client_callback)
  @client_callback = client_callback

  options = {
      :directory => ".",
      :pattern => "**/*",
      :priority => 0,
  }.merge(options)

  @pattern = options[:pattern]
  @directories = options[:directory]
  @directories = sanitize_dirs(@directories)
  @priority = options[:priority]
  @ignore = [options[:ignore]].flatten.compact
  @thread = nil
end

Instance Attribute Details

#directoryObject (readonly)

def self.default_ignore

Listen::Silencer.new(Listen::Listener.new).send :_default_ignore_patterns

end



21
22
23
# File 'lib/rerun/watcher.rb', line 21

def directory
  @directory
end

#patternObject (readonly)

def self.default_ignore

Listen::Silencer.new(Listen::Listener.new).send :_default_ignore_patterns

end



21
22
23
# File 'lib/rerun/watcher.rb', line 21

def pattern
  @pattern
end

#priorityObject (readonly)

def self.default_ignore

Listen::Silencer.new(Listen::Listener.new).send :_default_ignore_patterns

end



21
22
23
# File 'lib/rerun/watcher.rb', line 21

def priority
  @priority
end

Instance Method Details

#adapterObject



88
89
90
91
92
93
# File 'lib/rerun/watcher.rb', line 88

def adapter
  @listener.registry[:adapter] || (timeout(4) do
    sleep 1 until adapter = @listener.registry[:adapter]
    adapter
  end)
end

#ignoringObject



82
83
84
85
86
# File 'lib/rerun/watcher.rb', line 82

def ignoring
  # todo: --no-ignore-dotfiles
  dotfiles = /^\.[^.]/ # at beginning of string, a real dot followed by any other character
  [dotfiles] + @ignore.map { |x| Rerun::Glob.new(x).to_regexp }
end

#joinObject

wait for the file watcher to finish



107
108
109
110
111
# File 'lib/rerun/watcher.rb', line 107

def join
  @thread.join if @thread
rescue Interrupt => e
  # don't care
end

#pauseObject



113
114
115
# File 'lib/rerun/watcher.rb', line 113

def pause
  @listener.pause if @listener
end

#running?Boolean

Returns:

  • (Boolean)


121
122
123
# File 'lib/rerun/watcher.rb', line 121

def running?
  @listener && @listener.instance_variable_get(:@adapter)
end

#sanitize_dirs(dirs) ⇒ Object



46
47
48
49
50
51
52
53
54
55
# File 'lib/rerun/watcher.rb', line 46

def sanitize_dirs(dirs)
  dirs = [*dirs]
  dirs.map do |d|
    d.chomp!("/")
    unless FileTest.exists?(d) && FileTest.readable?(d) && FileTest.directory?(d)
      raise InvalidDirectoryError, "Directory '#{d}' either doesnt exist or isn't readable"
    end
    File.expand_path(d)
  end
end

#startObject



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rerun/watcher.rb', line 57

def start
  if @thread then
    raise RuntimeError, "already started"
  end

  @thread = Thread.new do
    @listener = Listen.to(*@directories, only: watching, ignore: ignoring, wait_for_delay: 1) do |modified, added, removed|
      if((modified.size + added.size + removed.size) > 0)
        @client_callback.call(:modified => modified, :added => added, :removed => removed)
      end
    end
    @listener.start
  end

  @thread.priority = @priority

  sleep 0.1 until @listener

  at_exit { stop } # try really hard to clean up after ourselves
end

#stopObject

kill the file watcher thread



96
97
98
99
100
101
102
103
104
# File 'lib/rerun/watcher.rb', line 96

def stop
  @thread.wakeup rescue ThreadError
  begin
    @listener.stop
  rescue Exception => e
    puts "#{e.class}: #{e.message} stopping listener"
  end
  @thread.kill rescue ThreadError
end

#unpauseObject



117
118
119
# File 'lib/rerun/watcher.rb', line 117

def unpause
  @listener.unpause if @listener
end

#watchingObject



78
79
80
# File 'lib/rerun/watcher.rb', line 78

def watching
  Rerun::Glob.new(@pattern).to_regexp
end