listen 2.7.5 → 2.7.6

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -0
  3. data/.rspec +0 -0
  4. data/.rubocop.yml +0 -0
  5. data/.travis.yml +0 -1
  6. data/.yardopts +0 -0
  7. data/CHANGELOG.md +0 -0
  8. data/CONTRIBUTING.md +0 -0
  9. data/Gemfile +25 -4
  10. data/Guardfile +0 -0
  11. data/LICENSE.txt +0 -0
  12. data/README.md +18 -10
  13. data/Rakefile +0 -0
  14. data/lib/listen.rb +2 -4
  15. data/lib/listen/adapter.rb +13 -4
  16. data/lib/listen/adapter/base.rb +33 -16
  17. data/lib/listen/adapter/bsd.rb +21 -38
  18. data/lib/listen/adapter/darwin.rb +17 -25
  19. data/lib/listen/adapter/linux.rb +34 -52
  20. data/lib/listen/adapter/polling.rb +9 -25
  21. data/lib/listen/adapter/tcp.rb +27 -14
  22. data/lib/listen/adapter/windows.rb +67 -23
  23. data/lib/listen/change.rb +26 -23
  24. data/lib/listen/cli.rb +0 -0
  25. data/lib/listen/directory.rb +47 -58
  26. data/lib/listen/file.rb +66 -101
  27. data/lib/listen/listener.rb +214 -155
  28. data/lib/listen/queue_optimizer.rb +104 -0
  29. data/lib/listen/record.rb +15 -5
  30. data/lib/listen/silencer.rb +14 -10
  31. data/lib/listen/tcp.rb +0 -1
  32. data/lib/listen/tcp/broadcaster.rb +31 -26
  33. data/lib/listen/tcp/message.rb +2 -2
  34. data/lib/listen/version.rb +1 -1
  35. data/listen.gemspec +1 -1
  36. data/spec/acceptance/listen_spec.rb +151 -239
  37. data/spec/acceptance/tcp_spec.rb +125 -134
  38. data/spec/lib/listen/adapter/base_spec.rb +13 -30
  39. data/spec/lib/listen/adapter/bsd_spec.rb +7 -35
  40. data/spec/lib/listen/adapter/darwin_spec.rb +18 -30
  41. data/spec/lib/listen/adapter/linux_spec.rb +49 -55
  42. data/spec/lib/listen/adapter/polling_spec.rb +20 -35
  43. data/spec/lib/listen/adapter/tcp_spec.rb +25 -27
  44. data/spec/lib/listen/adapter/windows_spec.rb +7 -33
  45. data/spec/lib/listen/adapter_spec.rb +10 -10
  46. data/spec/lib/listen/change_spec.rb +55 -57
  47. data/spec/lib/listen/directory_spec.rb +105 -155
  48. data/spec/lib/listen/file_spec.rb +186 -73
  49. data/spec/lib/listen/listener_spec.rb +233 -216
  50. data/spec/lib/listen/record_spec.rb +60 -22
  51. data/spec/lib/listen/silencer_spec.rb +48 -75
  52. data/spec/lib/listen/tcp/broadcaster_spec.rb +78 -69
  53. data/spec/lib/listen/tcp/listener_spec.rb +28 -71
  54. data/spec/lib/listen/tcp/message_spec.rb +48 -14
  55. data/spec/lib/listen_spec.rb +3 -3
  56. data/spec/spec_helper.rb +6 -3
  57. data/spec/support/acceptance_helper.rb +250 -31
  58. data/spec/support/fixtures_helper.rb +6 -4
  59. data/spec/support/platform_helper.rb +2 -2
  60. metadata +5 -5
  61. data/lib/listen/tcp/listener.rb +0 -108
