listen 0.5.3 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -186
  3. data/CONTRIBUTING.md +45 -0
  4. data/{LICENSE → LICENSE.txt} +3 -1
  5. data/README.md +332 -181
  6. data/bin/listen +11 -0
  7. data/lib/listen/adapter/base.rb +129 -0
  8. data/lib/listen/adapter/bsd.rb +107 -0
  9. data/lib/listen/adapter/config.rb +25 -0
  10. data/lib/listen/adapter/darwin.rb +77 -0
  11. data/lib/listen/adapter/linux.rb +108 -0
  12. data/lib/listen/adapter/polling.rb +40 -0
  13. data/lib/listen/adapter/windows.rb +96 -0
  14. data/lib/listen/adapter.rb +32 -201
  15. data/lib/listen/backend.rb +40 -0
  16. data/lib/listen/change.rb +69 -0
  17. data/lib/listen/cli.rb +65 -0
  18. data/lib/listen/directory.rb +93 -0
  19. data/lib/listen/error.rb +11 -0
  20. data/lib/listen/event/config.rb +40 -0
  21. data/lib/listen/event/loop.rb +94 -0
  22. data/lib/listen/event/processor.rb +126 -0
  23. data/lib/listen/event/queue.rb +54 -0
  24. data/lib/listen/file.rb +95 -0
  25. data/lib/listen/fsm.rb +133 -0
  26. data/lib/listen/listener/config.rb +41 -0
  27. data/lib/listen/listener.rb +93 -160
  28. data/lib/listen/logger.rb +36 -0
  29. data/lib/listen/monotonic_time.rb +27 -0
  30. data/lib/listen/options.rb +26 -0
  31. data/lib/listen/queue_optimizer.rb +129 -0
  32. data/lib/listen/record/entry.rb +66 -0
  33. data/lib/listen/record/symlink_detector.rb +41 -0
  34. data/lib/listen/record.rb +123 -0
  35. data/lib/listen/silencer/controller.rb +50 -0
  36. data/lib/listen/silencer.rb +106 -0
  37. data/lib/listen/thread.rb +54 -0
  38. data/lib/listen/version.rb +3 -1
  39. data/lib/listen.rb +40 -32
  40. metadata +87 -38
  41. data/lib/listen/adapters/darwin.rb +0 -85
  42. data/lib/listen/adapters/linux.rb +0 -113
  43. data/lib/listen/adapters/polling.rb +0 -67
  44. data/lib/listen/adapters/windows.rb +0 -87
  45. data/lib/listen/dependency_manager.rb +0 -126
  46. data/lib/listen/directory_record.rb +0 -344
  47. data/lib/listen/multi_listener.rb +0 -121
  48. data/lib/listen/turnstile.rb +0 -28
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thread'
4
+ require 'listen/record/entry'
5
+ require 'listen/record/symlink_detector'
6
+
7
+ module Listen
8
+ class Record
9
+ # TODO: one Record object per watched directory?
10
+ # TODO: deprecate
11
+
12
+ attr_reader :root
13
+
14
+ def initialize(directory, silencer)
15
+ reset_tree
16
+ @root = directory.to_s
17
+ @silencer = silencer
18
+ end
19
+
20
+ def add_dir(rel_path)
21
+ if !empty_dirname?(rel_path.to_s)
22
+ @tree[rel_path.to_s]
23
+ end
24
+ end
25
+
26
+ def update_file(rel_path, data)
27
+ dirname, basename = Pathname(rel_path).split.map(&:to_s)
28
+ _fast_update_file(dirname, basename, data)
29
+ end
30
+
31
+ def unset_path(rel_path)
32
+ dirname, basename = Pathname(rel_path).split.map(&:to_s)
33
+ _fast_unset_path(dirname, basename)
34
+ end
35
+
36
+ def file_data(rel_path)
37
+ dirname, basename = Pathname(rel_path).split.map(&:to_s)
38
+ if empty_dirname?(dirname)
39
+ @tree[basename].dup
40
+ else
41
+ @tree[dirname][basename] ||= {}
42
+ @tree[dirname][basename].dup
43
+ end
44
+ end
45
+
46
+ def dir_entries(rel_path)
47
+ rel_path_s = rel_path.to_s
48
+ subtree = if empty_dirname?(rel_path_s)
49
+ @tree
50
+ else
51
+ @tree[rel_path_s]
52
+ end
53
+
54
+ subtree.each_with_object({}) do |(key, values), result|
55
+ # only return data for file entries inside the dir (which will each be sub-hashes)
56
+ if values.is_a?(Hash)
57
+ result[key] = values.has_key?(:mtime) ? values : {}
58
+ end
59
+ end
60
+ end
61
+
62
+ def build
63
+ reset_tree
64
+ # TODO: test with a file name given
65
+ # TODO: test other permissions
66
+ # TODO: test with mixed encoding
67
+ symlink_detector = SymlinkDetector.new
68
+ remaining = ::Queue.new
69
+ remaining << Entry.new(root, nil, nil)
70
+ _fast_build_dir(remaining, symlink_detector) until remaining.empty?
71
+ end
72
+
73
+ private
74
+
75
+ def empty_dirname?(dirname)
76
+ dirname == '.' || dirname == ''
77
+ end
78
+
79
+ def reset_tree
80
+ @tree = Hash.new { |h, k| h[k] = {} }
81
+ end
82
+
83
+ def _fast_update_file(dirname, basename, data)
84
+ if empty_dirname?(dirname.to_s)
85
+ @tree[basename] = @tree[basename].merge(data)
86
+ else
87
+ @tree[dirname][basename] = (@tree[dirname][basename] || {}).merge(data)
88
+ end
89
+ end
90
+
91
+ def _fast_unset_path(dirname, basename)
92
+ # this may need to be reworked to properly remove
93
+ # entries from a tree, without adding non-existing dirs to the record
94
+ if empty_dirname?(dirname.to_s)
95
+ if @tree.key?(basename)
96
+ @tree.delete(basename)
97
+ end
98
+ elsif @tree.key?(dirname)
99
+ @tree[dirname].delete(basename)
100
+ end
101
+ end
102
+
103
+ def _fast_build_dir(remaining, symlink_detector)
104
+ entry = remaining.pop
105
+ return if @silencer.silenced?(entry.record_dir_key, :dir)
106
+
107
+ children = entry.children # NOTE: children() implicitly tests if dir
108
+ symlink_detector.verify_unwatched!(entry)
109
+ children.each { |child| remaining << child }
110
+ add_dir(entry.record_dir_key)
111
+ rescue Errno::ENOTDIR
112
+ _fast_try_file(entry)
113
+ rescue SystemCallError, SymlinkDetector::Error
114
+ _fast_unset_path(entry.relative, entry.name)
115
+ end
116
+
117
+ def _fast_try_file(entry)
118
+ _fast_update_file(entry.relative, entry.name, entry.meta)
119
+ rescue SystemCallError
120
+ _fast_unset_path(entry.relative, entry.name)
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Listen
4
+ class Silencer
5
+ class Controller
6
+ def initialize(silencer, default_options)
7
+ @silencer = silencer
8
+
9
+ opts = default_options
10
+
11
+ @prev_silencer_options = {}
12
+ rules = [:only, :ignore, :ignore!].map do |option|
13
+ [option, opts[option]] if opts.key? option
14
+ end
15
+
16
+ _reconfigure_silencer(Hash[rules.compact])
17
+ end
18
+
19
+ def append_ignores(*regexps)
20
+ prev_ignores = Array(@prev_silencer_options[:ignore])
21
+ _reconfigure_silencer(ignore: [prev_ignores + regexps])
22
+ end
23
+
24
+ def replace_with_bang_ignores(regexps)
25
+ _reconfigure_silencer(ignore!: regexps)
26
+ end
27
+
28
+ def replace_with_only(regexps)
29
+ _reconfigure_silencer(only: regexps)
30
+ end
31
+
32
+ private
33
+
34
+ def _reconfigure_silencer(extra_options)
35
+ opts = extra_options.dup
36
+ opts = opts.map do |key, value|
37
+ [key, Array(value).flatten.compact]
38
+ end
39
+ opts = Hash[opts]
40
+
41
+ if opts.key?(:ignore) && opts[:ignore].empty?
42
+ opts.delete(:ignore)
43
+ end
44
+
45
+ @prev_silencer_options = opts
46
+ @silencer.configure(@prev_silencer_options.dup.freeze)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Listen
4
+ class Silencer
5
+ # The default list of directories that get ignored.
6
+ DEFAULT_IGNORED_FILES = %r{\A(?:
7
+ \.git
8
+ | \.svn
9
+ | \.hg
10
+ | \.rbx
11
+ | \.bundle
12
+ | bundle
13
+ | vendor/bundle
14
+ | log
15
+ | tmp
16
+ | vendor/ruby
17
+
18
+ # emacs temp files
19
+ | \#.+\#
20
+ | \.\#.+
21
+ )(/|\z)}x.freeze
22
+
23
+ # The default list of files that get ignored.
24
+ DEFAULT_IGNORED_EXTENSIONS = %r{(?:
25
+ # Kate's tmp\/swp files
26
+ \..*\d+\.new
27
+ | \.kate-swp
28
+
29
+ # Gedit tmp files
30
+ | \.goutputstream-.{6}
31
+
32
+ # Intellij files
33
+ | ___jb_bak___
34
+ | ___jb_old___
35
+
36
+ # Vim swap files and write test
37
+ | \.sw[px]
38
+ | \.swpx
39
+ | ^4913
40
+
41
+ # Sed temporary files - but without actual words, like 'sedatives'
42
+ | (?:\A
43
+ sed
44
+
45
+ (?:
46
+ [a-zA-Z0-9]{0}[A-Z]{1}[a-zA-Z0-9]{5} |
47
+ [a-zA-Z0-9]{1}[A-Z]{1}[a-zA-Z0-9]{4} |
48
+ [a-zA-Z0-9]{2}[A-Z]{1}[a-zA-Z0-9]{3} |
49
+ [a-zA-Z0-9]{3}[A-Z]{1}[a-zA-Z0-9]{2} |
50
+ [a-zA-Z0-9]{4}[A-Z]{1}[a-zA-Z0-9]{1} |
51
+ [a-zA-Z0-9]{5}[A-Z]{1}[a-zA-Z0-9]{0}
52
+ )
53
+ )
54
+
55
+ # Mutagen sync temporary files
56
+ | \.mutagen-temporary.*
57
+
58
+ # other files
59
+ | \.DS_Store
60
+ | \.tmp
61
+ | ~
62
+ )\z}x.freeze
63
+
64
+ # TODO: deprecate these mutators; use attr_reader instead
65
+ attr_accessor :only_patterns, :ignore_patterns
66
+
67
+ def initialize(**options)
68
+ configure(options)
69
+ end
70
+
71
+ # TODO: deprecate this mutator
72
+ def configure(options)
73
+ @only_patterns = options[:only] ? Array(options[:only]) : nil
74
+ @ignore_patterns = _init_ignores(options[:ignore], options[:ignore!])
75
+ end
76
+
77
+ def silenced?(relative_path, type)
78
+ path = relative_path.to_s # in case it is a Pathname
79
+
80
+ _ignore?(path) || (only_patterns && type == :file && !_only?(path))
81
+ end
82
+
83
+ private
84
+
85
+ def _ignore?(path)
86
+ ignore_patterns.any? { |pattern| path =~ pattern }
87
+ end
88
+
89
+ def _only?(path)
90
+ only_patterns.any? { |pattern| path =~ pattern }
91
+ end
92
+
93
+ def _init_ignores(ignores, overrides)
94
+ patterns = []
95
+ unless overrides
96
+ patterns << DEFAULT_IGNORED_FILES
97
+ patterns << DEFAULT_IGNORED_EXTENSIONS
98
+ end
99
+
100
+ patterns << ignores
101
+ patterns << overrides
102
+
103
+ patterns.compact.flatten
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thread'
4
+
5
+ require_relative 'logger'
6
+
7
+ module Listen
8
+ module Thread
9
+ class << self
10
+ # Creates a new thread with the given name.
11
+ # Any exceptions raised by the thread will be logged with the thread name and complete backtrace.
12
+ # rubocop:disable Style/MultilineBlockChain
13
+ def new(name, &block)
14
+ thread_name = "listen-#{name}"
15
+ caller_stack = caller
16
+
17
+ ::Thread.new do
18
+ rescue_and_log(thread_name, caller_stack: caller_stack, &block)
19
+ end.tap do |thread|
20
+ thread.name = thread_name
21
+ end
22
+ end
23
+ # rubocop:enable Style/MultilineBlockChain
24
+
25
+ def rescue_and_log(method_name, *args, caller_stack: nil)
26
+ yield(*args)
27
+ rescue => exception
28
+ _log_exception(exception, method_name, caller_stack: caller_stack)
29
+ end
30
+
31
+ private
32
+
33
+ def _log_exception(exception, thread_name, caller_stack: nil)
34
+ complete_backtrace = if caller_stack
35
+ [*exception.backtrace, "--- Thread.new ---", *caller_stack]
36
+ else
37
+ exception.backtrace
38
+ end
39
+ message = "Exception rescued in #{thread_name}:\n#{_exception_with_causes(exception)}\n#{complete_backtrace * "\n"}"
40
+ Listen.logger.error(message)
41
+ end
42
+
43
+ def _exception_with_causes(exception)
44
+ result = +"#{exception.class}: #{exception}"
45
+ if exception.cause
46
+ result << "\n"
47
+ result << "--- Caused by: ---\n"
48
+ result << _exception_with_causes(exception.cause)
49
+ end
50
+ result
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Listen
2
- VERSION = '0.5.3'
4
+ VERSION = '3.7.1'
3
5
  end
