adhearsion 2.0.0.alpha2 → 2.0.0.alpha3

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 (66) hide show
  1. data/.gitignore +16 -14
  2. data/CHANGELOG.md +17 -1
  3. data/adhearsion.gemspec +14 -13
  4. data/features/cli_basic.feature +30 -0
  5. data/features/cli_create.feature +24 -0
  6. data/features/cli_daemon.feature +21 -0
  7. data/features/cli_generate.feature +9 -0
  8. data/features/cli_start.feature +33 -0
  9. data/features/cli_stop.feature +38 -0
  10. data/features/controller_generator.feature +19 -0
  11. data/features/plugin_generator.feature +55 -0
  12. data/lib/adhearsion.rb +5 -1
  13. data/lib/adhearsion/call.rb +57 -51
  14. data/lib/adhearsion/call_controller.rb +4 -20
  15. data/lib/adhearsion/call_controller/dial.rb +34 -4
  16. data/lib/adhearsion/calls.rb +4 -1
  17. data/lib/adhearsion/cli_commands.rb +31 -6
  18. data/lib/adhearsion/configuration.rb +2 -6
  19. data/lib/adhearsion/console.rb +56 -17
  20. data/lib/adhearsion/generators.rb +53 -2
  21. data/lib/adhearsion/generators/app/app_generator.rb +2 -24
  22. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +1 -1
  23. data/lib/adhearsion/generators/controller/controller_generator.rb +18 -0
  24. data/lib/adhearsion/generators/controller/templates/lib/controller.rb +4 -0
  25. data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +3 -0
  26. data/lib/adhearsion/generators/generator.rb +77 -0
  27. data/lib/adhearsion/generators/plugin/plugin_generator.rb +33 -0
  28. data/lib/adhearsion/generators/plugin/templates/.gitignore +9 -0
  29. data/lib/adhearsion/generators/plugin/templates/Gemfile.tt +4 -0
  30. data/lib/adhearsion/generators/plugin/templates/README.md.tt +2 -0
  31. data/lib/adhearsion/generators/plugin/templates/Rakefile.tt +1 -0
  32. data/lib/adhearsion/generators/plugin/templates/lib/plugin-template.rb.tt +5 -0
  33. data/lib/adhearsion/generators/plugin/templates/lib/plugin-template/controller_methods.rb.tt +10 -0
  34. data/lib/adhearsion/generators/plugin/templates/lib/plugin-template/plugin.rb.tt +29 -0
  35. data/lib/adhearsion/generators/plugin/templates/lib/plugin-template/version.rb.tt +3 -0
  36. data/lib/adhearsion/generators/plugin/templates/plugin-template.gemspec.tt +35 -0
  37. data/lib/adhearsion/generators/plugin/templates/spec/plugin-template/controller_methods_spec.rb.tt +26 -0
  38. data/lib/adhearsion/generators/plugin/templates/spec/spec_helper.rb.tt +13 -0
  39. data/lib/adhearsion/initializer.rb +14 -22
  40. data/lib/adhearsion/logging.rb +25 -16
  41. data/lib/adhearsion/outbound_call.rb +3 -3
  42. data/lib/adhearsion/plugin.rb +14 -0
  43. data/lib/adhearsion/punchblock_plugin.rb +6 -1
  44. data/lib/adhearsion/punchblock_plugin/initializer.rb +8 -8
  45. data/lib/adhearsion/tasks/configuration.rb +1 -1
  46. data/lib/adhearsion/version.rb +1 -1
  47. data/spec/adhearsion/call_controller/dial_spec.rb +108 -17
  48. data/spec/adhearsion/call_controller_spec.rb +7 -64
  49. data/spec/adhearsion/call_spec.rb +48 -29
  50. data/spec/adhearsion/calls_spec.rb +7 -0
  51. data/spec/adhearsion/configuration_spec.rb +14 -14
  52. data/spec/adhearsion/console_spec.rb +124 -4
  53. data/spec/adhearsion/generators_spec.rb +17 -0
  54. data/spec/adhearsion/initializer_spec.rb +22 -18
  55. data/spec/adhearsion/logging_spec.rb +78 -48
  56. data/spec/adhearsion/outbound_call_spec.rb +6 -15
  57. data/spec/adhearsion/plugin_spec.rb +18 -0
  58. data/spec/adhearsion/process_spec.rb +10 -1
  59. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +8 -6
  60. data/spec/spec_helper.rb +5 -2
  61. data/spec/support/call_controller_test_helpers.rb +1 -1
  62. data/spec/support/initializer_stubs.rb +1 -1
  63. metadata +106 -66
  64. data/features/cli.feature +0 -108
  65. data/lib/adhearsion/initializer/logging.rb +0 -33
  66. data/spec/adhearsion/initializer/logging_spec.rb +0 -55
@@ -41,14 +41,14 @@ module Adhearsion
41
41
 
42
42
  write_and_await_response(Punchblock::Command::Dial.new(options), wait_timeout).tap do |dial_command|
43
43
  @dial_command = dial_command
44
- Adhearsion.active_calls << self
44
+ Adhearsion.active_calls << current_actor
45
45
  end
46
46
  end
47
47
 
48
48
  def run_router
49
49
  catching_standard_errors do
50
- dispatcher = Adhearsion.router.handle self
51
- dispatcher.call self
50
+ dispatcher = Adhearsion.router.handle current_actor
51
+ dispatcher.call current_actor
52
52
  end
53
53
  end
54
54
 
@@ -105,6 +105,20 @@ module Adhearsion
105
105
  end
106
106
  end
107
107
 
108
+ ##
109
+ # Register generator classes
110
+ #
111
+ # @example
112
+ # FooBar = Class.new Adhearsion::Plugin do
113
+ # generators :'my_plugin:foo_generator' => FooGenerator
114
+ # end
115
+ #
116
+ def generators(mapping)
117
+ mapping.each_pair do |name, klass|
118
+ Generators.add_generator name, klass
119
+ end
120
+ end
121
+
108
122
  def subclasses
109
123
  @subclasses ||= []
110
124
  end
@@ -23,11 +23,16 @@ module Adhearsion
23
23
  end
24
24
 
25
25
  init :punchblock do
26
- Initializer.start
26
+ Initializer.init
27
+ end
28
+
29
+ run :punchblock do
30
+ Initializer.run
27
31
  end
28
32
 
29
33
  class << self
30
34
  delegate :client, :to => Initializer
35
+ delegate :connection, :to => Initializer
31
36
 
32
37
  def validate_number(value)
33
38
  return 1.0/0.0 if ["Infinity", 1.0/0.0].include? value
@@ -1,12 +1,12 @@
1
1
  module Adhearsion
2
2
  class PunchblockPlugin
3
3
  class Initializer
4
- cattr_accessor :config, :client, :dispatcher, :attempts
4
+ cattr_accessor :config, :client, :connection, :dispatcher, :attempts
5
5
 
6
6
  self.attempts = 0
7
7
 
8
8
  class << self
9
- def start
9
+ def init
10
10
  self.config = Adhearsion.config[:punchblock]
11
11
  connection_class = case (self.config.platform || :xmpp)
12
12
  when :xmpp
@@ -25,7 +25,7 @@ module Adhearsion
25
25
  :mixers_domain => self.config.mixers_domain
26
26
  }
27
27
 
28
- connection = connection_class.new connection_options
28
+ self.connection = connection_class.new connection_options
29
29
  self.client = ::Punchblock::Client.new :connection => connection
30
30
 
31
31
  # Tell the Punchblock connection that we are ready to process calls.
@@ -65,7 +65,9 @@ module Adhearsion
65
65
  Events.punchblock proc { |e| e.respond_to?(:call_id) }, :call_id do |event|
66
66
  dispatch_call_event event
67
67
  end