@@ -0,0 +1,104 @@
1
+ module Listen
2
+ module QueueOptimizer
3
+ private
4
+
5
+ def _smoosh_changes(changes)
6
+ # 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 { |_, change, path, _| smooshed[change] << path.to_s }
15
+ smooshed.tap { |s| s.each { |_, v| v.uniq! } }
16
+ end
17
+ end
18
+
19
+ def _squash_changes(changes)
20
+ actions = changes.group_by(&:last).map do |path, action_list|
21
+ [_logical_action_for(path, action_list.map(&:first)), path.to_s]
22
+ end
23
+ _log :info, "listen: raw changes: #{actions.inspect}"
24
+
25
+ { modified: [], added: [], removed: [] }.tap do |squashed|
26
+ actions.each do |type, path|
27
+ squashed[type] << path unless type.nil?
28
+ end
29
+ _log :info, "listen: final changes: #{squashed.inspect}"
30
+ end
31
+ end
32
+
33
+ def _logical_action_for(path, actions)
34
+ actions << :added if actions.delete(:moved_to)
35
+ actions << :removed if actions.delete(:moved_from)
36
+
37
+ modified = actions.detect { |x| x == :modified }
38
+ _calculate_add_remove_difference(actions, path, modified)
39
+ end
40
+
41
+ def _calculate_add_remove_difference(actions, path, default_if_exists)
42
+ added = actions.count { |x| x == :added }
43
+ removed = actions.count { |x| x == :removed }
44
+ diff = added - removed
45
+
46
+ # TODO: avoid checking if path exists and instead assume the events are
47
+ # in order (if last is :removed, it doesn't exist, etc.)
48
+ if path.exist?
49
+ if diff > 0
50
+ :added
51
+ elsif diff.zero? && added > 0
52
+ :modified
53
+ else
54
+ default_if_exists
55
+ end
56
+ else
57
+ diff < 0 ? :removed : nil
58
+ end
59
+ end
60
+
61
+ # remove extraneous rb-inotify events, keeping them only if it's a possible
62
+ # editor rename() call (e.g. Kate and Sublime)
63
+ def _reinterpret_related_changes(cookies)
64
+ table = { moved_to: :added, moved_from: :removed }
65
+ cookies.map do |_, changes|
66
+ file = _detect_possible_editor_save(changes)
67
+ if file
68
+ [[:modified, file]]
69
+ else
70
+ not_silenced = changes.reject do |type, _, path, _|
71
+ _silenced?(path, type)
72
+ end
73
+ not_silenced.map do |_, change, path, _|
74
+ [table.fetch(change, change), path]
75
+ end
76
+ end
77
+ end.flatten(1)
78
+ end
79
+
80
+ def _detect_possible_editor_save(changes)
81
+ return unless changes.size == 2
82
+
83
+ from_type = from_change = from = nil
84
+ to_type = to_change = to = nil
85
+
86
+ changes.each do |data|
87
+ case data[1]
88
+ when :moved_from
89
+ from_type, from_change, from, _ = data
90
+ when :moved_to
91
+ to_type, to_change, to, _ = data
92
+ else
93
+ return nil
94
+ end
95
+ end
96
+
97
+ return unless from && to
98
+
99
+ # Expect an ignored moved_from and non-ignored moved_to
100
+ # to qualify as an "editor modify"
101
+ _silenced?(from, from_type) && !_silenced?(to, to_type) ? to : nil
102
+ end
103
+ end
104
+ end
@@ -2,6 +2,7 @@ module Listen
2
2
  class Record
3
3
  include Celluloid
4
4
 
5
+ # TODO: deprecate
5
6
  attr_accessor :paths, :listener
6
7
 
7
8
  def initialize(listener)
@@ -9,8 +10,8 @@ module Listen
9
10
  @paths = _init_paths
10
11
  end
11
12
 
12
- def set_path(path, data)
13
- new_data = file_data(path).merge(data)
13
+ def set_path(type, path, data = {})
14
+ new_data = file_data(path).merge(data).merge(type: type)
14
15
  @paths[::File.dirname(path)][::File.basename(path)] = new_data
15
16
  end
16
17
 
@@ -23,15 +24,24 @@ module Listen
23
24
  end
