adhearsion 2.0.1 → 2.1.0

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.
Files changed (62) hide show
  1. data/.travis.yml +4 -3
  2. data/CHANGELOG.md +30 -0
  3. data/README.markdown +1 -0
  4. data/adhearsion.gemspec +3 -4
  5. data/bin/ahn +0 -20
  6. data/features/cli_create.feature +1 -1
  7. data/features/cli_restart.feature +25 -1
  8. data/features/cli_start.feature +0 -2
  9. data/features/plugin_generator.feature +66 -15
  10. data/features/support/env.rb +0 -13
  11. data/lib/adhearsion.rb +26 -6
  12. data/lib/adhearsion/call.rb +42 -7
  13. data/lib/adhearsion/call_controller.rb +5 -2
  14. data/lib/adhearsion/call_controller/dial.rb +92 -50
  15. data/lib/adhearsion/call_controller/input.rb +19 -6
  16. data/lib/adhearsion/call_controller/menu_dsl/menu.rb +4 -0
  17. data/lib/adhearsion/call_controller/output.rb +143 -161
  18. data/lib/adhearsion/call_controller/output/abstract_player.rb +30 -0
  19. data/lib/adhearsion/call_controller/output/async_player.rb +26 -0
  20. data/lib/adhearsion/call_controller/output/formatter.rb +81 -0
  21. data/lib/adhearsion/call_controller/output/player.rb +25 -0
  22. data/lib/adhearsion/call_controller/record.rb +19 -2
  23. data/lib/adhearsion/events.rb +3 -0
  24. data/lib/adhearsion/foundation.rb +12 -6
  25. data/lib/adhearsion/foundation/exception_handler.rb +8 -6
  26. data/lib/adhearsion/generators/app/templates/README.md +13 -0
  27. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +7 -1
  28. data/lib/adhearsion/generators/plugin/plugin_generator.rb +1 -0
  29. data/lib/adhearsion/generators/plugin/templates/plugin-template.gemspec.tt +3 -7
  30. data/lib/adhearsion/generators/plugin/templates/spec/spec_helper.rb.tt +0 -1
  31. data/lib/adhearsion/outbound_call.rb +15 -5
  32. data/lib/adhearsion/punchblock_plugin.rb +13 -2
  33. data/lib/adhearsion/punchblock_plugin/initializer.rb +13 -12
  34. data/lib/adhearsion/router.rb +43 -2
  35. data/lib/adhearsion/router/evented_route.rb +15 -0
  36. data/lib/adhearsion/router/openended_route.rb +16 -0
  37. data/lib/adhearsion/router/route.rb +31 -13
  38. data/lib/adhearsion/router/unaccepting_route.rb +11 -0
  39. data/lib/adhearsion/version.rb +1 -1
  40. data/pre-commit +14 -1
  41. data/spec/adhearsion/call_controller/dial_spec.rb +105 -10
  42. data/spec/adhearsion/call_controller/input_spec.rb +19 -21
  43. data/spec/adhearsion/call_controller/output/async_player_spec.rb +67 -0
  44. data/spec/adhearsion/call_controller/output/formatter_spec.rb +90 -0
  45. data/spec/adhearsion/call_controller/output/player_spec.rb +65 -0
  46. data/spec/adhearsion/call_controller/output_spec.rb +436 -190
  47. data/spec/adhearsion/call_controller/record_spec.rb +49 -6
  48. data/spec/adhearsion/call_controller_spec.rb +10 -2
  49. data/spec/adhearsion/call_spec.rb +138 -0
  50. data/spec/adhearsion/calls_spec.rb +1 -1
  51. data/spec/adhearsion/outbound_call_spec.rb +48 -8
  52. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +34 -23
  53. data/spec/adhearsion/router/evented_route_spec.rb +34 -0
  54. data/spec/adhearsion/router/openended_route_spec.rb +61 -0
  55. data/spec/adhearsion/router/route_spec.rb +26 -4
  56. data/spec/adhearsion/router/unaccepting_route_spec.rb +72 -0
  57. data/spec/adhearsion/router_spec.rb +107 -2
  58. data/spec/adhearsion_spec.rb +19 -0
  59. data/spec/capture_warnings.rb +28 -21
  60. data/spec/spec_helper.rb +2 -3
  61. data/spec/support/call_controller_test_helpers.rb +31 -30
  62. metadata +32 -29
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Adhearsion
6
+ class Router
7
+ describe EventedRoute do
8
+ let(:name) { 'catchall' }
9
+
10
+ subject { Route.new name }
11
+
12
+ before { subject.extend described_class }
13
+
14
+ it { should be_evented }
15
+
16
+ describe "dispatching a call" do
17
+ let(:call) { Call.new }
18
+
19
+ context "via a block" do
20
+ subject :route do
21
+ Route.new 'foobar' do |c|
22
+ c.foo
23
+ end
24
+ end
25
+
26
+ it "should yield the call to the block" do
27
+ flexmock(call).should_receive(:foo).once
28
+ route.dispatch call
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Adhearsion
6
+ class Router
7
+ describe OpenendedRoute do
8
+ let(:name) { 'catchall' }
9
+
10
+ subject { Route.new name }
11
+
12
+ before { subject.extend described_class }
13
+
14
+ it { should be_openended }
15
+
16
+ describe "dispatching a call" do
17
+ let(:call) { Call.new }
18
+
19
+ let(:latch) { CountDownLatch.new 1 }
20
+
21
+ before { flexmock(call.wrapped_object).should_receive :write_and_await_response }
22
+
23
+ context "via a call controller" do
24
+ let(:controller) { CallController }
25
+ subject(:route) { Route.new 'foobar', controller }
26
+
27
+ it "should accept the call" do
28
+ flexmock(call).should_receive(:accept).once
29
+ route.dispatch call
30
+ end
31
+
32
+ it "should instruct the call to use an instance of the controller" do
33
+ flexmock(call).should_receive(:execute_controller).once.with controller, Proc
34
+ route.dispatch call
35
+ end
36
+
37
+ it "should not hangup the call after all controllers have executed" do
38
+ flexmock(call).should_receive(:hangup).never
39
+ route.dispatch call, lambda { latch.countdown! }
40
+ latch.wait(2).should be true
41
+ end
42
+ end
43
+
44
+ context "via a block" do
45
+ let :route do
46
+ Route.new 'foobar' do
47
+ :foobar
48
+ end
49
+ end
50
+
51
+ it "should instruct the call to use a CallController with the correct block" do
52
+ flexmock(call).should_receive(:execute_controller).once.with(CallController, Proc).and_return do |controller|
53
+ controller.block.call.should be == :foobar
54
+ end
55
+ route.dispatch call
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -14,6 +14,11 @@ module Adhearsion
14
14
  ]
