listen 2.10.1 → 3.0.0

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.
@@ -0,0 +1,45 @@
1
+ module Listen
2
+ class Listener
3
+ class Config
4
+ DEFAULTS = {
5
+ # Listener options
6
+ debug: false, # TODO: is this broken?
7
+ wait_for_delay: nil, # NOTE: should be provided by adapter if possible
8
+ relative: false,
9
+
10
+ # Backend selecting options
11
+ force_polling: false,
12
+ polling_fallback_message: nil
13
+ }
14
+
15
+ def initialize(opts)
16
+ @options = DEFAULTS.merge(opts)
17
+ @relative = @options[:relative]
18
+ @min_delay_between_events = @options[:wait_for_delay]
19
+ @silencer_rules = @options # silencer will extract what it needs
20
+ end
21
+
22
+ def relative?
23
+ @relative
24
+ end
25
+
26
+ def min_delay_between_events
27
+ @min_delay_between_events
28
+ end
29
+
30
+ def silencer_rules
31
+ @silencer_rules
32
+ end
33
+
34
+ def adapter_instance_options(klass)
35
+ valid_keys = klass.const_get('DEFAULTS').keys
36
+ Hash[@options.select { |key, _| valid_keys.include?(key) }]
37
+ end
38
+
39
+ def adapter_select_options
40
+ valid_keys = %w(force_polling polling_fallback_message).map(&:to_sym)
41
+ Hash[@options.select { |key, _| valid_keys.include?(key) }]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,32 @@
1
+ module Listen
2
+ def self.logger
3
+ @logger
4
+ end
5
+
6
+ def self.logger=(logger)
7
+ @logger = logger
8
+ end
9
+
10
+ def self.setup_default_logger_if_unset
11
+ self.logger ||= ::Logger.new(STDERR).tap do |logger|
12
+ debugging = ENV['LISTEN_GEM_DEBUGGING']
13
+ logger.level =
14
+ case debugging.to_s
15
+ when /2/
16
+ ::Logger::DEBUG
17
+ when /true|yes|1/i
18
+ ::Logger::INFO
19
+ else
20
+ ::Logger::ERROR
21
+ end
22
+ end
23
+ end
24
+
25
+ class Logger
26
+ %i(fatal error warn info debug).each do |meth|
27
+ define_singleton_method(meth) do |*args, &block|
28
+ Listen.logger.public_send(meth, *args, &block) if Listen.logger
29
+ end
30
+ end
31
+ end
32
+ end
@@ -10,7 +10,7 @@ module Listen
10
10
  return if given_options.empty?
11
11
 
12
12
  msg = "Unknown options: #{given_options.inspect}"
13
- Celluloid::Logger.warn msg
13
+ Listen::Logger.warn msg
14
14
  fail msg
15
15
  end
16
16
 
@@ -1,23 +1,40 @@
1
1
  module Listen
2
- module QueueOptimizer
3
- private
2
+ class QueueOptimizer
3
+ class Config
4
+ def initialize(adapter_class, silencer)
5
+ @adapter_class = adapter_class
6
+ @silencer = silencer
7
+ end
8
+
9
+ def exist?(path)
10
+ Pathname(path).exist?
11
+ end
12
+
13
+ def silenced?(path, type)
14
+ @silencer.silenced?(path, type)
15
+ end
16
+
17
+ def debug(*args, &block)
18
+ Listen.logger.debug(*args, &block)
19
+ end
20
+ end
4
21
 
5
- def _smoosh_changes(changes)
22
+ def smoosh_changes(changes)
6
23
  # TODO: adapter could be nil at this point (shutdown)
7
- if _adapter_class.local_fs?
8
- cookies = changes.group_by do |_, _, _, _, options|
9
- (options || {})[:cookie]
10
- end
11
- _squash_changes(_reinterpret_related_changes(cookies))
12
- else
13
- smooshed = { modified: [], added: [], removed: [] }
14
- changes.each do |_, change, dir, rel_path, _|
15
- smooshed[change] << (dir + rel_path).to_s if smooshed.key?(change)
16
- end
17
- smooshed.tap { |s| s.each { |_, v| v.uniq! } }
24
+ cookies = changes.group_by do |_, _, _, _, options|
25
+ (options || {})[:cookie]
18
26
  end
27
+ _squash_changes(_reinterpret_related_changes(cookies))
28
+ end
29
+
30
+ def initialize(config)
31
+ @config = config
19
32
  end
20
33
 
