electric_slide 0.5.0 → 0.5.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 49101ac1f213829f41caab6990cf3183d533f3e1
4
- data.tar.gz: 796e9cd5bbee24fcda95e58022cf1f83f8c4f2f0
2
+ SHA256:
3
+ metadata.gz: c782d5d738f3fb714ff7fb1f3ae8ab63189118ebee98136ceccd9f1d41790526
4
+ data.tar.gz: 253786fe5964259c43a90d0dc2132df9bf1509d75203276f9ef6c6cdfc736eb0
5
5
  SHA512:
6
- metadata.gz: 65de89ec11a21d769a14c50c0a359407b2805b096bf87ca35637502030cbc9bbdc0e3199345ecb52cdea49168d652d0ba19559f02e1edcb379789d9e8f032c3f
7
- data.tar.gz: 9ba4811599997c2831762642cd0e9b49dc98f9606054ef1de8f00448e9c4fe7dc680b0e173aa2ec3cf284bc245ba16c2b67c7d78474e52055c00cbd08503da3d
6
+ metadata.gz: bc24e7ae491fd5d7f3ce5925695d16c6ea9fcb0da4e49ca8258247289a498efcdd17c98a747740c813e8df82dfe368a15ae915b3df19e7b53c404a60b5614e8e
7
+ data.tar.gz: 474108814ec1a811b56c6a8c435d457429a1a447c6901660d1f6dd457c56cc00c4ef2b226e85b19c40fdf90e8ccd93d81243b1c78aea9551c635194c183a19fa
@@ -0,0 +1,27 @@
1
+ name: build
2
+ on:
3
+ - push
4
+ - pull_request
5
+ jobs:
6
+ build:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ ruby: ['2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', 'jruby-9.1.17', 'jruby-9.2.21']
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout
14
+ uses: actions/checkout@v3
15
+
16
+ - name: Setup Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ bundler: 1.17.3
21
+ bundler-cache: true
22
+
23
+ - name: Create build
24
+ run: bundle exec rake build
25
+
26
+ - name: Run Specs
27
+ run: bundle exec rake spec
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  *.swp
2
2
  Gemfile.lock
3
3
  pkg
4
+ .tool-versions
data/CHANGELOG.md CHANGED
@@ -1,4 +1,12 @@
1
1
  # [develop](https://github.com/adhearsion/electric_slide)
2
+ * Bugfix: Conditionally clear agent's call object if the current
3
+ call is the same as the previous call so that call object information
4
+ is not lost when the call goes from queued to connected
5
+
6
+ # [develop](https://github.com/adhearsion/electric_slide)
7
+ * Added `ElectricSlide::CallQueue#update_agent` to safely update a queued agent object's attributes
8
+ * Added `ElectricSlide::Agent#update` to update an agent object's attributes
9
+ * Added `ElectricSlide::Agent#callable?` to check if an agent can be called
2
10
 
3
11
  # [0.5.0](https://github.com/adhearsion/electric_slide/compare/v0.4.2...v0.5.0) - [2016-02-12](https://rubygems.org/gems/adhearsion/versions/0.5.0)
4
12
  * API Breakage: Fixed priority strategy now ensures that the same agent cannot be in multiple priorities
@@ -23,9 +23,9 @@ Gem::Specification.new do |s|
23
23
  s.rubygems_version = "1.2.0"
24
24
  s.summary = "Automatic Call Distributor for Adhearsion"
25
25
 
26
- s.add_runtime_dependency 'adhearsion'
26
+ s.add_runtime_dependency 'adhearsion', ['>= 2.5.0', '< 3.0.0']
27
27
  s.add_runtime_dependency 'countdownlatch'
28
- s.add_runtime_dependency 'activesupport'
28
+ s.add_runtime_dependency 'activesupport', ['<= 3.2.13']
29
29
  s.add_development_dependency 'rspec', ['~> 3.0']
30
30
  s.add_development_dependency 'timecop'
31
31
  s.add_development_dependency 'ci_reporter'
@@ -34,5 +34,9 @@ Gem::Specification.new do |s|
34
34
  s.add_development_dependency 'simplecov'
35
35
  s.add_development_dependency 'simplecov-rcov'
36
36
 
37
+ # These two are needed to keep compatibility with Ruby >= 2.2.0, <= 2.2.3
38
+ s.add_development_dependency 'ruby_dep', ['= 1.3.1']
39
+ s.add_development_dependency 'listen', ['<= 3.1.1']
40
+
37
41
  s.specification_version = 2
38
42
  end
@@ -15,6 +15,22 @@ class ElectricSlide
15
15
  @presence = opts[:presence] || :available
16
16
  end
