adhearsion 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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