24
25
 
25
26
  def dir_entries(path)
26
- @paths[path.to_s]
27
+ @paths[path.to_s].dup
27
28
  end
28
29
 
29
30
  def build
31
+ @last_build_at = Time.now
30
32
  @paths = _init_paths
31
33
  listener.directories.each do |path|
32
- options = { type: 'Dir', recursive: true, silence: true }
33
- listener.registry[:change_pool].change(path, options)
34
+ options = { recursive: true, silence: true, build: true }
35
+ listener.sync(:change_pool).change(:dir, path, options)
34
36
  end
37
+ sleep 0.01 until @last_build_at + 0.1 < Time.now
38
+ rescue
39
+ Celluloid.logger.warn "build crashed: #{$!.inspect}"
40
+ raise
41
+ end
42
+
43
+ def still_building!
44
+ @last_build_at = Time.now
35
45
  end
36
46
 
37
47
  private
@@ -25,6 +25,15 @@ module Listen
25
25
  # Gedit tmp files
26
26
  | \.goutputstream-.{6}
27
27
 
28
+ # Intellij files
29
+ | ___jb_bak___
30
+ | ___jb_old___
31
+
32
+ # Vim swap files and write test
33
+ | \.sw[px]
34
+ | \.swpx
35
+ | ^4913
36
+
28
37
  # other files
29
38
  | \.DS_Store
30
39
  | \.tmp
@@ -39,29 +48,24 @@ module Listen
39
48
  _init_ignore_patterns
40
49
  end
41
50
 
42
- def silenced?(path, type = 'Unknown')
51
+ def silenced?(path, type)
43
52
  silenced = false
44
53
 
45
54
  relative_path = _relative_path(path)
46
55
 
47
- if only_patterns && type == 'File'
56
+ if only_patterns && type == :file
48
57
  silenced = !only_patterns.any? { |pattern| relative_path =~ pattern }
49
58
  end
50
59
 
51
60
  silenced || ignore_patterns.any? { |pattern| relative_path =~ pattern }
52
61
  end
53
62
 
54
- def match(args)
55
- path, type = args.first
56
- silenced?(path, type)
57
- end
58
-
59
63
  private
60
64
 
61
65
  def _init_only_patterns
62
- if listener.options[:only]
63
- @only_patterns = Array(listener.options[:only])
64
- end
66
+ return unless listener.options[:only]
67
+
68
+ @only_patterns = Array(listener.options[:only])
65
69
  end
66
70
 
67
71
  def _init_ignore_patterns
@@ -6,4 +6,3 @@ rescue LoadError
6
6
  end
7
7
 
8
8
  require 'listen/adapter/tcp'
9
- require 'listen/tcp/listener'
@@ -7,8 +7,6 @@ module Listen
7
7
 
8
8
  finalizer :finalize
9
9
 
10
- attr_reader :server, :sockets
11
-
12
10
  # Initializes a Celluloid::IO-powered TCP-broadcaster
13
11
  #
14
12
  # @param [String] host to broadcast on
@@ -17,8 +15,11 @@ module Listen
17
15
  # Note: Listens on all addresses when host is nil
18
16
  #
19
17
  def initialize(host, port)
20
- @server = TCPServer.new(host, port)
21
18
  @sockets = []
19
+ @server = TCPServer.new(host, port)
20
+ rescue
21
+ _log :error, "Broadcaster.initialize: #{$!.inspect}:#{$@.join("\n")}"
22
+ raise
22
23
  end
23
24
 
24
25
  # Asynchronously start accepting connections
@@ -28,42 +29,46 @@ module Listen
28
29
 
29
30
  # Cleans up sockets and server
30
31
  def finalize
31
- if @server
32
- @sockets.clear
33
- @server.close
34
- @server = nil
35
- end
32
+ @sockets.map(&:close) if @sockets
33
+ @sockets = nil
34
+
35
+ return unless @server
36
+ @server.close
37
+ @server = nil
36
38
  end
37
39
 
38
40
  # Broadcasts given payload to all connected sockets
39
41
  def broadcast(payload)
40
- @sockets.each do |socket|
41
- unicast(socket, payload)
42
+ active_sockets = @sockets.select do |socket|
43
+ _unicast(socket, payload)
42
44
  end
43
- end
44
-
45
- # Unicasts payload to given socket
46
- #
47
- # @return [Boolean] whether writing to socket was succesful
48
- #
49
- def unicast(socket, payload)
50
- socket.write(payload)
51
- true
52
- rescue IOError, Errno::ECONNRESET, Errno::EPIPE
53
- @sockets.delete(socket)
54
- false
45
+ @sockets.replace(active_sockets)
55
46
  end
56
47
 
57
48
  # Continuously accept and handle incoming connections
58
49
  def run
59
50
  while socket = @server.accept
60
- handle_connection(socket)
51
+ @sockets << socket
61
52
  end
53
+ rescue Celluloid::Task::TerminatedError
54
+ _log :debug, "TCP adapter was terminated: #{$!.inspect}"
55
+ rescue
56
+ _log :error, "Broadcaster.run: #{$!.inspect}:#{$@.join("\n")}"
57
+ raise
58
+ end
59
+
60
+ private
61
+
62
+ def _log(type, message)
63
+ Celluloid.logger.send(type, message)
62
64
  end
63
65
 
64
- # Handles incoming socket connection
65
- def handle_connection(socket)
66
- @sockets << socket
66
+ def _unicast(socket, payload)
67
+ socket.write(payload)
68
+ true
69
+ rescue IOError, Errno::ECONNRESET, Errno::EPIPE
70
+ _log :debug, "Broadcaster failed: #{socket.inspect}"
71
+ false
67
72
  end
68
73
  end
69
74
  end
@@ -13,8 +13,8 @@ module Listen
13
13
  #
14
14
  # @param [Object] object to initialize message with
15
15
  #
16
- def initialize(object = nil)
17
- self.object = object if object
16
+ def initialize(*args)
17
+ self.object = args
18
18
  end
19
19
 
20
20
  # Generates message size and payload for given object
@@ -1,3 +1,3 @@
1
1
  module Listen
2
- VERSION = '2.7.5'
2
+ VERSION = '2.7.6'
3
3
  end
@@ -28,6 +28,6 @@ Gem::Specification.new do |s|
28
28
  s.add_development_dependency 'bundler', '>= 1.3.5'
29
29
  s.add_development_dependency 'celluloid-io', '>= 0.15.0'
30
30
  s.add_development_dependency 'rake'
31
- s.add_development_dependency 'rspec', '~> 2.14'
31
+ s.add_development_dependency 'rspec', '~> 3.0.0rc1'
32
32
  s.add_development_dependency 'rspec-retry'
33
33
  end
@@ -2,23 +2,16 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  describe 'Listen' do
5
+ let(:base_options) { { wait_for_delay: 0.1, latency: 0.1 } }
6
+ let(:polling_options) { {} }
5
7
  let(:options) { {} }
8
+ let(:all_options) { base_options.merge(polling_options).merge(options) }
6
9
 
7
- let(:callback) do
8
- lambda do |modified, added, removed|
9
- add_changes(:modified, modified)
10
- add_changes(:added, added)
11
- add_changes(:removed, removed)
12
- end
13
- end
14
-
15
- let(:listener) { @listener }
16
- before do
17
- @listener = setup_listener(options, callback)
18
- @listener.start
19
- end
10
+ let(:wrapper) { setup_listener(all_options, :track_changes) }
11
+ before { wrapper.listener.start }
12
+ after { wrapper.listener.stop }
20
13
 
21
- after { listener.stop }
14
+ subject { wrapper }
22
15
 
23
16
  context 'with one listen dir' do
24
17
  let(:paths) { Pathname.new(Dir.pwd) }