15
15
  end
16
16
 
17
+ subject { Route.new name }
18
+
19
+ it { should_not be_evented }
20
+ it { should be_accepting }
21
+
17
22
  describe "with a class target and guards" do
18
23
  let(:target) { CallController }
19
24
 
@@ -104,27 +109,44 @@ module Adhearsion
104
109
 
105
110
  let(:latch) { CountDownLatch.new 1 }
106
111
 
112
+ before { flexmock(call.wrapped_object).should_receive :write_and_await_response }
113
+
107
114
  context "via a call controller" do
108
115
  let(:controller) { CallController }
109
116
  let(:route) { Route.new 'foobar', controller }
110
117
 
118
+ it "should accept the call" do
119
+ flexmock(call).should_receive(:accept).once
120
+ route.dispatch call
121
+ end
122
+
111
123
  it "should instruct the call to use an instance of the controller" do
112
124
  flexmock(call).should_receive(:execute_controller).once.with controller, Proc
113
- route.dispatcher.call call
125
+ route.dispatch call
114
126
  end
115
127
 
116
128
  it "should hangup the call after all controllers have executed" do
117
129
  flexmock(call).should_receive(:hangup).once
118
- route.dispatcher.call call, lambda { latch.countdown! }
130
+ route.dispatch call, lambda { latch.countdown! }
119
131
  latch.wait(2).should be true
120
132
  end
121
133
 
134
+ context 'with the :ahn_prevent_hangup call variable set' do
135
+ before { call[:ahn_prevent_hangup] = true }
136
+
137
+ it "should not hangup the call after controller execution" do
138
+ flexmock(call).should_receive(:hangup).never
139
+ route.dispatch call, lambda { latch.countdown! }
140
+ latch.wait(2).should be true
141
+ end
142
+ end
143
+
122
144
  context "if hangup raises a Call::Hangup" do
123
145
  before { flexmock(call).should_receive(:hangup).once.and_raise Call::Hangup }
124
146
 
125
147
  it "should not raise an exception" do
126
148
  lambda do
127
- route.dispatcher.call call, lambda { latch.countdown! }
149
+ route.dispatch call, lambda { latch.countdown! }
128
150
  latch.wait(2).should be true
129
151
  end.should_not raise_error
130
152
  end