34
+ private
35
+
36
+ attr_reader :config
37
+
21
38
  # groups changes into the expected structure expected by
22
39
  # clients
23
40
  def _squash_changes(changes)
@@ -28,13 +45,14 @@ module Listen
28
45
  actions = changes.group_by(&:last).map do |path, action_list|
29
46
  [_logical_action_for(path, action_list.map(&:first)), path.to_s]
30
47
  end
31
- _log :info, "listen: raw changes: #{actions.inspect}"
48
+
49
+ config.debug("listen: raw changes: #{actions.inspect}")
32
50
 
33
51
  { modified: [], added: [], removed: [] }.tap do |squashed|
34
52
  actions.each do |type, path|
35
53
  squashed[type] << path unless type.nil?
36
54
  end
37
- _log :info, "listen: final changes: #{squashed.inspect}"
55
+ config.debug("listen: final changes: #{squashed.inspect}")
38
56
  end
39
57
  end
40
58
 
@@ -53,7 +71,7 @@ module Listen
53
71
 
54
72
  # TODO: avoid checking if path exists and instead assume the events are
55
73
  # in order (if last is :removed, it doesn't exist, etc.)
56
- if path.exist?
74
+ if config.exist?(path)
57
75
  if diff > 0
58
76
  :added
59
77
  elsif diff.zero? && added > 0
@@ -77,7 +95,7 @@ module Listen
77
95
  [[:modified, to_dir, to_file]]
78
96
  else
79
97
  not_silenced = changes.reject do |type, _, _, path, _|
80
- _silenced?(Pathname(path), type)
98
+ config.silenced?(Pathname(path), type)
81
99
  end
82
100
  not_silenced.map do |_, change, dir, path, _|
83
101
  [table.fetch(change, change), dir, path]
@@ -107,8 +125,8 @@ module Listen
107
125
 
108
126
  # Expect an ignored moved_from and non-ignored moved_to
109
127
  # to qualify as an "editor modify"
110
- return unless _silenced?(Pathname(from), from_type)
111
- _silenced?(Pathname(to), to_type) ? nil : [to_dir, to]
128
+ return unless config.silenced?(Pathname(from), from_type)
129
+ config.silenced?(Pathname(to), to_type) ? nil : [to_dir, to]
112
130
  end
113
131
  end
114
132
  end
data/lib/listen/record.rb CHANGED
@@ -3,55 +3,53 @@ require 'listen/record/symlink_detector'
3
3
 
4
4
  module Listen
5
5
  class Record
6
- include Celluloid
7
6
  # TODO: one Record object per watched directory?
8
-
9
7
  # TODO: deprecate
10
- attr_accessor :paths, :listener
11
8
 
12
- def initialize(listener)
13
- @listener = listener
14
- @paths = _auto_hash
9
+ attr_reader :root
10
+ def initialize(directory)
11
+ @tree = _auto_hash
12
+ @root = directory.to_s
15
13
  end
16
14
 
17
- def add_dir(dir, rel_path)
15
+ def add_dir(rel_path)
18
16
  return if [nil, '', '.'].include? rel_path
19
- @paths[dir.to_s][rel_path] ||= {}
17
+ @tree[rel_path] ||= {}
20
18
  end
21
19
 
22
- def update_file(dir, rel_path, data)
20
+ def update_file(rel_path, data)
23
21
  dirname, basename = Pathname(rel_path).split.map(&:to_s)
24
- _fast_update_file(dir, dirname, basename, data)
22
+ _fast_update_file(dirname, basename, data)
25
23
  end
26
24
 
27
- def unset_path(dir, rel_path)
25
+ def unset_path(rel_path)
28
26
  dirname, basename = Pathname(rel_path).split.map(&:to_s)
29
- _fast_unset_path(dir, dirname, basename)
27
+ _fast_unset_path(dirname, basename)
30
28
  end
31
29
 
32
- def file_data(dir, rel_path)
33
- root = @paths[dir.to_s]
30
+ def file_data(rel_path)
34
31
  dirname, basename = Pathname(rel_path).split.map(&:to_s)
35
32
  if [nil, '', '.'].include? dirname
36
- root[basename] ||= {}
37
- root[basename].dup
33
+ tree[basename] ||= {}
34
+ tree[basename].dup
38
35
  else
39
- root[dirname] ||= {}
40
- root[dirname][basename] ||= {}
41
- root[dirname][basename].dup
36
+ tree[dirname] ||= {}
37
+ tree[dirname][basename] ||= {}
38
+ tree[dirname][basename].dup
42
39
  end
