Class: Listen::Adapter

Inherits:
Object
  • Object
show all
Defined in:
lib/listen/adapter.rb

Constant Summary collapse

DEFAULT_LATENCY =

The default delay between checking for changes.

0.25
MISSING_DEPENDENCY_MESSAGE =

The default warning message when there is a missing dependency.

<<-EOS.gsub(/^\s*/, '')
  For a better performance, it's recommended that you satisfy the missing dependency.
EOS
POLLING_FALLBACK_MESSAGE =

The default warning message when falling back to polling adapter.

<<-EOS.gsub(/^\s*/, '')
  Listen will be polling changes. Learn more at https://github.com/guard/listen#polling-fallback.
EOS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(directories, options = {}) {|changed_dirs, options| ... } ⇒ Listen::Adapter

Initializes the adapter.

Parameters:

  • directories (String, Array<String>)

    the directories to watch

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

    the adapter options

Options Hash (options):

  • latency (Float)

    the delay between checking for changes in seconds

  • report_changes (Boolean)

    whether or not to automatically report changes (run the callback)

Yields:

  • (changed_dirs, options)

    callback Callback called when a change happens

Yield Parameters:

  • changed_dirs (Array<String>)

    the changed directories

  • options (Hash)

    callback options (like :recursive => true)



78
79
80
81
82
83
84
85
86
87
88
# File 'lib/listen/adapter.rb', line 78

def initialize(directories, options = {}, &callback)
  @directories  = Array(directories)
  @callback     = callback
  @paused       = false
  @mutex        = Mutex.new
  @changed_dirs = Set.new
  @turnstile    = Turnstile.new
  @latency    ||= DEFAULT_LATENCY
  @latency      = options[:latency] if options[:latency]
  @report_changes = options[:report_changes].nil? ? true : options[:report_changes]
end

Instance Attribute Details

#directoriesObject

Returns the value of attribute directories.



8
9
10
# File 'lib/listen/adapter.rb', line 8

def directories
  @directories
end

#latencyObject

Returns the value of attribute latency.



8
9
10
# File 'lib/listen/adapter.rb', line 8

def latency
  @latency
end

#pausedObject

Returns the value of attribute paused.



8
9
10
# File 'lib/listen/adapter.rb', line 8

def paused
  @paused
end

Class Method Details

.select_and_initialize(directories, options = {}) {|changed_dirs, options| ... } ⇒ Listen::Adapter

Selects the appropriate adapter implementation for the current OS and initializes it.

Parameters:

  • directories (String, Array<String>)

    the directories to watch

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

    the adapter options

Options Hash (options):

  • force_polling (Boolean)

    to force polling or not

  • polling_fallback_message (String, Boolean)

    to change polling fallback message or remove it

  • latency (Float)

    the delay between checking for changes in seconds

Yields:

  • (changed_dirs, options)

    callback Callback called when a change happens

Yield Parameters:

  • changed_dirs (Array<String>)

    the changed directories

  • options (Hash)

    callback options (like :recursive => true)

Returns:



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
# File 'lib/listen/adapter.rb', line 38

def self.select_and_initialize(directories, options = {}, &callback)
  return Adapters::Polling.new(directories, options, &callback) if options.delete(:force_polling)

  warning = ''

  begin
    if Adapters::Darwin.usable_and_works?(directories, options)
      return Adapters::Darwin.new(directories, options, &callback)
    elsif Adapters::Linux.usable_and_works?(directories, options)
      return Adapters::Linux.new(directories, options, &callback)
    elsif Adapters::BSD.usable_and_works?(directories, options)
      return Adapters::BSD.new(directories, options, &callback)
    elsif Adapters::Windows.usable_and_works?(directories, options)
      return Adapters::Windows.new(directories, options, &callback)
    end
  rescue DependencyManager::Error => e
    warning += e.message + "\n" + MISSING_DEPENDENCY_MESSAGE
  end

  unless options[:polling_fallback_message] == false
    warning += options[:polling_fallback_message] || POLLING_FALLBACK_MESSAGE
    Kernel.warn "[Listen warning]:\n" + warning.gsub(/^(.*)/, '  \1')
  end

  Adapters::Polling.new(directories, options, &callback)
end

.usable?Boolean

Checks if the adapter is usable on the current OS.

Returns:

  • (Boolean)

    whether usable or not



140
141
142
143
# File 'lib/listen/adapter.rb', line 140

def self.usable?
  load_depenencies
  dependencies_loaded?
end

.usable_and_works?(directories, options = {}) ⇒ Boolean

Checks if the adapter is usable and works on the current OS.

Parameters:

  • directories (String, Array<String>)

    the directories to watch

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

    the adapter options

Options Hash (options):

  • latency (Float)

    the delay between checking for changes in seconds

Returns:

  • (Boolean)

    whether usable and work or not



153
154
155
# File 'lib/listen/adapter.rb', line 153

def self.usable_and_works?(directories, options = {})
  usable? && Array(directories).all? { |d| works?(d, options) }
end

.works?(directory, options = {}) ⇒ Boolean

Note:

This test takes some time depending the adapter latency.

Runs a tests to determine if the adapter can actually pick up changes in a given directory and returns the result.

Parameters:

  • directory (String, Pathname)

    the directory to watch

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

    the adapter options

Options Hash (options):

  • latency (Float)

    the delay between checking for changes in seconds

Returns:

  • (Boolean)

    whether the adapter works or not



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/listen/adapter.rb', line 168

def self.works?(directory, options = {})
  work = false
  test_file = "#{directory}/.listen_test"
  callback = lambda { |*| work = true }
  adapter  = self.new(directory, options, &callback)
  adapter.start(false)

  FileUtils.touch(test_file)

  t = Thread.new { sleep(adapter.latency * 5); adapter.stop }

  adapter.wait_for_callback
  work
ensure
  Thread.kill(t) if t
  FileUtils.rm(test_file) if File.exists?(test_file)
  adapter.stop if adapter && adapter.started?
end

Instance Method Details

#report_changesObject

Runs the callback and passes it the changes if there are any.



189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/listen/adapter.rb', line 189

def report_changes
  changed_dirs = nil

  @mutex.synchronize do
    return if @changed_dirs.empty?
    changed_dirs = @changed_dirs.to_a
    @changed_dirs.clear
  end

  @callback.call(changed_dirs, {})
  @turnstile.signal
end

#start(blocking = true) ⇒ Object

Starts the adapter.

Parameters:

  • blocking (Boolean) (defaults to: true)

    whether or not to block the current thread after starting



94
95
96
# File 'lib/listen/adapter.rb', line 94

def start(blocking = true)
  @stop = false
end

#started?Boolean

Returns whether the adapter is statred or not

Returns:

  • (Boolean)

    whether the adapter is started or not



109
110
111
# File 'lib/listen/adapter.rb', line 109

def started?
  @stop.nil? ? false : !@stop
end

#stopObject

Stops the adapter.



100
101
102
103
# File 'lib/listen/adapter.rb', line 100

def stop
  @stop = true
  @turnstile.signal # ensure no thread is blocked
end

#wait_for_callbackObject

Blocks the main thread until the poll thread runs the callback.



116
117
118
# File 'lib/listen/adapter.rb', line 116

def wait_for_callback
  @turnstile.wait unless @paused
end

#wait_for_changes(goal = 0) ⇒ Object

Blocks the main thread until N changes are detected.



123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/listen/adapter.rb', line 123

def wait_for_changes(goal = 0)
  changes = 0

  loop do
    @mutex.synchronize { changes = @changed_dirs.size }

    return if @paused || @stop
    return if changes >= goal

    sleep(@latency)
  end
end