@@ -142,7 +164,7 @@ module Adhearsion
142
164
  flexmock(call).should_receive(:execute_controller).once.with(CallController, Proc).and_return do |controller|
143
165
  controller.block.call.should be == :foobar
144
166
  end
145
- route.dispatcher.call call
167
+ route.dispatch call
146
168
  end
147
169
  end
148
170
  end
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Adhearsion
6
+ class Router
7
+ describe UnacceptingRoute do
8
+ let(:name) { 'catchall' }
9
+
10
+ subject { Route.new name }
11
+
12
+ before { subject.extend described_class }
13
+
14
+ it { should_not be_accepting }
15
+
16
+ describe "dispatching a call" do
17
+ let(:call) { Call.new }
18
+
19
+ let(:latch) { CountDownLatch.new 1 }
20
+
21
+ before { flexmock(call.wrapped_object).should_receive :write_and_await_response }
22
+
23
+ context "via a call controller" do
24
+ let(:controller) { CallController }
25
+ subject(:route) { Route.new 'foobar', controller }
26
+
27
+ it "should not accept the call" do
28
+ flexmock(call).should_receive(:accept).never
29
+ route.dispatch call
30
+ end
31
+
32
+ it "should instruct the call to use an instance of the controller" do
33
+ flexmock(call).should_receive(:execute_controller).once.with controller, Proc
34
+ route.dispatch call
35
+ end
36
+
37
+ it "should hangup the call after all controllers have executed" do
38
+ flexmock(call).should_receive(:hangup).once
39
+ route.dispatch call, lambda { latch.countdown! }
40
+ latch.wait(2).should be true
41
+ end
42
+
43
+ context "if hangup raises a Call::Hangup" do
44
+ before { flexmock(call).should_receive(:hangup).once.and_raise Call::Hangup }
45
+
46
+ it "should not raise an exception" do
47
+ lambda do
48
+ route.dispatch call, lambda { latch.countdown! }
49
+ latch.wait(2).should be true
50
+ end.should_not raise_error
51
+ end
52
+ end
53
+ end
54
+
55
+ context "via a block" do
56
+ let :route do
57
+ Route.new 'foobar' do
58
+ :foobar
59
+ end
60
+ end
61
+
62
+ it "should instruct the call to use a CallController with the correct block" do
63
+ flexmock(call).should_receive(:execute_controller).once.with(CallController, Proc).and_return do |controller|
64
+ controller.block.call.should be == :foobar
65
+ end
66
+ route.dispatch call
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -53,6 +53,82 @@ module Adhearsion
53
53
  subject[2].guards.should be == []
54
54
  subject[2].target.should be_a Proc
55
55
  end
56
+
57
+ context "as evented" do
58
+ let(:router) do
59
+ Router.new do
60
+ route 'calls from fred', FooBarController, :from => 'fred'
61
+ evented do
62
+ route 'catchall' do |call|
63
+ :foo
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ it "should create a route which is evented" do
70
+ subject[0].should_not be_evented
71
+ subject[1].should be_evented
72
+ end
73
+ end
74
+
75
+ context "as unaccepting" do
76
+ let(:router) do
77
+ Router.new do
78
+ route 'calls from fred', FooBarController, :from => 'fred'
79
+ unaccepting do
80
+ route 'catchall' do |call|
81
+ :foo
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ it "should create a route with is unaccepting" do
88
+ subject[0].should be_accepting
89
+ subject[1].should_not be_accepting
90
+ end
91
+ end
92
+
93
+ context "as openended" do
94
+ let(:router) do
95
+ Router.new do
96
+ route 'calls from fred', FooBarController, :from => 'fred'
97
+ openended do
98
+ route 'catchall' do |call|
99
+ :foo
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ it "should create a route which is openended" do
106
+ subject[0].should_not be_openended
107
+ subject[1].should be_openended
108
+ end
109
+ end
110
+
111
+ context "as combined evented/unaccepting" do
112
+ let(:router) do
113
+ Router.new do
114
+ route 'calls from fred', FooBarController, :from => 'fred'
115
+ unaccepting do
116
+ evented do
117
+ route 'catchall' do |call|
118
+ :foo
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ it "should create a route which is evented and unaccepting" do
126
+ subject[0].should be_accepting
127
+ subject[0].should_not be_evented
128
+ subject[1].should be_evented
129
+ subject[1].should_not be_accepting
130
+ end
131
+ end
56
132
  end
57
133
 
58
134
  describe "matching a call" do
@@ -99,8 +175,37 @@ module Adhearsion
99
175
  let(:call) { flexmock 'Adhearsion::Call', :id => 'abc123' }