43
40
  end
44
41
 
45
- def dir_entries(dir, rel_path)
46
- tree = if [nil, '', '.'].include? rel_path.to_s
47
- @paths[dir.to_s]
48
- else
49
- @paths[dir.to_s][rel_path.to_s] ||= _auto_hash
50
- @paths[dir.to_s][rel_path.to_s]
51
- end
42
+ def dir_entries(rel_path)
43
+ subtree =
44
+ if [nil, '', '.'].include? rel_path.to_s
45
+ tree
46
+ else
47
+ tree[rel_path.to_s] ||= _auto_hash
48
+ tree[rel_path.to_s]
49
+ end
52
50
 
53
51
  result = {}
54
- tree.each do |key, values|
52
+ subtree.each do |key, values|
55
53
  # only get data for file entries
56
54
  result[key] = values.key?(:mtime) ? values : {}
57
55
  end
@@ -59,18 +57,14 @@ module Listen
59
57
  end
60
58
 
61
59
  def build
62
- start = Time.now.to_f
63
- @paths = _auto_hash
64
-
65
- # TODO: refactor this out (1 Record = 1 watched dir)
66
- listener.directories.each do |directory|
67
- _fast_build(directory.to_s)
68
- end
69
-
70
- Celluloid::Logger.info "Record.build(): #{Time.now.to_f - start} seconds"
71
- rescue
72
- Celluloid::Logger.warn "build crashed: #{$ERROR_INFO.inspect}"
73
- raise
60
+ @tree = _auto_hash
61
+ # TODO: test with a file name given
62
+ # TODO: test other permissions
63
+ # TODO: test with mixed encoding
64
+ symlink_detector = SymlinkDetector.new
65
+ remaining = Queue.new
66
+ remaining << Entry.new(root, nil, nil)
67
+ _fast_build_dir(remaining, symlink_detector) until remaining.empty?
74
68
  end
75
69
 
76
70
  private
@@ -79,56 +73,47 @@ module Listen
79
73
  Hash.new { |h, k| h[k] = Hash.new }
80
74
  end
81
75
 
82
- def _fast_update_file(dir, dirname, basename, data)
83
- root = @paths[dir.to_s]
76
+ def tree
77
+ @tree
78
+ end
79
+
80
+ def _fast_update_file(dirname, basename, data)
84
81
  if [nil, '', '.'].include? dirname
85
- root[basename] = (root[basename] || {}).merge(data)
82
+ tree[basename] = (tree[basename] || {}).merge(data)
86
83
  else
87
- root[dirname] ||= {}
88
- root[dirname][basename] = (root[dirname][basename] || {}).merge(data)
84
+ tree[dirname] ||= {}
85
+ tree[dirname][basename] = (tree[dirname][basename] || {}).merge(data)
89
86
  end
90
87
  end
91
88
 
92
- def _fast_unset_path(dir, dirname, basename)
93
- root = @paths[dir.to_s]
89
+ def _fast_unset_path(dirname, basename)
94
90
  # this may need to be reworked to properly remove
95
91
  # entries from a tree, without adding non-existing dirs to the record
96
92
  if [nil, '', '.'].include? dirname
97
- return unless root.key?(basename)
98
- root.delete(basename)
93
+ return unless tree.key?(basename)
94
+ tree.delete(basename)
99
95
  else
100
- return unless root.key?(dirname)
101
- root[dirname].delete(basename)
96
+ return unless tree.key?(dirname)
97
+ tree[dirname].delete(basename)
102
98
  end
103
99
  end
104
100
 
105
- # TODO: test with a file name given
106
- # TODO: test other permissions
107
- # TODO: test with mixed encoding
108
- def _fast_build(root)
109
- symlink_detector = SymlinkDetector.new
110
- @paths[root] = _auto_hash
111
- remaining = Queue.new
112
- remaining << Entry.new(root, nil, nil)
113
- _fast_build_dir(remaining, symlink_detector) until remaining.empty?
114
- end
115
-
116
101
  def _fast_build_dir(remaining, symlink_detector)
117
102
  entry = remaining.pop
118
103
  children = entry.children # NOTE: children() implicitly tests if dir
119
104
  symlink_detector.verify_unwatched!(entry)
120
105
  children.each { |child| remaining << child }
121
- add_dir(entry.root, entry.record_dir_key)
106
+ add_dir(entry.record_dir_key)
122
107
  rescue Errno::ENOTDIR
