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)



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/listen/adapter.rb', line 70

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 required or not



194
195
196
# File 'lib/listen/adapter.rb', line 194

def self.load_dependent_adapter
  @loaded ||= require adapter_gem
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
# File 'lib/listen/adapter.rb', line 44

def self.select_and_initialize(directories, options = {}, &callback)
  return Adapters::Polling.new(directories, options, &callback) if options.delete(: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



186
187
188
# File 'lib/listen/adapter.rb', line 186

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



178
179
180
# File 'lib/listen/adapter.rb', line 178

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



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/listen/adapter.rb', line 209

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.



121
122
123
# File 'lib/listen/adapter.rb', line 121

def pause
  @paused = true
end

#paused?Boolean

Returns whether the adapter is paused or not.

Returns:

  • (Boolean)

    whether the adapter is paused or not



143
144
145
# File 'lib/listen/adapter.rb', line 143

def paused?
  paused
end

#report_changesObject

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



230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/listen/adapter.rb', line 230

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



86
87
88
89
90
91
92
93
94
# File 'lib/listen/adapter.rb', line 86

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



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

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



135
136
137
# File 'lib/listen/adapter.rb', line 135

def started?
  !stopped
end

#stopObject

Stops the adapter.



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/listen/adapter.rb', line 107

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
  poller_thread.join if poller_thread
end

#unpauseObject

Unpauses the adapter.



127
128
129
# File 'lib/listen/adapter.rb', line 127

def unpause
  @paused = false
end

#wait_for_callbackObject

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



150
151
152
# File 'lib/listen/adapter.rb', line 150

def wait_for_callback
  turnstile.wait unless paused
end

#wait_for_changes(threshold = 0) ⇒ Object

Blocks the main thread until N changes are detected.



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/listen/adapter.rb', line 157

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