Class: Listen::Adapter

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

Constant Summary collapse

OPTIMIZED_ADAPTERS =

The list of existing optimized adapters.

%w[Darwin Linux BSD Windows]
FALLBACK_ADAPTERS =

The list of existing fallback adapters.

%w[Polling]
ADAPTERS =

The list of all existing adapters.

OPTIMIZED_ADAPTERS + FALLBACK_ADAPTERS
DEFAULT_LATENCY =

The default delay between checking for changes.

0.25
POLLING_FALLBACK_MESSAGE =

The default warning message when falling back to polling adapter.

<<-EOS.gsub(/^\s*/, '')
  Listen will be polling for 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_directories, 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

Yields:

Yield Parameters:

  • changed_directories (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
  @stopped             = true
  @paused              = false
  @mutex               = Mutex.new
  @changed_directories = Set.new
  @turnstile           = Turnstile.new
  @latency             = options.fetch(:latency, default_latency)
  @worker              = initialize_worker
end

Instance Attribute Details

#callbackObject

Returns the value of attribute callback.



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

def callback
  @callback
end

#changed_directoriesObject

Returns the value of attribute changed_directories.



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

def changed_directories
  @changed_directories
end

#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

#mutexObject

Returns the value of attribute mutex.



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

def mutex
  @mutex
end

#pausedObject

Returns the value of attribute paused.



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

def paused
  @paused
end

#poller_threadObject

Returns the value of attribute poller_thread.



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

def poller_thread
  @poller_thread
end

#stoppedObject

Returns the value of attribute stopped.



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

def stopped
  @stopped
end

#turnstileObject

Returns the value of attribute turnstile.



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

def turnstile
  @turnstile
end

#workerObject

Returns the value of attribute worker.



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

def worker
  @worker
end

#worker_threadObject

Returns the value of attribute worker_thread.



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

def worker_thread
  @worker_thread
end

Class Method Details

.load_dependent_adapterBoolean

Load the adapter gem

Returns:

  • (Boolean)

    whether loaded or not



205
206
207
208
209
# File 'lib/listen/adapter.rb', line 205

def self.load_dependent_adapter
  return true if @loaded
  require adapter_gem
  return @loaded = true
end

.select_and_initialize(directories, options = {}) {|changed_directories, 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:

Yield Parameters:

  • changed_directories (Array<String>)

    the changed directories

  • options (Hash)

    callback options (like recursive: true)

Returns:



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/listen/adapter.rb', line 44

def self.select_and_initialize(directories, options = {}, &callback)
  forced_adapter_class = options.delete(:force_adapter)
  force_polling = options.delete(:force_polling)

  if forced_adapter_class
    forced_adapter_class.load_dependent_adapter
    return forced_adapter_class.new(directories, options, &callback)
  end

  return Adapters::Polling.new(directories, options, &callback) if force_polling

  OPTIMIZED_ADAPTERS.each do |adapter|
    namespaced_adapter = Adapters.const_get(adapter)
    if namespaced_adapter.send(:usable_and_works?, directories, options)
      return namespaced_adapter.new(directories, options, &callback)
    end
  end

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

.usable?Boolean

Checks if the adapter is usable on target OS.

Returns:

  • (Boolean)

    whether usable or not



197
198
199
# File 'lib/listen/adapter.rb', line 197

def self.usable?
  load_dependent_adapter if RbConfig::CONFIG['target_os'] =~ target_os_regex
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 the adapter is usable and work or not



189
190
191
# File 'lib/listen/adapter.rb', line 189

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 on 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



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/listen/adapter.rb', line 222

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

  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, :force => true)
  adapter.stop if adapter && adapter.started?
end

Instance Method Details

#pauseObject

Pauses the adapter.



132
133
134
# File 'lib/listen/adapter.rb', line 132

def pause
  @paused = true
end

#paused?Boolean

Returns whether the adapter is paused or not.

Returns:

  • (Boolean)

    whether the adapter is paused or not



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

def paused?
  paused
end

#report_changesObject

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



243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/listen/adapter.rb', line 243

def report_changes
  changed_dirs = nil

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

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

#startObject

Starts the adapter and don’t block the current thread.

Parameters:

  • blocking (Boolean)

    whether or not to block the current thread after starting



94
95
96
97
98
99
100
101
102
# File 'lib/listen/adapter.rb', line 94

def start
  mutex.synchronize do
    return unless stopped
    @stopped = false
  end

  start_worker
  start_poller
end

#start!Object

Starts the adapter and block the current thread.

Since:

  • 1.0.0



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

def start!
  start
  blocking_thread.join
end

#started?Boolean

Returns whether the adapter is started or not.

Returns:

  • (Boolean)

    whether the adapter is started or not



146
147
148
# File 'lib/listen/adapter.rb', line 146

def started?
  !stopped
end

#stopObject

Stops the adapter.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/listen/adapter.rb', line 115

def stop
  mutex.synchronize do
    return if stopped
    @stopped = true
    turnstile.signal # ensure no thread is blocked
  end

  worker.stop if worker
  Thread.kill(worker_thread) if worker_thread
  if poller_thread
    poller_thread.kill
    poller_thread.join
  end
end

#unpauseObject

Unpauses the adapter.



138
139
140
# File 'lib/listen/adapter.rb', line 138

def unpause
  @paused = false
end

#wait_for_callbackObject

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



161
162
163
# File 'lib/listen/adapter.rb', line 161

def wait_for_callback
  turnstile.wait unless paused
end

#wait_for_changes(threshold = 0) ⇒ Object

Blocks the main thread until N changes are detected.



168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/listen/adapter.rb', line 168

def wait_for_changes(threshold = 0)
  changes = 0

  loop do
    mutex.synchronize { changes = changed_directories.size }

    return if paused || stopped
    return if changes >= threshold

    sleep(latency)
  end
end