Class: Debouncer

Inherits:
Object
  • Object
show all
Includes:
Inspection
Defined in:
lib/debouncer.rb,
lib/debouncer/group.rb,
lib/debouncer/version.rb,
lib/debouncer/debounceable.rb

Defined Under Namespace

Modules: Debounceable Classes: Group

Constant Summary collapse

DEFAULT_GROUP =
Object.new
EMPTY =
Object.new
VERSION =
'0.2.2'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Inspection

#inspect

Constructor Details

#initialize(delay, &block) ⇒ Debouncer

Returns a new instance of Debouncer.

Raises:

  • (ArgumentError)


14
15
16
17
18
19
20
21
22
# 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

Instance Attribute Details

#delayObject

Returns the value of attribute delay.



12
13
14
# File 'lib/debouncer.rb', line 12

def delay
  @delay
end

Instance Method Details

#arityObject



29
30
31
# File 'lib/debouncer.rb', line 29

def arity
  @block.arity
end

#call(*args, &block) ⇒ Object Also known as: []



47
48
49
# File 'lib/debouncer.rb', line 47

def call(*args, &block)
  call_with_id DEFAULT_GROUP, *args, &block
end

#call_with_id(id, *args, &block) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# 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

#flush(id = EMPTY, and_join: false) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 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) ⇒ Object



105
106
107
# File 'lib/debouncer.rb', line 105

def flush!(*args)
  flush *args, and_join: true
end

#group(id) ⇒ Object



43
44
45
# File 'lib/debouncer.rb', line 43

def group(id)
  Group.new self, id
end

#inspect_paramsObject



128
129
130
# File 'lib/debouncer.rb', line 128

def inspect_params
  {delay: @delay, timeouts: @timeouts.count, threads: @threads.count}
end

#join(id = EMPTY, kill_first: false) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
# 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) ⇒ Object



124
125
126
# File 'lib/debouncer.rb', line 124

def kill(id = EMPTY)
  join id, kill_first: true
end

#reducer(*initial, &block) ⇒ Object



33
34
35
36
# File 'lib/debouncer.rb', line 33

def reducer(*initial, &block)
  @reducer = [initial, block || initial.pop]
  self
end

#rescuer(kind = StandardError, &block) ⇒ Object



38
39
40
41
# File 'lib/debouncer.rb', line 38

def rescuer(kind = StandardError, &block)
  @rescuers[kind] = block
  self
end

#runs_at(id = DEFAULT_GROUP) ⇒ Object



140
141
142
143
# File 'lib/debouncer.rb', line 140

def runs_at(id = DEFAULT_GROUP)
  thread = @timeouts[id]
  thread && thread[:run_at]
end

#sleeping?Boolean

Returns:

  • (Boolean)


136
137
138
# File 'lib/debouncer.rb', line 136

def sleeping?
  @timeouts.length.nonzero?
end

#to_procObject



132
133
134
# File 'lib/debouncer.rb', line 132

def to_proc
  method(:call).to_proc
end