17
17
 
18
+ # Updates an agent instance's attributes.
19
+ # @param [Hash] opts Agent attributes
20
+ # @return {Agent}
21
+ def update(attrs = {})
22
+ unless Hash === attrs
23
+ raise ArgumentError.new('Agent attributes must be a hash')
24
+ end
25
+
26
+ attrs.each do |attr, value|
27
+ setter = "#{attr}="
28
+ send setter, value
29
+ end
30
+
31
+ self
32
+ end
33
+
18
34
  def update_presence(new_presence, extra_params = {})
19
35
  old_presence = @presence
20
36
  @presence = new_presence
@@ -68,5 +84,10 @@ class ElectricSlide
68
84
  def from
69
85
  @call.from
70
86
  end
87
+
88
+ # Returns `true` if the agent can be called, i.e., has a contact address
89
+ def callable?
90
+ address.present?
91
+ end
71
92
  end
72
93
  end
@@ -165,13 +165,7 @@ class ElectricSlide
165
165
  abort DuplicateAgentError.new("Agent is already in the queue") if get_agent(agent.id)
166
166
 
167
167
  agent.queue = current_actor
168
-
169
- case @connection_type
170
- when :call
171
- abort ArgumentError.new("Agent has no callable address") unless agent.address
172
- when :bridge
173
- bridged_agent_health_check agent
174
- end
168
+ accept_agent! agent
175
169
 
176
170
  logger.info "Adding agent #{agent} to the queue"
177
171
  @agents << agent
@@ -182,6 +176,24 @@ class ElectricSlide
182
176
  async.check_for_connections
183
177
  end
184
178
 
179
+ # Updates a queued agent's attributes
180
+ def update_agent(agent, agent_attrs)
181
+ abort ArgumentError.new('Agent must not be `nil`') unless agent
182
+ unless get_agent(agent.id)
183
+ abort MissingAgentError.new('Agent is not in the queue')
184
+ end
185
+
186
+ # check if the agent is allowed to have the given set of attributes using
187
+ # a dupe, to preserve the state of the original in case of failure
188
+ agent.dup.tap do |double_agent|
189
+ double_agent.update agent_attrs
190
+ accept_agent! double_agent
191
+ end
192
+
193
+ agent.update agent_attrs
194
+ return_agent agent, agent.presence
195
+ end
196
+
185
197
  # Marks an agent as available to take a call. To be called after an agent completes a call
186
198
  # and is ready to take the next call.
187
199
  # @param [Agent] agent The {Agent} that is being returned to the queue
@@ -276,9 +288,10 @@ class ElectricSlide
276
288
  # @param [Agent] agent Agent to be connected
277
289
  # @param [Adhearsion::Call] call Caller to be connected
278
290
  def connect(agent, queued_call)
279
- unless queued_call.active?
291
+ unless queued_call && queued_call.active?
280
292
  logger.warn "Inactive queued call found in #connect"
281
293
  return_agent agent
294
+ return
282
295
  end
283
296
 
284
297
  queued_call[:agent] = agent
@@ -288,15 +301,11 @@ class ElectricSlide
288
301
  when :call
289
302
  call_agent agent, queued_call
290
303
  when :bridge
291
- unless agent.call && agent.call.active?
292
- logger.warn "Inactive agent call found in #connect, returning caller to queue"
293
- priority_enqueue queued_call
294
- end
295
304
  bridge_agent agent, queued_call
296
305
  end
297
306
  rescue *ENDED_CALL_EXCEPTIONS
298
307
  ignoring_ended_calls do
299
- if queued_call.active?
308
+ if queued_call && queued_call.active?
300
309
  logger.warn "Dead call exception in #connect but queued_call still alive, reinserting into queue"
301
310
  priority_enqueue queued_call
302
311
  end
@@ -392,9 +401,9 @@ class ElectricSlide
392
401
 
393
402
  agent_call.on_end do |end_event|
394
403
  # Ensure we don't return an agent that was removed or paused
404
+ old_call = agent.call
395
405
  conditionally_return_agent agent
396
-
397
- agent.call = nil
406
+ agent.call = nil if agent_return_method == :manual || agent.call == old_call
398
407
 
399
408
  agent.callback :disconnect, queue, agent_call, queued_call
400
409
 
@@ -418,6 +427,13 @@ class ElectricSlide
418
427
  end
419
428
 
420
429
  def bridge_agent(agent, queued_call)
430
+ unless agent.call && agent.call.active?
431
+ logger.warn "Inactive agent call found for Agent #{agent.id} while bridging. Logging out agent and returning caller to queue."
432
+ priority_enqueue queued_call
433
+ remove_agent agent
434
+ return
435
+ end
436
+
421
437
  # Stash caller ID to make log messages work even if calls end