data/lib/listen.rb CHANGED
@@ -1,39 +1,47 @@
1
- module Listen
1
+ # frozen_string_literal: true
2
2
 
3
- autoload :Turnstile, 'listen/turnstile'
4
- autoload :Listener, 'listen/listener'
5
- autoload :MultiListener, 'listen/multi_listener'
6
- autoload :DirectoryRecord, 'listen/directory_record'
7
- autoload :DependencyManager, 'listen/dependency_manager'
8
- autoload :Adapter, 'listen/adapter'
3
+ require 'logger'
4
+ require 'weakref'
5
+ require 'listen/logger'
6
+ require 'listen/listener'
9
7
 
10
- module Adapters
11
- autoload :Darwin, 'listen/adapters/darwin'
12
- autoload :Linux, 'listen/adapters/linux'
13
- autoload :Windows, 'listen/adapters/windows'
14
- autoload :Polling, 'listen/adapters/polling'
15
- end
8
+ # Won't print anything by default because of level - unless you've set
9
+ # LISTEN_GEM_DEBUGGING or provided your own logger with a high enough level
10
+ Listen.logger.info "Listen loglevel set to: #{Listen.logger.level}"
11
+ Listen.logger.info "Listen version: #{Listen::VERSION}"
12
+
13
+ module Listen
14
+ @listeners = Queue.new
16
15
 