68
+ end
68
69
 
70
+ def run
69
71
  connect
70
72
  end
71
73
 
@@ -119,6 +121,7 @@ module Adhearsion
119
121
  when :booting, :rejecting
120
122
  call.reject :decline
121
123
  when :running
124
+ call.accept
122
125
  dispatcher = Adhearsion.router.handle call
123
126
  dispatcher.call call
124
127
  else
@@ -129,11 +132,8 @@ module Adhearsion
129
132
 
130
133
  def dispatch_call_event(event, latch = nil)
131
134
  if call = Adhearsion.active_calls.find(event.call_id)
132
- logger.debug "Event received for call #{call.id}: #{event.inspect}"
133
- Thread.new do
134
- call << event
135
- latch.countdown! if latch
136
- end
135
+ call.deliver_message! event
136
+ latch.countdown! if latch
137
137
  else
138
138
  logger.error "Event received for inactive call #{event.call_id}: #{event.inspect}"
139
139
  end
@@ -1,6 +1,6 @@
1
1
  namespace :config do
2
2
  desc "Show configuration values; accepts a parameter: [nil|platform|<plugin-name>|all]"
3
- task :show, :name, :needs => :environment do |t, args|
3
+ task :show, [:name] => [:environment] do |t, args|
4
4
  name = args.name.nil? ? :all : args.name.to_sym
5
5
  puts "\nAdhearsion.config do |config|\n\n"
6
6
  puts Adhearsion.config.description name, :show_values => true
@@ -1,5 +1,5 @@
1
1
  module Adhearsion #:nodoc:
2
- VERSION = '2.0.0.alpha2'
2
+ VERSION = '2.0.0.alpha3'
3
3
 
4
4
  class PkgVersion
5
5
  include Comparable
@@ -6,11 +6,11 @@ module Adhearsion
6
6
  include CallControllerTestHelpers
7
7
 
8
8
  let(:to) { 'sip:foo@bar.com' }
9
- let(:other_call_id) { rand }
9
+ let(:other_call_id) { new_uuid }
10
10
  let(:other_mock_call) { flexmock OutboundCall.new, :id => other_call_id }
11
11
 
12
12
  let(:second_to) { 'sip:baz@bar.com' }
13
- let(:second_other_call_id) { rand }
13
+ let(:second_other_call_id) { new_uuid }
14
14
  let(:second_other_mock_call) { flexmock OutboundCall.new, :id => second_other_call_id }
15
15
 
16
16
  let(:mock_end) { flexmock Punchblock::Event::End.new, :reason => :hangup }
@@ -18,26 +18,26 @@ module Adhearsion
18
18
 
19
19
  let(:latch) { CountDownLatch.new 1 }
20
20
 
21
- def mock_dial
22
- flexmock(OutboundCall).new_instances.should_receive(:dial).and_return true
23
- end
24
-
25
21
  describe "#dial" do
26
- it "should create a new call and return it" do
27
- mock_dial
28
- t = Thread.new do
29
- subject.dial(to, {}, latch).should be_a OutboundCall
22
+ it "should dial the call to the correct endpoint and return it" do
23
+ other_mock_call
24
+ flexmock(OutboundCall).should_receive(:new).and_return other_mock_call
25
+ flexmock(other_mock_call).should_receive(:dial).with(to, :from => 'foo').once
26
+ dial_thread = Thread.new do
27
+ subject.dial(to, :from => 'foo').should be_a OutboundCall
30
28
  end
31
- latch.countdown!
32
- t.join
29
+ sleep 0.1
30
+ other_mock_call << mock_end
31
+ dial_thread.join.should be_true
33
32
  end
34
33
 
35
- it "should dial the call to the correct endpoint" do
34
+ it "should default the caller ID to that of the original call" do
36
35
  other_mock_call
36
+ flexmock call, :from => 'sip:foo@bar.com'
37
37
  flexmock(OutboundCall).should_receive(:new).and_return other_mock_call