@@ -26,291 +19,210 @@ describe 'Listen' do
26
19
 
27
20
  context 'with change block raising' do
28
21
  let(:callback) { ->(_, _, _) { fail 'foo' } }
22
+ let(:wrapper) { setup_listener(all_options, callback) }
29
23
 
30
24
  it 'warns the backtrace' do
31
25
  expect(Kernel).to receive(:warn).
32
26
  with('[Listen warning]: Change block raised an exception: foo')
33
27
  expect(Kernel).to receive(:warn).with(/^Backtrace:.*/)
34
- listen { touch 'file.rb' }
28
+ wrapper.listen { touch 'file.rb' }
35
29
  end
36
30
  end
37
31
 
38
32
  [false, true].each do |polling|
39
33
  context "force_polling option to #{polling}" do
40
- let(:options) { { force_polling: polling, latency: 0.1 } }
34
+ let(:polling_options) { { force_polling: polling } }
41
35
 
42
- context 'nothing in listen dir' do
43
- it 'listens to file addition' do
44
- expect(listen do
45
- touch 'file.rb'
46
- end).to eq(modified: [], added: ['file.rb'], removed: [])
47
- end
36
+ context 'with default ignore options' do
37
+ context 'with nothing in listen dir' do
38
+
39
+ it { is_expected.to process_addition_of('file.rb') }
40
+ it { is_expected.to process_addition_of('.hidden') }
48
41
 
49
- it 'listens to multiple files addition' do
50
- result = listen do
51
- touch 'file1.rb'
52
- touch 'file2.rb'
42
+ it 'listens to multiple files addition' do
43
+ result = wrapper.listen do
44
+ change_fs(:added, 'file1.rb')
45
+ change_fs(:added, 'file2.rb')
46
+ end
47
+
48
+ expect(result).to eq(modified: [],
49
+ added: %w(file1.rb file2.rb),
50
+ removed: [])
53
51
  end
54
52
 
55
- expect(result).to eq(modified: [],
56
- added: %w(file1.rb file2.rb),
57
- removed: [])
53
+ it 'listens to file moved inside' do
54
+ touch '../file.rb'
55
+ expect(wrapper.listen do
56
+ mv '../file.rb', 'file.rb'
57
+ end).to eq(modified: [], added: ['file.rb'], removed: [])
58
+ end
58
59
  end
59
60
 
60
- it 'listens to file moved inside' do
61
- touch '../file.rb'
62
- expect(listen do
63
- mv '../file.rb', 'file.rb'
64
- end).to eq(modified: [], added: ['file.rb'], removed: [])
65
- end
66
- end
61
+ context 'existing file.rb in listen dir' do
62
+ around do |example|
63
+ change_fs(:added, 'file.rb')
64
+ example.run
65
+ end
67
66
 
68
- context 'file in listen dir' do
69
- around do |example|
70
- touch 'file.rb'
71
- example.run
72
- end
67
+ it { is_expected.to process_modification_of('file.rb') }
68
+ it { is_expected.to process_removal_of('file.rb') }
73
69
 
74
- it 'listens to file touch' do
75
- expect(listen do
76
- touch 'file.rb'
77
- end).to eq(modified: ['file.rb'], added: [], removed: [])
78
- end
70
+ it 'listens to file.rb moved out' do
71
+ expect(wrapper.listen do
72
+ mv 'file.rb', '../file.rb'
73
+ end).to eq(modified: [], added: [], removed: ['file.rb'])
74
+ end
79
75
 
80
- it 'listens to file modification' do
81
- expect(listen do
82
- open('file.rb', 'w') { |f| f.write('foo') }
83
- end).to eq(modified: ['file.rb'], added: [], removed: [])
84
- end
76
+ it 'listens to file mode change' do
77
+ prev_mode = File.stat('file.rb').mode
85
78
 
86
- it 'listens to file modification and wait' do
87
- expect(listen do
88
- open('file.rb', 'w') { |f| f.write('foo') }
89
- sleep 0.5
90
- end).to eq(modified: ['file.rb'], added: [], removed: [])
91
- end
79
+ result = wrapper.listen do
80
+ windows? ? `attrib +r file.rb` : chmod(0444, 'file.rb')
81
+ end
92
82
 
93
- it 'listens to file echo' do
94
- expect(listen do
95
- `echo foo > #{Dir.pwd}/file.rb`
96
- end).to eq(modified: ['file.rb'], added: [], removed: [])
97
- end
83
+ new_mode = File.stat('file.rb').mode
84
+ no_event = result[:modified].empty? && prev_mode == new_mode
98
85
 
99
- it 'listens to file removal' do
100
- expect(listen do
101
- rm 'file.rb'
102
- end).to eq(modified: [], added: [], removed: ['file.rb'])
103
- end
86
+ # Check if chmod actually works or an attrib event happens,
87
+ # or expect nothing otherwise
88
+ #
89
+ # (e.g. fails for polling+vfat on Linux, but works with
90
+ # INotify+vfat because you get an event regardless if mode
91
+ # actually changes)
92
+ #
93
+ files = no_event ? [] : ['file.rb']
104
94
 
105
- it 'listens to file moved out' do
106
- expect(listen do
107
- mv 'file.rb', '../file.rb'
108
- end).to eq(modified: [], added: [], removed: ['file.rb'])
95
+ expect(result).to eq(modified: files, added: [], removed: [])
96
+ end
109
97
  end
110
98
 
111
- it 'listens to file mode change' do
112
- expect(listen do
113
- chmod 0777, 'file.rb'
114
- end).to eq(modified: ['file.rb'], added: [], removed: [])
115
- end
116
- end
99
+ context 'hidden file in listen dir' do
100
+ around do |example|
101
+ change_fs(:added, '.hidden')
102
+ example.run
103
+ end
117
104
 
118
- context 'hidden file in listen dir' do
119
- around do |example|
120
- touch '.hidden'
121
- example.run
105
+ it { is_expected.to process_modification_of('.hidden') }
122
106
  end
123
107
 
124
- it 'listens to file touch' do
125
- expect(listen do
126
- touch '.hidden'
127
- end).to eq(modified: ['.hidden'], added: [], removed: [])
128
- end
129
- end
108
+ context 'dir in listen dir' do
109
+ around do |example|
110
+ mkdir_p 'dir'
111
+ example.run
112
+ end
130
113
 
131
- context 'dir in listen dir' do
132
- around do |example|
133
- mkdir_p 'dir'
134
- example.run
114
+ it { is_expected.to process_addition_of('dir/file.rb') }
135
115
  end
136
116
 
137
- it 'listens to file touch' do
138
- expect(listen do
117
+ context 'dir with file in listen dir' do
118
+ around do |example|
119
+ mkdir_p 'dir'
139
120
  touch 'dir/file.rb'
140
- end).to eq(modified: [], added: ['dir/file.rb'], removed: [])
141
- end
142
- end
143
-
144
- context 'dir with file in listen dir' do
145
- around do |example|
146
- mkdir_p 'dir'
147
- touch 'dir/file.rb'
148
- example.run
149
- end
121
+ example.run
122
+ end
150
123
 
151
- it 'listens to file move' do
152
- expected = { modified: [],
153
- added: %w(file.rb),
154
- removed: %w(dir/file.rb)
155
- }
124
+ it 'listens to file move' do
125
+ expected = { modified: [],
126
+ added: %w(file.rb),
127
+ removed: %w(dir/file.rb)
128
+ }
156
129
 
157
- expect(listen do
158
- mv 'dir/file.rb', 'file.rb'
159
- end).to eq expected
160
- end
161
- end
162
-
163
- context 'two dirs with files in listen dir' do
164
- around do |example|
165
- mkdir_p 'dir1'
166
- touch 'dir1/file1.rb'
167
- mkdir_p 'dir2'
168
- touch 'dir2/file2.rb'
169
- example.run
130
+ expect(wrapper.listen do
131
+ mv 'dir/file.rb', 'file.rb'
132
+ end).to eq expected
133
+ end
170
134
  end