17
- # Listens to filesystem modifications on a either single directory or multiple directories.
18
- #
19
- # @param (see Listen::Listener#new)
20
- # @param (see Listen::MultiListener#new)
21
- #
22
- # @yield [modified, added, removed] the changed files
23
- # @yieldparam [Array<String>] modified the list of modified files
24
- # @yieldparam [Array<String>] added the list of added files
25
- # @yieldparam [Array<String>] removed the list of removed files
26
- #
27
- # @return [Listen::Listener] the file listener if no block given
28
- #
29
- def self.to(*args, &block)
30
- listener = if args.length == 1 || ! args[1].is_a?(String)
31
- Listener.new(*args, &block)
32
- else
33
- MultiListener.new(*args, &block)
16
+ class << self
17
+ # Listens to file system modifications on a either single directory or
18
+ # multiple directories.
19
+ #
20
+ # @param (see Listen::Listener#new)
21
+ #
22
+ # @yield [modified, added, removed] the changed files
23
+ # @yieldparam [Array<String>] modified the list of modified files
24
+ # @yieldparam [Array<String>] added the list of added files
25
+ # @yieldparam [Array<String>] removed the list of removed files
26
+ #
27
+ # @return [Listen::Listener] the listener
28
+ #
29
+ def to(*args, &block)
30
+ Listener.new(*args, &block).tap do |listener|
31
+ @listeners.enq(WeakRef.new(listener))
32
+ end
34
33
  end
35
34
 
36
- block ? listener.start : listener
35
+ # This is used by the `listen` binary to handle Ctrl-C
36
+ #
37
+ def stop
38
+ while (listener = @listeners.deq(true))
39
+ begin
40
+ listener.stop
41
+ rescue WeakRef::RefError
42
+ end
43
+ end
44
+ rescue ThreadError
45
+ end
37
46
  end
38
-
39
47
  end
metadata CHANGED
@@ -1,79 +1,128 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: listen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
5
- prerelease:
4
+ version: 3.7.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Thibaud Guillaume-Gentil
9
- - Maher Sallam
10
8
  autorequire:
11
9
  bindir: bin
12
10
  cert_chain: []
13
- date: 2012-10-03 00:00:00.000000000 Z
11
+ date: 2022-01-13 00:00:00.000000000 Z
14
12
  dependencies:
15
13
  - !ruby/object:Gem::Dependency
16
- name: bundler
14
+ name: rb-fsevent
17
15
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
16
  requirements:
20
- - - ! '>='
17
+ - - "~>"
21
18
  - !ruby/object:Gem::Version
22
- version: '0'
23
- type: :development
19
+ version: '0.10'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.10.3
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.10'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.10.3
33
+ - !ruby/object:Gem::Dependency
34
+ name: rb-inotify
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.9'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.9.10
43
+ type: :runtime
24
44
  prerelease: false
25
45
  version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
46
  requirements:
28
- - - ! '>='
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.9'
50
+ - - ">="
29
51
  - !ruby/object:Gem::Version
30
- version: '0'
52
+ version: 0.9.10
31
53
  description: The Listen gem listens to file modifications and notifies you about the
32
54
  changes. Works everywhere!
33
- email:
34
- - thibaud@thibaud.me
35
- - maher@sallam.me
36
- executables: []
55
+ email: thibaud@thibaud.gg
56
+ executables:
57
+ - listen
37
58
  extensions: []
38
59
  extra_rdoc_files: []
39
60
  files:
61
+ - CHANGELOG.md
62
+ - CONTRIBUTING.md
63
+ - LICENSE.txt
64
+ - README.md
65
+ - bin/listen
66
+ - lib/listen.rb
40
67
  - lib/listen/adapter.rb
41
- - lib/listen/adapters/darwin.rb
42
- - lib/listen/adapters/linux.rb
43
- - lib/listen/adapters/polling.rb
44
- - lib/listen/adapters/windows.rb
45
- - lib/listen/dependency_manager.rb
46
- - lib/listen/directory_record.rb
68
+ - lib/listen/adapter/base.rb
69
+ - lib/listen/adapter/bsd.rb
70
+ - lib/listen/adapter/config.rb
71
+ - lib/listen/adapter/darwin.rb
72
+ - lib/listen/adapter/linux.rb
73
+ - lib/listen/adapter/polling.rb
74
+ - lib/listen/adapter/windows.rb
75
+ - lib/listen/backend.rb
76
+ - lib/listen/change.rb
77
+ - lib/listen/cli.rb
78
+ - lib/listen/directory.rb
79
+ - lib/listen/error.rb
80
+ - lib/listen/event/config.rb
81
+ - lib/listen/event/loop.rb
82
+ - lib/listen/event/processor.rb
83
+ - lib/listen/event/queue.rb
84
+ - lib/listen/file.rb
85
+ - lib/listen/fsm.rb
47
86
  - lib/listen/listener.rb
48
- - lib/listen/multi_listener.rb
49
- - lib/listen/turnstile.rb
87
+ - lib/listen/listener/config.rb
88
+ - lib/listen/logger.rb
89
+ - lib/listen/monotonic_time.rb
90
+ - lib/listen/options.rb
91
+ - lib/listen/queue_optimizer.rb
92
+ - lib/listen/record.rb
93
+ - lib/listen/record/entry.rb
94
+ - lib/listen/record/symlink_detector.rb
95
+ - lib/listen/silencer.rb
96
+ - lib/listen/silencer/controller.rb
97
+ - lib/listen/thread.rb
50
98
  - lib/listen/version.rb
