class Debouncer
Constants
- DEFAULT_GROUP
- EMPTY
- VERSION
Attributes
delay[R]
Public Class Methods
new(delay, &block)
click to toggle source
# File lib/debouncer.rb, line 14 def initialize(delay, &block) self.delay = delay raise ArgumentError, 'Expected a block' unless block @timeouts = {} @threads = [] @rescuers = {} @block = block @lock = Mutex.new end
Public Instance Methods
arity()
click to toggle source
# File lib/debouncer.rb, line 29 def arity @block.arity end
call(*args, &block)
click to toggle source
# File lib/debouncer.rb, line 47 def call(*args, &block) call_with_id DEFAULT_GROUP, *args, &block end
Also aliased as: []
call_with_id(id, *args, &block)
click to toggle source
# File lib/debouncer.rb, line 52 def call_with_id(id, *args, &block) args << block if block run_thread = nil exclusively do thread = @timeouts[id] ||= new_thread { begin_delay id, args } @flush = [id] old_args = thread[:args] thread[:args] = if @reducer initial, reducer = @reducer old_args ||= initial || [] if reducer.is_a? Symbol old_args.__send__ reducer, args elsif reducer.respond_to? :call reducer.call old_args, args, id end else args.empty? ? old_args : args end if @flush.is_a? Array thread[:run_at] = Time.now + @delay else thread.kill @timeouts.delete id @threads.delete thread run_thread = new_thread { run_block thread } run_thread = nil unless @flush @flush = false end end run_thread.join if run_thread self end
delay=(delay)
click to toggle source
# File lib/debouncer.rb, line 24 def delay=(delay) raise ArgumentError, "Expected Numeric, but got #{delay.class.name}" unless delay.is_a? Numeric @delay = delay end
flush(id = EMPTY, and_join: false)
click to toggle source
# File lib/debouncer.rb, line 86 def flush(id = EMPTY, and_join: false) if @lock.owned? raise ArgumentError, 'You cannot flush other groups from inside a reducer' unless id == EMPTY || [id] == @flush @flush = and_join elsif id == EMPTY flush @timeouts.keys.first while @timeouts.any? else thread = exclusively do if (old_thread = @timeouts.delete(id)) old_thread.kill @threads.delete old_thread new_thread { run_block old_thread } end end thread.join if thread end self end
flush!(*args)
click to toggle source
# File lib/debouncer.rb, line 105 def flush!(*args) flush *args, and_join: true end
group(id)
click to toggle source
# File lib/debouncer.rb, line 43 def group(id) Group.new self, id end
inspect_params()
click to toggle source
# File lib/debouncer.rb, line 128 def inspect_params {delay: @delay, timeouts: @timeouts.count, threads: @threads.count} end
join(id = EMPTY, kill_first: false)
click to toggle source
# File lib/debouncer.rb, line 109 def join(id = EMPTY, kill_first: false) if id == EMPTY while (thread = exclusively { @threads.find &:alive? }) thread.kill if kill_first thread.join end exclusively { [@threads, @timeouts].each &:clear } if kill_first elsif (thread = exclusively { @timeouts.delete id }) @threads.delete thread thread.kill if kill_first thread.join end self end
kill(id = EMPTY)
click to toggle source
# File lib/debouncer.rb, line 124 def kill(id = EMPTY) join id, kill_first: true end
reducer(*initial, &block)
click to toggle source
# File lib/debouncer.rb, line 33 def reducer(*initial, &block) @reducer = [initial, block || initial.pop] self end
rescuer(kind = StandardError, &block)
click to toggle source
# File lib/debouncer.rb, line 38 def rescuer(kind = StandardError, &block) @rescuers[kind] = block self end
runs_at(id = DEFAULT_GROUP)
click to toggle source
# File lib/debouncer.rb, line 140 def runs_at(id = DEFAULT_GROUP) thread = @timeouts[id] thread && thread[:run_at] end
sleeping?()
click to toggle source
# File lib/debouncer.rb, line 136 def sleeping? @timeouts.length.nonzero? end
to_proc()
click to toggle source
# File lib/debouncer.rb, line 132 def to_proc method(:call).to_proc end
Private Instance Methods
begin_delay(id, args)
click to toggle source
# File lib/debouncer.rb, line 147 def begin_delay(id, args) thread[:run_at] = Time.now + @delay thread[:args] ||= args sleep @delay until exclusively { (thread[:run_at] <= Time.now).tap { |ready| @timeouts.delete id if ready } } sleep [thread[:run_at] - Time.now, 0].max end run_block thread rescue => ex @timeouts.reject! { |_, v| v == thread } (rescuer = @rescuers.find { |klass, _| ex.is_a? klass }) && rescuer.last[ex] ensure exclusively { @threads.delete thread } end
exclusively(&block)
click to toggle source
# File lib/debouncer.rb, line 170 def exclusively(&block) @lock.synchronize &block end
new_thread(*args, &block)
click to toggle source
# File lib/debouncer.rb, line 166 def new_thread(*args, &block) Thread.new(*args, &block).tap { |t| @threads << t } end
run_block(thread)
click to toggle source
# File lib/debouncer.rb, line 162 def run_block(thread) @block.call *thread[:args] end
thread()
click to toggle source
# File lib/debouncer.rb, line 174 def thread Thread.current end