171
135
 
172
- it 'listens to multiple file moves' do
173
- expected = {
174
- modified: [],
175
- added: ['dir1/file2.rb', 'dir2/file1.rb'],
176
- removed: ['dir1/file1.rb', 'dir2/file2.rb']
177
- }
178
-
179
- expect(listen do
180
- mv 'dir1/file1.rb', 'dir2/file1.rb'
181
- mv 'dir2/file2.rb', 'dir1/file2.rb'
182
- end).to eq expected
183
- end
136
+ context 'two dirs with files in listen dir' do
137
+ around do |example|
138
+ mkdir_p 'dir1'
139
+ touch 'dir1/file1.rb'
140
+ mkdir_p 'dir2'
141
+ touch 'dir2/file2.rb'
142
+ example.run
143
+ end
184
144
 
185
- it 'listens to dir move' do
186
- expected = { modified: [],
187
- added: ['dir2/dir1/file1.rb'],
188
- removed: ['dir1/file1.rb'] }
145
+ it 'listens to multiple file moves' do
146
+ expected = {
147
+ modified: [],
148
+ added: ['dir1/file2.rb', 'dir2/file1.rb'],
149
+ removed: ['dir1/file1.rb', 'dir2/file2.rb']
150
+ }
151
+
152
+ expect(wrapper.listen do
153
+ mv 'dir1/file1.rb', 'dir2/file1.rb'
154
+ mv 'dir2/file2.rb', 'dir1/file2.rb'
155
+ end).to eq expected
156
+ end
189
157
 
190
- expect(listen do
191
- mv 'dir1', 'dir2/'
192
- end).to eq expected
193
- end
194
- end
158
+ it 'listens to dir move' do
159
+ expected = { modified: [],
160
+ added: ['dir2/dir1/file1.rb'],
161
+ removed: ['dir1/file1.rb'] }
195
162
 
196
- context 'default ignored dir with file in listen dir' do
197
- around do |example|
198
- mkdir_p '.bundle'
199
- touch '.bundle/file.rb'
200
- example.run
163
+ expect(wrapper.listen do
164
+ mv 'dir1', 'dir2/'
165
+ end).to eq expected
166
+ end
201
167
  end
202
168
 
203
- let(:options) { { force_polling: polling, latency: 0.1 } }
169
+ context 'with .bundle dir ignored by default' do
170
+ around do |example|
171
+ mkdir_p '.bundle'
172
+ example.run
173
+ end
204
174
 
205
- it "doesn't listen to file touch" do
206
- expect(listen do
207
- touch '.bundle/file.rb'
208
- end).to eq(modified: [], added: [], removed: [])
175
+ it { is_expected.not_to process_addition_of('.bundle/file.rb') }
209
176
  end
210
177
  end
211
178
 
212
- context 'ignored dir with file in listen dir' do
213
- around do |example|
214
- mkdir_p 'ignored_dir'
215
- touch 'ignored_dir/file.rb'
216
- example.run
217
- end
179
+ context 'when :ignore is *ignored_dir*' do
180
+ context 'ignored dir with file in listen dir' do
181
+ let(:options) { { ignore: /ignored_dir/ } }
218
182
 
219
- let(:options) do
220
- { force_polling: polling,
221
- latency: 0.1,
222
- ignore: /ignored_dir/ }
223
- end
183
+ around do |example|
184
+ mkdir_p 'ignored_dir'
185
+ example.run
186
+ end
224
187
 
225
- it "doesn't listen to file touch" do
226
- expect(listen do
227
- touch 'ignored_dir/file.rb'
228
- end).to eq(modified: [], added: [], removed: [])
188
+ it { is_expected.not_to process_addition_of('ignored_dir/file.rb') }
229
189
  end
230
- end
231
190
 