422
438
  queued_caller_id = remote_party queued_call
423
439
  agent.call[:queued_call] = queued_call
@@ -455,6 +471,17 @@ class ElectricSlide
455
471
  end
456
472
  end
457
473
 
474
+ def accept_agent!(agent)
475
+ case @connection_type
476
+ when :call
477
+ unless agent.callable?
478
+ abort ArgumentError.new('Agent has no callable address')
479
+ end
480
+ when :bridge
481
+ bridged_agent_health_check agent
482
+ end
483
+ end
484
+
458
485
  # @private
459
486
  def bridged_agent_health_check(agent)
460
487
  if agent.presence == :available && @connection_type == :bridge
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  class ElectricSlide
3
- VERSION = '0.5.0'
3
+ VERSION = '0.5.1'
4
4
  end
@@ -63,4 +63,56 @@ describe ElectricSlide::Agent do
63
63
  expect(old_presence).to eq :unavailable
64
64
  expect(extra_params[:triggered_by]).to eq 'auto'
65
65
  end
66
+
67
+ describe '#update' do
68
+ it 'returns the agent' do
69
+ expect(subject.update).to eq(subject)
70
+ end
71
+
72
+ context 'when given a hash with an agent attribute as key' do
73
+ it "sets the corresponding agent's attribute to the given value" do
74
+ expect {
75
+ subject.update(address: '456@bar.net')
76
+ }.to change(subject, :address).from('123@foo.com').to('456@bar.net')
77
+ end
78
+ end
79
+
80
+ context 'when given an non-hash argument' do
81
+ it 'raises an error' do
82
+ expect {
83
+ subject.update(nil)
84
+ }.to raise_error(ArgumentError, 'Agent attributes must be a hash')
85
+ end
86
+ end
87
+
88
+ context 'when given a hash with a key that does not correspond to any agent attribute' do
89
+ it "raises an error" do
90
+ expect {
91
+ subject.update(blah: 1)
92
+ }.to raise_error(NoMethodError)
93
+ end
94
+ end
95
+ end
96
+
97
+ describe '#callable?' do
98
+ context 'when the agent has an address' do
99
+ before do
100
+ subject.address = 'Baker St.'
101
+ end
102
+
103
+ it 'returns `true`' do
104
+ expect(subject).to be_callable
105
+ end
106
+ end
107
+
108
+ context 'when the agent has no address' do
109
+ before do
110
+ subject.address = ''
111
+ end
112
+
113
+ it 'returns `false`' do
114
+ expect(subject).to_not be_callable
115
+ end
116
+ end
117
+ end
66
118
  end
@@ -54,6 +54,7 @@ describe ElectricSlide::CallQueue do
54
54
  let(:agent_id) { '123' }
55
55
  let(:agent) { ElectricSlide::Agent.new id: agent_id, address: '123', presence: :available }
56
56
  let!(:agent_call) { Adhearsion::OutboundCall.new }
57
+ let!(:new_agent_call) { Adhearsion::OutboundCall.new }
57
58
  let(:queued_call) { dummy_call }
58
59
  let(:connected_time) { DateTime.now }
59
60
 
@@ -71,6 +72,7 @@ describe ElectricSlide::CallQueue do
71
72
 
72
73
  before do
73
74
  allow(agent_call).to receive(:dial)
75
+ allow(new_agent_call).to receive(:dial)
74
76
  queue.add_agent agent
75
77
  queue.enqueue queued_call
76
78
  end
@@ -101,6 +103,16 @@ describe ElectricSlide::CallQueue do
101
103
  }.to change(agent, :call).from(agent_call).to(nil)
102
104
  end
103
105
 
106
+ it "does not unsets the agent's `call` attribute when the agent connects to another call" do
107
+ queue.remove_agent double_agent # We only want one agent in the queue so they get the next call
108
+ allow(Adhearsion::OutboundCall).to receive(:new) { new_agent_call }
109
+ queue.enqueue dummy_call # We want another call to be waiting after this call
110
+
111
+ expect {
112
+ agent_call << Punchblock::Event::End.new(reason: :hangup)
113
+ }.to change(agent, :call).from(agent_call).to(new_agent_call)
114
+ end
115
+
104
116
  context "when the return strategy is :auto" do
105
117
  let(:agent_return_method) { :auto }
106
118
 
@@ -287,6 +299,54 @@ describe ElectricSlide::CallQueue do
287
299
  end
288
300
  end
289
301
 