100
176
  let(:route) { subject.routes.first }
101
177
 
102
- it "should return the route's dispatcher" do
103
- subject.handle(call).should be route.dispatcher
178
+ it "should dispatch via the route" do
179
+ flexmock(route).should_receive(:dispatch).once.with call
180
+ subject.handle call
181
+ end
182
+
183
+ context "when there are no routes" do
184
+ subject do
185
+ Router.new {}
186
+ end
187
+
188
+ let(:call) { flexmock 'Adhearsion::Call', :id => 'abc123' }
189
+
190
+ it "should return a dispatcher which rejects the call as an error" do
191
+ call.should_receive(:reject).once.with(:error)
192
+ subject.handle call
193
+ end
194
+ end
195
+
196
+ context "when no routes match" do
197
+ subject do
198
+ Router.new do
199
+ route 'too-specific', FooBarController, :to => 'foo'
200
+ end
201
+ end
202
+
203
+ let(:call) { flexmock 'Adhearsion::Call', :id => 'abc123', :to => 'bar' }
204
+
205
+ it "should return a dispatcher which rejects the call as an error" do
206
+ call.should_receive(:reject).once.with(:error)
207
+ subject.handle call
208
+ end
104
209
  end
105
210
  end
106
211
  end
@@ -3,6 +3,25 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Adhearsion do
6
+ describe "#root=" do
7
+ it "should update properly the config root variable" do
8
+ Adhearsion.root = "./"
9
+ Adhearsion.config[:platform].root.should be == Dir.getwd
10
+ end
11
+
12
+ it "should update properly the config root variable when path is nil" do
13
+ Adhearsion.root = nil
14
+ Adhearsion.config[:platform].root.should be_nil
15
+ end
16
+ end
17
+
18
+ describe "#root" do
19
+ it "should return the set root" do
20
+ Adhearsion.root = "./"
21
+ Adhearsion.root.should be == Dir.getwd
22
+ end
23
+ end
24
+
6
25
  describe "#ahn_root=" do
7
26
  it "should update properly the config root variable" do
8
27
  Adhearsion.ahn_root = "./"
@@ -1,33 +1,40 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'tempfile'
4
- stderr_file = Tempfile.new "ahn.stderr"
4
+ stderr_file = Tempfile.new "ahn#{rand}.stderr"
5
+ warnings_filename = "tmp/ahnwarnings#{rand}.txt"
5
6
  $stderr.reopen stderr_file.path
6
7
  current_dir = Dir.pwd
7
8
 
8
9
  at_exit do
9
- stderr_file.rewind
10
- lines = stderr_file.read.split("\n").uniq
11
- stderr_file.close!
10
+ begin
11
+ stderr_file.rewind
12
+ lines = stderr_file.read.split("\n").uniq
13
+ stderr_file.close!
12
14
 
13
- ahn_warnings, other_warnings = lines.partition { |line| line.include?(current_dir) && !line.include?('vendor') && line.include?('warning') }
15
+ ahn_warnings, other_warnings = lines.partition { |line| line.include?(current_dir) && !line.include?('vendor') && line.include?('warning') }
14
16
 
15
- if ahn_warnings.any?
16
- puts
17
- puts "-" * 30 + " AHN Warnings: " + "-" * 30
18
- puts
19
- puts ahn_warnings.join("\n")
20
- puts
21
- puts "-" * 75
22
- puts
23
- end
17
+ if ahn_warnings.any?
18
+ puts
19
+ puts "-" * 30 + " AHN Warnings: " + "-" * 30
20
+ puts
21
+ puts ahn_warnings.join("\n")
22
+ puts
23
+ puts "-" * 75
24
+ puts
25
+ end
24
26
 
25
- if other_warnings.any?
26
- File.open('tmp/warnings.txt', 'w') { |f| f.write other_warnings.join("\n") }
27
- puts
28
- puts "Non-AHN warnings written to tmp/warnings.txt"
29
- puts
30
- end
27
+ if other_warnings.any?
28
+ File.open(warnings_filename, 'w') { |f| f.write other_warnings.join("\n") }
29
+ puts
30
+ puts "Non-AHN warnings written to #{warnings_filename}"
31
+ puts
32
+ end
31
33
 
32
- exit 1 if ahn_warnings.any? # fail the build...
34
+ exit 1 if ahn_warnings.any? # fail the build...
35
+ rescue => e
36
+ puts "Warning capture failed."
37
+ puts e.message
38
+ puts e.backtrace.join("\n")
39
+ end
33
40
  end