38
- flexmock(other_mock_call).should_receive(:dial).with(to, :from => 'foo').once
38
+ flexmock(other_mock_call).should_receive(:dial).with(to, :from => 'sip:foo@bar.com').once
39
39
  dial_thread = Thread.new do
40
- subject.dial to, :from => 'foo'
40
+ subject.dial to
41
41
  end
42
42
  sleep 0.1
43
43
  other_mock_call << mock_end
@@ -49,6 +49,7 @@ module Adhearsion
49
49
  other_mock_call
50
50
 
51
51
  flexmock(other_mock_call).should_receive(:dial).once
52
+ flexmock(other_mock_call).should_receive(:hangup).once
52
53
  flexmock(OutboundCall).should_receive(:new).and_return other_mock_call
53
54
 
54
55
  latch = CountDownLatch.new 1
@@ -65,11 +66,33 @@ module Adhearsion
65
66
  latch.wait(1).should be_true
66
67
  end
67
68
 
69
+ it "unblocks the original controller if the original call ends" do
70
+ other_mock_call
71
+
72
+ flexmock(other_mock_call).should_receive(:dial).once
73
+ flexmock(other_mock_call).should_receive(:hangup).once
74
+ flexmock(OutboundCall).should_receive(:new).and_return other_mock_call
75
+
76
+ latch = CountDownLatch.new 1
77
+
78
+ Thread.new do
79
+ subject.dial to
80
+ latch.countdown!
81
+ end
82
+
83
+ latch.wait(1).should be_false
84
+
85
+ call << mock_end
86
+
87
+ latch.wait(1).should be_true
88
+ end
89
+
68
90
  it "joins the new call to the existing one on answer" do
69
91
  other_mock_call
70
92
 
71
93
  flexmock(other_mock_call).should_receive(:dial).once
72
94
  flexmock(other_mock_call).should_receive(:join).once.with(call)
95
+ flexmock(other_mock_call).should_receive(:hangup).once
73
96
  flexmock(OutboundCall).should_receive(:new).and_return other_mock_call
74
97
 
75
98
  latch = CountDownLatch.new 1
@@ -86,6 +109,29 @@ module Adhearsion
86
109
 
87
110
  latch.wait(1).should be_true
88
111
  end
112
+
113
+ it "hangs up the new call when the dial unblocks" do
114
+ other_mock_call
115
+
116
+ flexmock(other_mock_call).should_receive(:dial).once
117
+ flexmock(other_mock_call).should_receive(:join).once.with(call)
118
+ flexmock(other_mock_call).should_receive(:hangup).once
119
+ flexmock(OutboundCall).should_receive(:new).and_return other_mock_call
120
+
121
+ latch = CountDownLatch.new 1
122
+
123
+ Thread.new do
124
+ subject.dial to
125
+ latch.countdown!
126
+ end
127
+
128
+ latch.wait(1).should be_false
129
+
130
+ other_mock_call << mock_answered
131
+ call << mock_end
132
+
133
+ latch.wait(1).should be_true
134
+ end
89
135
  end
90
136
 
91
137
  describe "with multiple third parties specified" do
@@ -95,10 +141,11 @@ module Adhearsion
95
141
 
96
142
  flexmock(other_mock_call).should_receive(:dial).once
97
143
  flexmock(other_mock_call).should_receive(:join).once.with(call)
98
- flexmock(other_mock_call).should_receive(:hangup!).never
144
+ flexmock(other_mock_call).should_receive(:hangup).once
99
145
 
100
146
  flexmock(second_other_mock_call).should_receive(:dial).once
101
- flexmock(second_other_mock_call).should_receive(:hangup!).once
147
+ flexmock(second_other_mock_call).should_receive(:join).never
148
+ flexmock(second_other_mock_call).should_receive(:hangup).twice
102
149
 
103
150
  flexmock(OutboundCall).should_receive(:new).and_return other_mock_call, second_other_mock_call