302
+ describe '#update_agent' do
303
+ let(:queue) { ElectricSlide::CallQueue.new(connection_type: :call) }
304
+ let(:agent) { ElectricSlide::Agent.new(id: '1', address: 'agent@example.com', presence: :on_call) }
305
+
306
+ before do
307
+ queue.add_agent(agent)
308
+ end
309
+
310
+ it 'updates the agent with the given attributes' do
311
+ expect {
312
+ queue.update_agent(agent, address: 'reagent@acme.com')
313
+ }.to change(agent, :address).from('agent@example.com').to('reagent@acme.com')
314
+ end
315
+
316
+ it 'returns the agent to the queue' do
317
+ expect(queue.wrapped_object).to receive(:return_agent).with(agent, :on_call)
318
+ queue.update_agent(agent, address: 'reagent@acme.com')
319
+ end
320
+
321
+ context 'when given a set of attributes that makes the agent unacceptable in the queue' do
322
+ it 'raises an error' do
323
+ expect {
324
+ queue.update_agent(agent, address: '')
325
+ }.to raise_error(ArgumentError, 'Agent has no callable address')
326
+ end
327
+
328
+ it "does not change the agent's attributes" do
329
+ expect { queue.update_agent(agent, address: '') }.to raise_error(ArgumentError)
330
+
331
+ expect(agent.id).to eq('1')
332
+ expect(agent.address).to eq('agent@example.com')
333
+ expect(agent.presence).to eq(:on_call)
334
+ end
335
+ end
336
+
337
+ context 'when given an agent not in the queue' do
338
+ before do
339
+ queue.remove_agent agent
340
+ end
341
+
342
+ it 'raises an error' do
343
+ expect {
344
+ queue.update_agent(agent, address: 'ghost@imf.com')
345
+ }.to raise_error(ElectricSlide::CallQueue::MissingAgentError, 'Agent is not in the queue')
346
+ end
347
+ end
348
+ end
349
+
290
350
  describe '#return_agent' do
291
351
  let(:queue) { ElectricSlide::CallQueue.new }
292
352
  let(:agent) { ElectricSlide::Agent.new(id: '1', address: 'agent@example.com', presence: :on_call) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: electric_slide
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Klang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-12 00:00:00.000000000 Z
11
+ date: 2022-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: adhearsion
@@ -16,14 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 2.5.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 3.0.0
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '0'
29
+ version: 2.5.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: countdownlatch
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -42,16 +48,16 @@ dependencies:
42
48
  name: activesupport
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
- - - ">="
51
+ - - "<="
46
52
  - !ruby/object:Gem::Version
47
- version: '0'
53
+ version: 3.2.13
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
- - - ">="
58
+ - - "<="
53
59
  - !ruby/object:Gem::Version
54
- version: '0'
60
+ version: 3.2.13
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: rspec
57
63
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +156,34 @@ dependencies:
150
156
  - - ">="
151
157
  - !ruby/object:Gem::Version
152
158
  version: '0'
159
+ - !ruby/object:Gem::Dependency
160
+ name: ruby_dep
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - '='
164
+ - !ruby/object:Gem::Version
165
+ version: 1.3.1
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - '='
171
+ - !ruby/object:Gem::Version
172
+ version: 1.3.1
173
+ - !ruby/object:Gem::Dependency
174
+ name: listen
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "<="
178
+ - !ruby/object:Gem::Version
179
+ version: 3.1.1
180
+ type: :development
181
+ prerelease: false
182
+ version_requirements: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "<="
185
+ - !ruby/object:Gem::Version
186
+ version: 3.1.1
153
187
  description: Automatic Call Distributor (ACD) for Adhearsion. Currently implements
154
188
  only Round Robin distribution strategies.
155
189
  email: dev&adhearsion.com
@@ -157,10 +191,10 @@ executables: []
157
191
  extensions: []
158
192
  extra_rdoc_files: []
159
193
  files:
194
+ - ".github/workflows/build.yml"
160
195
  - ".gitignore"
161
196
  - ".lgtm"
162
197
  - ".rspec"
163
- - ".travis.yml"
164
198
  - CHANGELOG.md
165
199
  - Gemfile
166
200
  - Guardfile
@@ -201,8 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
235
  - !ruby/object:Gem::Version
202
236
  version: '0'
203
237
  requirements: []
204
- rubyforge_project:
205
- rubygems_version: 2.5.1
238
+ rubygems_version: 3.1.6
206
239
  signing_key:
207
240
  specification_version: 2
208
241
  summary: Automatic Call Distributor for Adhearsion
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.0
4
- - 2.2.1
5
- - 2.2.2
6
- - jruby-9.0.0.0
7
- matrix:
8
- allow_failures:
9
- - rvm: ruby-head
10
- sudo: false