232
- context 'with ignored file in listen dir' do
233
- around do |example|
234
- touch 'file.rb'
235
- example.run
236
- end
237
- let(:options) do
238
- { force_polling: polling,
239
- latency: 0.1,
240
- ignore: /\.rb$/ }
241
- end
191
+ context 'when :only is *.rb' do
192
+ let(:options) { { only: /\.rb$/ } }
242
193
 
243
- it "doesn't listen to file touch" do
244
- expect(listen do
245
- touch 'file.rb'
246
- end).to eq(modified: [], added: [], removed: [])
194
+ it { is_expected.to process_addition_of('file.rb') }
195
+ it { is_expected.not_to process_addition_of('file.txt') }
247
196
  end
248
- end
249
197
 
250
- context 'with only option' do
251
- let(:options) do
252
- { force_polling: polling,
253
- latency: 0.1,
254
- only: /\.rb$/ }
255
- end
198
+ context 'when :ignore is bar.rb' do
199
+ context 'when :only is *.rb' do
200
+ let(:options) { { ignore: /bar\.rb$/, only: /\.rb$/ } }
256
201
 
257
- it 'listens only to file touch matching with only patterns' do
258
- expect(listen do
259
- touch 'file.rb'
260
- touch 'file.txt'
261
- end).to eq(modified: [], added: ['file.rb'], removed: [])
202
+ it { is_expected.to process_addition_of('file.rb') }
203
+ it { is_expected.not_to process_addition_of('file.txt') }
204
+ it { is_expected.not_to process_addition_of('bar.rb') }
205
+ end
262
206
  end
263
- end
264
207
 
265
- context 'with ignore and only option' do
266
- let(:options) do
267
- { force_polling: polling,
268
- latency: 0.1,
269
- ignore: /bar\.rb$/, only: /\.rb$/ }
270
- end
208
+ context 'when default ignore is *.rb' do
209
+ let(:options) { { ignore: /\.rb$/ } }
271
210
 
272
- it 'listens only to file touch matching with only patterns' do
273
- expect(listen do
274
- touch 'file.rb'
275
- touch 'bar.rb'
276
- touch 'file.txt'
277
- end).to eq(modified: [], added: ['file.rb'], removed: [])
278
- end
279
- end
211
+ it { is_expected.not_to process_addition_of('file.rb') }
280
212
 
281
- describe '#ignore' do
282
- around do |example|
283
- touch 'file.rb'
284
- example.run
285
- end
286
- let(:options) do
287
- { force_polling: polling,
288
- latency: 0.1,
289
- ignore: /\.rb$/ }
290
- end
213
+ context 'with #ignore on *.txt mask' do
214
+ before { wrapper.listener.ignore(/\.txt/) }
291
215
 
292
- it 'overwrites existing patterns' do
293
- expect(listen do
294
- listener.ignore(/\.txt/)
295
- touch 'file.rb'
296
- touch 'file.txt'
297
- end).to eq(modified: [], added: [], removed: [])
298
- end
299
- end
216
+ it { is_expected.not_to process_addition_of('file.rb') }
217
+ it { is_expected.not_to process_addition_of('file.txt') }
218
+ end
300
219
 
301
- describe '#ignore!' do
302
- let(:options) do
303
- { force_polling: polling,
304
- latency: 0.1,
305
- ignore: /\.rb$/ }
306
- end
220
+ context 'with #ignore! on *.txt mask' do
221
+ before { wrapper.listener.ignore!(/\.txt/) }
307
222
 
308
- it 'overwrites existing patterns' do
309
- expect(listen do
310
- listener.ignore!(/\.txt/)
311
- touch 'file.rb'
312
- touch 'file.txt'
313
- end).to eq(modified: [], added: ['file.rb'], removed: [])
223
+ it { is_expected.to process_addition_of('file.rb') }
224
+ it { is_expected.not_to process_addition_of('file.txt') }
225
+ end
314
226
  end
315
227
  end
316
228
  end