123
108
  _fast_try_file(entry)
124
109
  rescue SystemCallError, SymlinkDetector::Error
125
- _fast_unset_path(entry.root, entry.relative, entry.name)
110
+ _fast_unset_path(entry.relative, entry.name)
126
111
  end
127
112
 
128
113
  def _fast_try_file(entry)
129
- _fast_update_file(entry.root, entry.relative, entry.name, entry.meta)
114
+ _fast_update_file(entry.relative, entry.name, entry.meta)
130
115
  rescue SystemCallError
131
- _fast_unset_path(entry.root, entry.relative, entry.name)
116
+ _fast_unset_path(entry.relative, entry.name)
132
117
  end
133
118
  end
134
119
  end
@@ -0,0 +1,48 @@
1
+ module Listen
2
+ class Silencer
3
+ class Controller
4
+ def initialize(silencer, default_options)
5
+ @silencer = silencer
6
+
7
+ opts = default_options
8
+
9
+ @prev_silencer_options = {}
10
+ rules = [:only, :ignore, :ignore!].map do |option|
11
+ [option, opts[option]] if opts.key? option
12
+ end
13
+
14
+ _reconfigure_silencer(Hash[rules.compact])
15
+ end
16
+
17
+ def append_ignores(*regexps)
18
+ prev_ignores = Array(@prev_silencer_options[:ignore])
19
+ _reconfigure_silencer(ignore: [prev_ignores + regexps])
20
+ end
21
+
22
+ def replace_with_bang_ignores(regexps)
23
+ _reconfigure_silencer(ignore!: regexps)
24
+ end
25
+
26
+ def replace_with_only(regexps)
27
+ _reconfigure_silencer(only: regexps)
28
+ end
29
+
30
+ private
31
+
32
+ def _reconfigure_silencer(extra_options)
33
+ opts = extra_options.dup
34
+ opts = opts.map do |key, value|
35
+ [key, Array(value).flatten.compact]
36
+ end
37
+ opts = Hash[opts]
38
+
39
+ if opts.key?(:ignore) && opts[:ignore].empty?
40
+ opts.delete(:ignore)
41
+ end
42
+
43
+ @prev_silencer_options = opts
44
+ @silencer.configure(@prev_silencer_options.dup.freeze)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,3 +1,3 @@
1
1
  module Listen
2
- VERSION = '2.10.1'
2
+ VERSION = '3.0.0'
3
3
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: listen
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thibaud Guillaume-Gentil
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-17 00:00:00.000000000 Z
11
+ date: 2015-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: celluloid
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 0.16.0
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: 0.16.0
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rb-fsevent
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -83,27 +69,32 @@ files:
83
69
  - lib/listen/adapter.rb
84
70
  - lib/listen/adapter/base.rb
85
71
  - lib/listen/adapter/bsd.rb
72
+ - lib/listen/adapter/config.rb
86
73
  - lib/listen/adapter/darwin.rb
87
74
  - lib/listen/adapter/linux.rb
88
75
  - lib/listen/adapter/polling.rb
89
- - lib/listen/adapter/tcp.rb
90
76
  - lib/listen/adapter/windows.rb
77
+ - lib/listen/backend.rb
91
78
  - lib/listen/change.rb
92
79
  - lib/listen/cli.rb
93
80
  - lib/listen/directory.rb
81
+ - lib/listen/event/config.rb
82
+ - lib/listen/event/loop.rb
83
+ - lib/listen/event/processor.rb
84
+ - lib/listen/event/queue.rb
94
85
  - lib/listen/file.rb
95
- - lib/listen/internals/logging.rb
86
+ - lib/listen/fsm.rb
96
87
  - lib/listen/internals/thread_pool.rb
97
88
  - lib/listen/listener.rb
89
+ - lib/listen/listener/config.rb
90
+ - lib/listen/logger.rb
98
91
  - lib/listen/options.rb
99
92
  - lib/listen/queue_optimizer.rb
100
93
  - lib/listen/record.rb
101
94
  - lib/listen/record/entry.rb
102
95
  - lib/listen/record/symlink_detector.rb
103
96
  - lib/listen/silencer.rb
104
- - lib/listen/tcp.rb
105
- - lib/listen/tcp/broadcaster.rb
106
- - lib/listen/tcp/message.rb
97
+ - lib/listen/silencer/controller.rb
107
98
  - lib/listen/version.rb
108
99
  homepage: https://github.com/guard/listen
109
100
  licenses: