call_center 0.1.1 → 0.1.2
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.
- data/README.md +2 -0
- data/Rakefile +7 -1
- data/VERSION +1 -1
- data/call_center.gemspec +1 -1
- data/lib/call_center.rb +4 -0
- data/test/call_center_test.rb +164 -160
- metadata +3 -3
data/README.md
CHANGED
@@ -3,6 +3,8 @@ Call Center
|
|
3
3
|
|
4
4
|
Support for defining call center workflows.
|
5
5
|
|
6
|
+
[](http://travis-ci.org/zendesk/call_center)
|
7
|
+
|
6
8
|
Overview
|
7
9
|
--------
|
8
10
|
Call Center streamlines the process of defining multi-party call workflows in your application. Particularly, with [Twilio](http://www.twilio.com/docs) in mind.
|
data/Rakefile
CHANGED
@@ -40,7 +40,13 @@ Rcov::RcovTask.new do |test|
|
|
40
40
|
test.rcov_opts << '--exclude "gems/*,lib/call_center/core_ext/object_instance_exec.rb"'
|
41
41
|
end
|
42
42
|
|
43
|
-
|
43
|
+
namespace :test do
|
44
|
+
Rake::TestTask.new(:dsl) do |t|
|
45
|
+
t.pattern = "minitest/**/*_test.rb"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
task :default => ['test', 'test:dsl']
|
44
50
|
|
45
51
|
require 'rake/rdoctask'
|
46
52
|
Rake::RDocTask.new do |rdoc|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/call_center.gemspec
CHANGED
data/lib/call_center.rb
CHANGED
@@ -54,6 +54,10 @@ module CallCenter
|
|
54
54
|
def current_state_machine
|
55
55
|
self.state_machines[self.call_flow_state_machine_name]
|
56
56
|
end
|
57
|
+
|
58
|
+
def inherited(subclass)
|
59
|
+
subclass.call_flow_state_machine_name ||= self.call_flow_state_machine_name
|
60
|
+
end
|
57
61
|
end
|
58
62
|
|
59
63
|
module InstanceMethods
|
data/test/call_center_test.rb
CHANGED
@@ -4,6 +4,7 @@ require 'call_center/test/dsl'
|
|
4
4
|
|
5
5
|
require 'test/examples/legacy_call'
|
6
6
|
require 'test/examples/call'
|
7
|
+
require 'test/examples/outbound_call'
|
7
8
|
require 'test/examples/non_standard_call'
|
8
9
|
require 'test/examples/multiple_flow_call'
|
9
10
|
require 'test/examples/dynamic_transition_call'
|
@@ -175,227 +176,230 @@ class CallCenterTest < Test::Unit::TestCase
|
|
175
176
|
end
|
176
177
|
end
|
177
178
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
@call.expects(:notify).with(:rendering_initial)
|
185
|
-
body @call.render
|
186
|
-
assert_select "Response>Say", "Hello World"
|
187
|
-
end
|
179
|
+
['Call', 'SubclassCall'].each do |class_name|
|
180
|
+
context class_name do
|
181
|
+
setup do
|
182
|
+
klass = class_name.constantize
|
183
|
+
@call = klass.new
|
184
|
+
end
|
188
185
|
|
189
|
-
|
190
|
-
|
191
|
-
|
186
|
+
should "render xml for initial state" do
|
187
|
+
@call.expects(:notify).with(:rendering_initial)
|
188
|
+
body @call.render
|
189
|
+
assert_select "Response>Say", "Hello World"
|
190
|
+
end
|
192
191
|
|
193
|
-
|
194
|
-
|
195
|
-
|
192
|
+
should "return customer url for event" do
|
193
|
+
assert_equal("/voice/calls/flow?event=voicemail_complete&actor=customer", @call.customer(:voicemail_complete))
|
194
|
+
end
|
196
195
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
@call.expects(:notify).with(:rendering_voicemail)
|
196
|
+
should "return agent url for event" do
|
197
|
+
assert_equal("/voice/calls/flow?event=voicemail_complete&actor=agent", @call.agent(:voicemail_complete))
|
198
|
+
end
|
201
199
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
200
|
+
should "render xml for voicemail state" do
|
201
|
+
@call.stubs(:agents_available?).returns(false)
|
202
|
+
@call.incoming_call!
|
203
|
+
@call.expects(:notify).with(:rendering_voicemail)
|
206
204
|
|
207
|
-
|
208
|
-
|
209
|
-
|
205
|
+
body @call.render
|
206
|
+
assert_select "Response>Say"
|
207
|
+
assert_select "Response>Record[action=/voice/calls/flow?event=voicemail_complete&actor=customer]"
|
208
|
+
end
|
210
209
|
|
211
|
-
|
212
|
-
|
213
|
-
|
210
|
+
should "render noop when no render block" do
|
211
|
+
@call.stubs(:agents_available?).returns(true)
|
212
|
+
@call.incoming_call!
|
214
213
|
|
215
|
-
|
216
|
-
|
214
|
+
body @call.render
|
215
|
+
assert_select "Response"
|
216
|
+
end
|
217
217
|
|
218
|
-
|
219
|
-
|
218
|
+
should "execute callbacks" do
|
219
|
+
@call.state = 'cancelled'
|
220
220
|
|
221
|
-
|
222
|
-
|
223
|
-
@call.expects(:notify).with(:after_failure).times(1)
|
221
|
+
@call.expects(:notify).with(:before_always).times(3)
|
222
|
+
@call.expects(:notify).with(:before_always_uniq).times(1)
|
224
223
|
|
225
|
-
|
226
|
-
|
227
|
-
|
224
|
+
@call.expects(:notify).with(:after_always).times(4)
|
225
|
+
@call.expects(:notify).with(:after_success).times(3)
|
226
|
+
@call.expects(:notify).with(:after_failure).times(1)
|
228
227
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
assert(!@call.customer_hangs_up)
|
233
|
-
end
|
228
|
+
@call.expects(:notify).with(:after_always_uniq).times(1)
|
229
|
+
@call.expects(:notify).with { |notification, transition| notification == :after_success_uniq && transition.kind_of?(StateMachine::Transition) }.times(1)
|
230
|
+
@call.expects(:notify).with(:after_failure_uniq).times(0)
|
234
231
|
|
235
|
-
|
236
|
-
|
237
|
-
|
232
|
+
@call.customer_end!
|
233
|
+
@call.customer_end!
|
234
|
+
@call.customer_end!
|
235
|
+
assert(!@call.customer_hangs_up)
|
236
|
+
end
|
238
237
|
|
239
|
-
|
240
|
-
|
238
|
+
should "execute callbacks in sequence" do
|
239
|
+
seq = sequence('callback_sequence')
|
240
|
+
@call.state = 'cancelled'
|
241
241
|
|
242
|
-
|
243
|
-
|
242
|
+
@call.expects(:notify).with(:before_always).in_sequence(seq)
|
243
|
+
@call.expects(:notify).with(:before_always_uniq).in_sequence(seq)
|
244
244
|
|
245
|
-
|
246
|
-
|
245
|
+
@call.expects(:notify).with(:after_always).in_sequence(seq)
|
246
|
+
@call.expects(:notify).with(:after_success).in_sequence(seq)
|
247
247
|
|
248
|
-
|
249
|
-
|
248
|
+
@call.expects(:notify).with(:after_always_uniq).in_sequence(seq)
|
249
|
+
@call.expects(:notify).with(:after_success_uniq, anything).in_sequence(seq)
|
250
250
|
|
251
|
-
|
252
|
-
(class << @call; self; end).class_eval do
|
253
|
-
include CallCenter::DeferredCallbacks
|
251
|
+
@call.customer_end!
|
254
252
|
end
|
255
|
-
@call.state = 'cancelled'
|
256
253
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
@call.expects(:notify).with(:after_failure).never
|
254
|
+
should "defer callbacks" do
|
255
|
+
(class << @call; self; end).class_eval do
|
256
|
+
include CallCenter::DeferredCallbacks
|
257
|
+
end
|
258
|
+
@call.state = 'cancelled'
|
263
259
|
|
264
|
-
|
265
|
-
|
260
|
+
@call.expects(:notify).with(:before_always).never
|
261
|
+
@call.expects(:notify).with(:before_always_uniq).never
|
266
262
|
|
267
|
-
|
268
|
-
|
263
|
+
@call.expects(:notify).with(:after_always).never
|
264
|
+
@call.expects(:notify).with(:after_success).never
|
265
|
+
@call.expects(:notify).with(:after_failure).never
|
269
266
|
|
270
|
-
|
267
|
+
@call.expects(:notify).with(:after_always_uniq).never
|
268
|
+
@call.expects(:notify).with(:after_success_uniq, anything).never
|
271
269
|
|
272
|
-
|
270
|
+
@call.customer_end!
|
271
|
+
assert(!@call.customer_hangs_up)
|
273
272
|
|
274
|
-
|
275
|
-
@call.expects(:notify).with(:before_always_uniq)
|
273
|
+
# Ready for deferred callbacks
|
276
274
|
|
277
|
-
|
275
|
+
seq = sequence('callback_sequence')
|
278
276
|
|
279
|
-
|
280
|
-
|
277
|
+
@call.expects(:notify).with(:before_always)
|
278
|
+
@call.expects(:notify).with(:before_always_uniq)
|
281
279
|
|
282
|
-
|
283
|
-
@call.expects(:notify).with(:after_success_uniq, anything)
|
280
|
+
@call.call_flow_run_deferred(:before_transition)
|
284
281
|
|
285
|
-
|
282
|
+
@call.expects(:notify).with(:after_always).times(2)
|
283
|
+
@call.expects(:notify).with(:after_success)
|
286
284
|
|
287
|
-
|
288
|
-
|
289
|
-
@call.expects(:notify).with(:after_always_uniq)
|
285
|
+
@call.expects(:notify).with(:after_always_uniq)
|
286
|
+
@call.expects(:notify).with(:after_success_uniq, anything)
|
290
287
|
|
291
|
-
|
288
|
+
@call.call_flow_run_deferred(:after_transition)
|
292
289
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
@call.call_flow_run_deferred(:after_failure)
|
297
|
-
end
|
290
|
+
@call.expects(:notify).with(:after_always).times(2)
|
291
|
+
@call.expects(:notify).with(:after_failure)
|
292
|
+
@call.expects(:notify).with(:after_always_uniq)
|
298
293
|
|
299
|
-
|
300
|
-
@call.stubs(:agents_available?).returns(true)
|
301
|
-
@call.incoming_call!
|
302
|
-
@call.expects(:redirect_to).with(:start_conference)
|
294
|
+
@call.call_flow_run_deferred(:after_failure)
|
303
295
|
|
304
|
-
|
305
|
-
|
296
|
+
# Empty
|
297
|
+
@call.call_flow_run_deferred(:before_transition)
|
298
|
+
@call.call_flow_run_deferred(:after_transition)
|
299
|
+
@call.call_flow_run_deferred(:after_failure)
|
300
|
+
end
|
306
301
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
302
|
+
should "asynchronously perform event" do
|
303
|
+
@call.stubs(:agents_available?).returns(true)
|
304
|
+
@call.incoming_call!
|
305
|
+
@call.expects(:redirect_to).with(:start_conference)
|
311
306
|
|
312
|
-
|
313
|
-
|
307
|
+
@call.redirect_and_start_conference!
|
308
|
+
end
|
314
309
|
|
315
|
-
|
316
|
-
|
317
|
-
@call.
|
318
|
-
|
319
|
-
end
|
310
|
+
should "asynchronously perform event with options" do
|
311
|
+
@call.stubs(:agents_available?).returns(true)
|
312
|
+
@call.incoming_call!
|
313
|
+
@call.expects(:redirect_to).with(:start_conference, :status => 'completed')
|
320
314
|
|
321
|
-
|
322
|
-
|
323
|
-
@call.draw_call_flow(:name => 'call_workflow', :font => 'Helvetica Neue')
|
324
|
-
end
|
315
|
+
@call.redirect_and_start_conference!(:status => 'completed')
|
316
|
+
end
|
325
317
|
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
@call.stubs(:customer).returns('the_flow')
|
331
|
-
} do
|
332
|
-
should_also { assert_received(@call, :notify) { |e| e.with(:rendering_voicemail) } }
|
333
|
-
and_also { assert_received(@call, :customer) { |e| e.with(:voicemail_complete) } }
|
334
|
-
and_render { "Response>Say" }
|
335
|
-
and_render { "Response>Record[action=the_flow]" }
|
318
|
+
should "raise error on missing method" do
|
319
|
+
assert_raises {
|
320
|
+
@call.i_am_missing!
|
321
|
+
}
|
336
322
|
end
|
337
323
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
should_render { "Response" }
|
324
|
+
should "draw state machine digraph" do
|
325
|
+
Call.state_machines[:state].expects(:draw).with(:name => 'call_workflow', :font => 'Helvetica Neue')
|
326
|
+
@call.draw_call_flow(:name => 'call_workflow', :font => 'Helvetica Neue')
|
342
327
|
end
|
343
328
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
329
|
+
context "using test DSL:" do
|
330
|
+
should_flow :on => :incoming_call, :initial => :voicemail, :when => Proc.new {
|
331
|
+
@call.stubs(:agents_available?).returns(false)
|
332
|
+
@call.stubs(:notify)
|
333
|
+
@call.stubs(:customer).returns('the_flow')
|
334
|
+
} do
|
335
|
+
should_also { assert_received(@call, :notify) { |e| e.with(:rendering_voicemail) } }
|
336
|
+
and_also { assert_received(@call, :customer) { |e| e.with(:voicemail_complete) } }
|
337
|
+
and_render { "Response>Say" }
|
338
|
+
and_render { "Response>Record[action=the_flow]" }
|
339
|
+
end
|
349
340
|
|
350
|
-
should_flow :on => :
|
351
|
-
|
341
|
+
should_flow :on => :incoming_call, :initial => :routing, :when => Proc.new {
|
342
|
+
@call.stubs(:agents_available?).returns(true)
|
343
|
+
} do
|
344
|
+
should_render { "Response" }
|
345
|
+
end
|
346
|
+
|
347
|
+
should_flow :on => :customer_hangs_up, :routing => :cancelled, :when => Proc.new {
|
348
|
+
@call.stubs(:notify)
|
349
|
+
} do
|
350
|
+
should_also { assert_received(@call, :notify) { |e| e.with(:cancelled).once } }
|
352
351
|
and_also { assert @call.cancelled? }
|
353
352
|
|
354
353
|
should_flow :on => :customer_hangs_up, :cancelled => :cancelled do
|
355
354
|
should_also { assert_received(@call, :notify) { |e| e.with(:cancelled).once } } # For above
|
356
355
|
and_also { assert @call.cancelled? }
|
356
|
+
|
357
|
+
should_flow :on => :customer_hangs_up, :cancelled => :cancelled do
|
358
|
+
should_also { assert_received(@call, :notify) { |e| e.with(:cancelled).once } } # For above
|
359
|
+
and_also { assert @call.cancelled? }
|
360
|
+
end
|
357
361
|
end
|
358
362
|
end
|
359
363
|
end
|
360
|
-
end
|
361
364
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
365
|
+
context "deferred and using test DSL:" do
|
366
|
+
setup do
|
367
|
+
(class << @call; self; end).class_eval do
|
368
|
+
include CallCenter::DeferredCallbacks
|
369
|
+
end
|
366
370
|
end
|
367
|
-
end
|
368
371
|
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
should_flow :on => :incoming_call, :initial => :routing, :when => Proc.new {
|
381
|
-
@call.stubs(:agents_available?).returns(true)
|
382
|
-
} do
|
383
|
-
should_render { "Response" }
|
384
|
-
end
|
372
|
+
should_flow :on => :incoming_call, :initial => :voicemail, :when => Proc.new {
|
373
|
+
@call.stubs(:agents_available?).returns(false)
|
374
|
+
@call.stubs(:notify)
|
375
|
+
@call.stubs(:customer).returns('the_flow')
|
376
|
+
} do
|
377
|
+
should_also { assert_received(@call, :notify) { |e| e.with(:rendering_voicemail) } }
|
378
|
+
and_also { assert_received(@call, :customer) { |e| e.with(:voicemail_complete) } }
|
379
|
+
and_render { "Response>Say" }
|
380
|
+
and_render { "Response>Record[action=the_flow]" }
|
381
|
+
end
|
385
382
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
383
|
+
should_flow :on => :incoming_call, :initial => :routing, :when => Proc.new {
|
384
|
+
@call.stubs(:agents_available?).returns(true)
|
385
|
+
} do
|
386
|
+
should_render { "Response" }
|
387
|
+
end
|
391
388
|
|
392
|
-
should_flow :on => :customer_hangs_up, :
|
393
|
-
|
389
|
+
should_flow :on => :customer_hangs_up, :routing => :cancelled, :when => Proc.new {
|
390
|
+
@call.stubs(:notify)
|
391
|
+
} do
|
392
|
+
should_also { assert_received(@call, :notify) { |e| e.with(:cancelled).once } }
|
394
393
|
and_also { assert @call.cancelled? }
|
395
394
|
|
396
395
|
should_flow :on => :customer_hangs_up, :cancelled => :cancelled do
|
397
396
|
should_also { assert_received(@call, :notify) { |e| e.with(:cancelled).once } } # For above
|
398
397
|
and_also { assert @call.cancelled? }
|
398
|
+
|
399
|
+
should_flow :on => :customer_hangs_up, :cancelled => :cancelled do
|
400
|
+
should_also { assert_received(@call, :notify) { |e| e.with(:cancelled).once } } # For above
|
401
|
+
and_also { assert @call.cancelled? }
|
402
|
+
end
|
399
403
|
end
|
400
404
|
end
|
401
405
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: call_center
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 2
|
10
|
+
version: 0.1.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Henry Hsu
|