Skip to content

Commit

Permalink
Alter "wait for changes" algorithm to wait for incoming changes to ac…
Browse files Browse the repository at this point in the history
…cumulate before calling block. Fixes guard#202

The new algorithm is (pseudocode):

      loop
        begin
          sleep options[:wait_for_delay] # wait for changes to accumulate
          new_changes = _pop_changes
          changes += new_changes
        end until new_changes.empty?
        unless changes.empty?
          block.call(changes)
        end
      end

Note that the "sleep" happens both before and after any callback, so the
value of wait_for_delay is effectively doubled. This also means that if
the filesystem is constantly changing, the callback will never happen...
maybe we should check for this and bail out after, say, 10 seconds even
if new changes keep on coming.
  • Loading branch information
alexch committed Mar 8, 2014
1 parent 3ec4a2d commit 3ecc5e3
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 19 deletions.
29 changes: 19 additions & 10 deletions lib/listen/listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,24 +138,33 @@ def _wait_for_changes
loop do
break if @stopping

changes = _pop_changes
unless changes.all? { |_,v| v.empty? }
block.call(changes[:modified], changes[:added], changes[:removed])
changes = []
begin
sleep options[:wait_for_delay] # wait for changes to accumulate
new_changes = _pop_changes
changes += new_changes
end until new_changes.empty?
unless changes.empty?
hash = _smoosh_changes changes
block.call(hash[:modified], hash[:added], hash[:removed])
end
sleep options[:wait_for_delay]
end
rescue => ex
Kernel.warn "[Listen warning]: Change block raised an exception: #{$!}"
Kernel.warn "Backtrace:\n\t#{ex.backtrace.join("\n\t")}"
end

def _pop_changes
changes = { modified: [], added: [], removed: [] }
until @changes.empty?
change = @changes.pop
change.each { |k, v| changes[k] << v.to_s }
end
changes.each { |_, v| v.uniq! }
popped = []
popped << @changes.pop until @changes.empty?
popped
end

def _smoosh_changes changes
smooshed = { modified: [], added: [], removed: [] }
changes.each { |h| type = h.keys.first; smooshed[type] << h[type].to_s }
smooshed.each { |_, v| v.uniq! }
smooshed
end
end
end
10 changes: 5 additions & 5 deletions spec/acceptance/tcp_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
end

it 'receives changes over TCP' do
expect(listen {
expect(listen(1) {
touch 'file.rb'
}).to eq(
modified: [],
Expand All @@ -98,7 +98,7 @@
it 'may be paused and unpaused' do
recipient.pause

expect(listen {
expect(listen(1) {
touch 'file.rb'
}).to eq(
modified: [],
Expand All @@ -108,7 +108,7 @@

recipient.unpause

expect(listen {
expect(listen(1) {
touch 'file.rb'
}).to eq(
modified: ['file.rb'],
Expand All @@ -120,7 +120,7 @@
it 'may be stopped and restarted' do
recipient.stop

expect(listen {
expect(listen(1) {
touch 'file.rb'
}).to eq(
modified: [],
Expand All @@ -130,7 +130,7 @@

recipient.start

expect(listen {
expect(listen(1) {
touch 'file.rb'
}).to eq(
modified: ['file.rb'],
Expand Down
34 changes: 33 additions & 1 deletion spec/lib/listen/listener_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
listener.block = block_stub
expect(block_stub).to receive(:call).with(['foo'], [], [])
listener.start
sleep 0.01
sleep 0.25
end
end

Expand Down Expand Up @@ -268,4 +268,36 @@
end
end

describe '_wait_for_changes' do
it 'works' do

fake_time = 0
listener.stub(:sleep) { |sec| fake_time += sec; listener.stopping = true if fake_time > 1 }

modified = []
added = []
removed = []
listener.block = proc { |m,a,r| modified += m; added += a; removed += r; }

i = 0
listener.stub(:_pop_changes) do
i+=1
case i
when 1
[{modified: 'foo.txt'}]
when 2
[{added: 'bar.txt'}]
else
[]
end
end

listener.send :_wait_for_changes

expect(modified).to eql(['foo.txt'])
expect(added).to eql(['bar.txt'])

end
end

end
6 changes: 3 additions & 3 deletions spec/support/acceptance_helper.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
def listen
sleep 0.5 # wait for changes
def listen lag = 0.5
sleep lag # wait for changes
sleep_until_next_second
reset_changes
yield
sleep 0.5 # wait for changes
sleep lag # wait for changes
@changes
end

Expand Down

0 comments on commit 3ecc5e3

Please sign in to comment.