104
151
  latch = CountDownLatch.new 1
@@ -114,6 +161,49 @@ module Adhearsion
114
161
  other_mock_call << mock_answered
115
162
  other_mock_call << mock_end
116
163
 
164
+ latch.wait(1).should be_false
165
+
166
+ second_other_mock_call << mock_end
167
+
168
+ latch.wait(2).should be_true
169
+
170
+ t.join
171
+ calls = t.value
172
+ calls.should have(2).calls
173
+ calls.each { |c| c.should be_a OutboundCall }
174
+ end
175
+
176
+ it "unblocks when the joined call unjoins, allowing it to proceed further" do
177
+ other_mock_call
178
+ second_other_mock_call
179
+
180
+ flexmock(other_mock_call).should_receive(:dial).once
181
+ flexmock(other_mock_call).should_receive(:join).once.with(call)
182
+ flexmock(other_mock_call).should_receive(:hangup).once
183
+
184
+ flexmock(second_other_mock_call).should_receive(:dial).once
185
+ flexmock(second_other_mock_call).should_receive(:join).never
186
+ flexmock(second_other_mock_call).should_receive(:hangup).twice
187
+
188
+ flexmock(OutboundCall).should_receive(:new).and_return other_mock_call, second_other_mock_call
189
+ latch = CountDownLatch.new 1
190
+
191
+ t = Thread.new do
192
+ calls = subject.dial [to, second_to]
193
+ latch.countdown!
194
+ calls
195
+ end
196
+
197
+ latch.wait(1).should be_false
198
+
199
+ other_mock_call << mock_answered
200
+ other_mock_call << Punchblock::Event::Unjoined.new(:other_call_id => call.id)
201
+ other_mock_call << mock_end
202
+
203
+ latch.wait(1).should be_false
204
+
205
+ second_other_mock_call << mock_end
206
+
117
207
  latch.wait(2).should be_true
118
208
 
119
209
  t.join
@@ -130,6 +220,7 @@ module Adhearsion
130
220
  other_mock_call
131
221
 
132
222
  flexmock(other_mock_call).should_receive(:dial).once
223
+ flexmock(other_mock_call).should_receive(:hangup).once
133
224
  flexmock(OutboundCall).should_receive(:new).and_return other_mock_call
134
225
 
135
226
  latch = CountDownLatch.new 1
@@ -29,41 +29,7 @@ module Adhearsion
29
29
  describe "execution on a call" do
30
30
  before do
31
31
  flexmock subject, :execute_component_and_await_completion => nil
32
- flexmock call, :write_and_await_response => nil
33
- end
34
-
35
- context "when auto-accept is enabled" do
36
- before do
37
- Adhearsion.config.platform.automatically_accept_incoming_calls = true
38
- end
39
-
40
- it "should accept the call" do
41
- subject.should_receive(:accept).once.ordered
42
- subject.should_receive(:run).once.ordered
43
- subject.execute!
44
- end
45
-
46
- context "and accept is skipped" do
47
- before { subject.skip_accept! }
48
-
49
- it "should not accept the call" do
50
- subject.should_receive(:accept).never
51
- subject.should_receive(:run).once
52
- subject.execute!
53
- end
54
- end
55
- end
56
-
57
- context "when auto-accept is disabled" do
58
- before do
59
- Adhearsion.config.platform.automatically_accept_incoming_calls = false
60
- end
61
-
62
- it "should not accept the call" do
63
- subject.should_receive(:accept).never
64
- subject.should_receive(:run).once
65
- subject.execute!
66
- end
32
+ flexmock call.wrapped_object, :write_and_await_response => nil
67
33
  end
68
34
 
69
35
  it "catches Hangup exceptions and logs the hangup" do
@@ -137,9 +103,8 @@ module Adhearsion
137
103
 
138
104
  before do
139
105
  flexmock subject, :execute_component_and_await_completion => nil