51
- - lib/listen.rb
52
- - CHANGELOG.md
53
- - LICENSE
54
- - README.md
55
99
  homepage: https://github.com/guard/listen
56
- licenses: []
100
+ licenses:
101
+ - MIT
102
+ metadata:
103
+ allowed_push_host: https://rubygems.org
104
+ bug_tracker_uri: https://github.com/guard/listen/issues
105
+ changelog_uri: https://github.com/guard/listen/releases
106
+ documentation_uri: https://www.rubydoc.info/gems/listen/3.7.1
107
+ homepage_uri: https://github.com/guard/listen
108
+ source_code_uri: https://github.com/guard/listen/tree/v3.7.1
57
109
  post_install_message:
58
110
  rdoc_options: []
59
111
  require_paths:
60
112
  - lib
61
113
  required_ruby_version: !ruby/object:Gem::Requirement
62
- none: false
63
114
  requirements:
64
- - - ! '>='
115
+ - - ">="
65
116
  - !ruby/object:Gem::Version
66
- version: '0'
117
+ version: 2.4.0
67
118
  required_rubygems_version: !ruby/object:Gem::Requirement
68
- none: false
69
119
  requirements:
70
- - - ! '>='
120
+ - - ">="
71
121
  - !ruby/object:Gem::Version
72
- version: 1.3.6
122
+ version: '0'
73
123
  requirements: []
74
- rubyforge_project: listen
75
- rubygems_version: 1.8.23
124
+ rubygems_version: 3.0.1
76
125
  signing_key:
77
- specification_version: 3
126
+ specification_version: 4
78
127
  summary: Listen to file modifications
79
128
  test_files: []
@@ -1,85 +0,0 @@
1
- module Listen
2
- module Adapters
3
-
4
- # Adapter implementation for Mac OS X `FSEvents`.
5
- #
6
- class Darwin < Adapter
7
- extend DependencyManager
8
-
9
- # Declare the adapter's dependencies
10
- dependency 'rb-fsevent', '~> 0.9.1'
11
-
12
- LAST_SEPARATOR_REGEX = /\/$/
13
-
14
- # Initializes the Adapter. See {Listen::Adapter#initialize} for more info.
15
- #
16
- def initialize(directories, options = {}, &callback)
17
- super
18
- @worker = init_worker
19
- end
20
-
21
- # Starts the adapter.
22
- #
23
- # @param [Boolean] blocking whether or not to block the current thread after starting
24
- #
25
- def start(blocking = true)
26
- @mutex.synchronize do
27
- return if @stop == false
28
- super
29
- end
30
-
31
- @worker_thread = Thread.new { @worker.run }
32
-
33
- # The FSEvent worker needs sometime to startup. Turnstiles can't
34
- # be used to wait for it as it runs in a loop.
35
- # TODO: Find a better way to block until the worker starts.
36
- sleep 0.1
37
-
38
- @poll_thread = Thread.new { poll_changed_dirs } if @report_changes
39
- @worker_thread.join if blocking
40
- end
41
-
42
- # Stops the adapter.
43
- #
44
- def stop
45
- @mutex.synchronize do
46
- return if @stop == true
47
- super
48
- end
49
-
50
- @worker.stop
51
- @worker_thread.join if @worker_thread
52
- @poll_thread.join if @poll_thread
53
- end
54
-
55
- # Checks if the adapter is usable on the current OS.
56
- #
57
- # @return [Boolean] whether usable or not
58
- #
59
- def self.usable?
60
- return false unless RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i
61
- super
62
- end
63
-
64
- private
65
-
66
- # Initializes a FSEvent worker and adds a watcher for
67
- # each directory passed to the adapter.
68
- #
69
- # @return [FSEvent] initialized worker
70
- #
71
- def init_worker
72
- FSEvent.new.tap do |worker|
73
- worker.watch(@directories.dup, :latency => @latency) do |changes|
74
- next if @paused
75
- @mutex.synchronize do
76
- changes.each { |path| @changed_dirs << path.sub(LAST_SEPARATOR_REGEX, '') }
77
- end
78
- end
79
- end
80
- end
81
-
82
- end
83
-
84
- end
85
- end