140
- flexmock call, :write_and_await_response => nil
106
+ flexmock call.wrapped_object, :write_and_await_response => nil
141
107
  flexmock(Events).should_receive(:trigger).with(:exception, Exception).never
142
- Adhearsion.config.platform.automatically_accept_incoming_calls = true
143
108
  end
144
109
 
145
110
  it "should invoke another controller before returning to the current controller" do
@@ -155,12 +120,6 @@ module Adhearsion
155
120
  subject.execute!
156
121
  end
157
122
 
158
- it "should not attempt to accept the call again" do
159
- call.should_receive(:accept).once
160
-
161
- subject.execute!
162
- end
163
-
164
123
  it "should allow the outer controller to cease execution and handle remote hangups" do
165
124
  subject[:second_controller] = SecondControllerWithRemoteHangup
166
125
 
@@ -195,11 +154,10 @@ module Adhearsion
195
154
  subject { PassController.new call }
196
155
 
197
156
  before do
198
- flexmock(call).should_receive(:write_and_await_response).and_return nil
157
+ flexmock(call.wrapped_object).should_receive(:write_and_await_response).and_return nil
199
158
  flexmock subject, :execute_component_and_await_completion => nil
200
159
  flexmock(SecondController).new_instances.should_receive(:md_check).once.with :foo => 'bar'
201
160
  flexmock(Events).should_receive(:trigger).with(:exception, Exception).never
202
- Adhearsion.config.platform.automatically_accept_incoming_calls = true
203
161
  end
204
162
 
205
163
  let(:latch) { CountDownLatch.new 1 }
@@ -208,18 +166,12 @@ module Adhearsion
208
166
  subject.should_receive(:before).once.ordered
209
167
  call.should_receive(:answer).once.ordered
210
168
  subject.should_receive(:after).never.ordered
211
- call.should_receive(:hangup!).once.ordered
169
+ call.wrapped_object.should_receive(:hangup).once.ordered
212
170
 
213
171
  call.execute_controller subject, latch
214
172
  latch.wait(1).should be_true
215
173
  end
216
174
 
217
- it "should not attempt to accept the call again" do
218
- call.should_receive(:accept).once
219
-
220
- CallController.exec subject
221
- end
222
-
223
175
  it "should execute after_call callbacks before passing control" do
224
176
  subject.should_receive(:before).once.ordered
225
177
  subject.should_receive(:foobar).once.ordered
@@ -296,13 +248,6 @@ module Adhearsion
296
248
  end
297
249
  end
298
250
 
299
- describe '#accept' do
300
- it "should delegate to the call" do
301
- flexmock(call).should_receive(:accept).once.with(:foo)
302
- subject.accept :foo
303
- end
304
- end
305
-
306
251
  describe '#answer' do
307
252
  it "should delegate to the call" do
308
253
  flexmock(call).should_receive(:answer).once.with(:foo)
@@ -319,7 +264,7 @@ module Adhearsion
319
264
 
320
265
  describe '#hangup' do
321
266
  it "should delegate to the call" do
322
- flexmock(call).should_receive(:hangup!).once.with(:foo)
267
+ flexmock(call).should_receive(:hangup).once.with(:foo)
323
268
  subject.hangup :foo
324
269
  end
325
270
  end
@@ -378,13 +323,11 @@ describe ExampleCallController do
378
323
 
379
324
  before do
380
325
  flexmock subject, :execute_component_and_await_completion => nil
381
- flexmock call, :write_and_await_response => nil
382
- Adhearsion.config.platform.automatically_accept_incoming_calls = true
326
+ flexmock call.wrapped_object, :write_and_await_response => nil
383
327
  end
384
328
 
385
- it "should execute the before_call callbacks before accepting the call" do
329
+ it "should execute the before_call callbacks before processing the call" do
386
330
  subject.should_receive(:setup_models).twice.ordered
387
- subject.should_receive(:accept).once.ordered
388
331
  subject.should_receive(:join_to_conference).once.ordered
389
332
  subject.execute!
390
333
  end