adhearsion 2.1.2 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,13 @@
1
1
  # [develop](https://github.com/adhearsion/adhearsion)
2
2
 
3
+ # [2.1.3](https://github.com/adhearsion/adhearsion/compare/v2.1.2...v2.1.3) - [2012-10-11](https://rubygems.org/gems/adhearsion/versions/2.1.3)
4
+ * Bugfix: Originating call is now answered before joining calls using `CallController#dial`
5
+ * Bugfix: Output controller methods no longer falsely detect a string with a colon as a URI for an audio file
6
+ * Bugfix: `CallController#record` takes timeout in seconds instead of milliseconds
7
+ * Bugfix: Generating controllers given lower case names now works properly
8
+ * Update: Bump Celluloid dependency
9
+ * CS: Log when a controller is executed on a call
10
+
3
11
  # [2.1.2](https://github.com/adhearsion/adhearsion/compare/v2.1.1...v2.1.2) - [2012-09-16](https://rubygems.org/gems/adhearsion/versions/2.1.2)
4
12
  * Bugfix: Celluloid 0.12.x dependency now disallowed due to incompatible API changes.
5
13
  * Bugfix: Generated Gemfiles no longer pessimistically locked. Matches promise of SemVer compliance.
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.add_runtime_dependency 'activesupport', ["~> 3.0"]
21
21
  s.add_runtime_dependency 'adhearsion-loquacious', ["~> 1.9"]
22
22
  s.add_runtime_dependency 'bundler', ["~> 1.0"]
23
- s.add_runtime_dependency 'celluloid', ["~> 0.11.0"]
23
+ s.add_runtime_dependency 'celluloid', ["~> 0.12", ">= 0.12.1"]
24
24
  s.add_runtime_dependency 'countdownlatch'
25
25
  s.add_runtime_dependency 'deep_merge'
26
26
  s.add_runtime_dependency 'ffi', ["~> 1.0"]
@@ -17,3 +17,18 @@ Feature: Adhearsion controller generator
17
17
 
18
18
  And the file "lib/test_controller.rb" should contain "class TestController < Adhearsion::CallController"
19
19
  And the file "spec/test_controller_spec.rb" should contain "describe TestController"
20
+
21
+ Scenario: Generate a controller with lower-case name
22
+ When I run `ahn create path/somewhere`
23
+ And I cd to "path/somewhere"
24
+ And I run `ahn generate controller test_controller`
25
+ Then the following directories should exist:
26
+ | lib |
27
+ | spec |
28
+
29
+ And the following files should exist:
30
+ | lib/test_controller.rb |
31
+ | spec/test_controller_spec.rb |
32
+
33
+ And the file "lib/test_controller.rb" should contain "class TestController < Adhearsion::CallController"
34
+ And the file "spec/test_controller_spec.rb" should contain "describe TestController"
@@ -321,18 +321,10 @@ module Adhearsion
321
321
  end
322
322
 
323
323
  def execute_controller(controller = nil, completion_callback = nil, &block)
324
- raise ArgumentError if controller && block_given?
325
- call = current_actor
326
- controller ||= CallController.new call, &block
327
- Thread.new do
328
- catching_standard_errors do
329
- begin
330
- CallController.exec controller
331
- ensure
332
- completion_callback.call call if completion_callback
333
- end
334
- end
335
- end.tap { |t| Adhearsion::Process.important_threads << t }
324
+ raise ArgumentError, "Cannot supply a controller and a block at the same time" if controller && block_given?
325
+ controller ||= CallController.new current_actor, &block
326
+ logger.info "Executing controller #{controller.inspect}"
327
+ controller.bg_exec completion_callback
336
328
  end
337
329
 
338
330
  # @private
@@ -41,12 +41,7 @@ module Adhearsion
41
41
  # @param [CallController] controller
42
42
  #
43
43
  def exec(controller)
44
- new_controller = catch :pass_controller do
45
- controller.execute!
46
- nil
47
- end
48
-
49
- exec new_controller if new_controller
44
+ controller.exec
50
45
  end
51
46
 
52
47
  #
@@ -76,6 +71,32 @@ module Adhearsion
76
71
  @call, @metadata, @block = call, metadata || {}, block
77
72
  end
78
73
 
74
+ #
75
+ # Execute the controller, allowing passing control to another controller
76
+ #
77
+ def exec(controller = self)
78
+ new_controller = catch :pass_controller do
79
+ controller.execute!
80
+ nil
81
+ end
82
+
83
+ exec new_controller if new_controller
84
+ end
85
+
86
+ def bg_exec(completion_callback = nil)
87
+ Thread.new do
88
+ catching_standard_errors do
89
+ exec_with_callback completion_callback
90
+ end
91
+ end.tap { |t| Adhearsion::Process.important_threads << t }
92
+ end
93
+
94
+ def exec_with_callback(completion_callback = nil)
95
+ exec
96
+ ensure
97
+ completion_callback.call call if completion_callback
98
+ end
99
+
79
100
  # @private
80
101
  def execute!(*options)
81
102
  call.register_controller! self
@@ -243,7 +264,7 @@ module Adhearsion
243
264
 
244
265
  # @private
245
266
  def inspect
246
- "#<#{self.class} call=#{call.id}, metadata=#{metadata.inspect}>"
267
+ "#<#{self.class} call=#{call.alive? ? call.id : ''}, metadata=#{metadata.inspect}>"
247
268
  end
248
269
  end#class
249
270
  end
@@ -118,6 +118,7 @@ module Adhearsion
118
118
 
119
119
  if new_call.alive? && new_call.active?
120
120
  logger.debug "#dial joining call #{new_call.id} to #{@call.id}"
121
+ @call.answer
121
122
  new_call.join @call
122
123
  status.answer!
123
124
  end
@@ -20,11 +20,25 @@ module Adhearsion
20
20
  end
21
21
 
22
22
  def detect_type(output)
23
- result = nil
24
- result = :time if [Date, Time, DateTime].include? output.class
25
- result = :numeric if output.kind_of?(Numeric) || output =~ /^\d+$/
26
- result = :audio if !result && (/^\//.match(output.to_s) || URI::regexp.match(output.to_s))
27
- result ||= :text
23
+ case output
24
+ when Date, Time, DateTime
25
+ :time
26
+ when Numeric, /^\d+$/
27
+ :numeric
28
+ when /^\//, ->(string) { uri? string }
29
+ :audio
30
+ else
31
+ :text
32
+ end
33
+ end
34
+
35
+ def uri?(string)
36
+ uri = URI.parse string
37
+ !!uri.scheme
38
+ rescue URI::BadURIError
39
+ false
40
+ rescue URI::InvalidURIError
41
+ false
28
42
  end
29
43
 
30
44
  #
@@ -21,11 +21,11 @@ module Adhearsion
21
21
  # @option options [Boolean, Optional] :async Execute asynchronously. Defaults to false
22
22
  # @option options [Boolean, Optional] :start_beep Indicates whether subsequent record will be preceded with a beep. Default is true.
23
23
  # @option options [Boolean, Optional] :start_paused Whether subsequent record will start in PAUSE mode. Default is false.
24
- # @option options [String, Optional] :max_duration Indicates the maximum duration (milliseconds) for a recording.
24
+ # @option options [String, Optional] :max_duration Indicates the maximum duration (seconds) for a recording.
25
25
  # @option options [String, Optional] :format File format used during recording.
26
26
  # @option options [String, Optional] :format File format used during recording.
27
- # @option options [String, Optional] :initial_timeout Controls how long (milliseconds) the recognizer should wait after the end of the prompt for the caller to speak before sending a Recorder event.
28
- # @option options [String, Optional] :final_timeout Controls the length (milliseconds) of a period of silence after callers have spoken to conclude they finished.
27
+ # @option options [String, Optional] :initial_timeout Controls how long (seconds) the recognizer should wait after the end of the prompt for the caller to speak before sending a Recorder event.
28
+ # @option options [String, Optional] :final_timeout Controls the length (seconds) of a period of silence after callers have spoken to conclude they finished.
29
29
  # @option options [Boolean, Optional] :interruptible Allows the recording to be terminated by any single DTMF key, default is false
30
30
  #
31
31
  # @return Punchblock::Component::Record
@@ -35,6 +35,9 @@ module Adhearsion
35
35
  interruptible = options.delete :interruptible
36
36
  interrupt_key = '0123456789#*'
37
37
  stopper_component = nil
38
+ [:max_duration, :initial_timeout, :final_timeout].each do |k|
39
+ options[k] = options[k].to_i * 1000 if options[k]
40
+ end
38
41
 
39
42
  component = Punchblock::Component::Record.new options
40
43
  component.register_event_handler Punchblock::Event::Complete do |event|
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- class <%= @controller_name %> < Adhearsion::CallController
3
+ class <%= @controller_name.camelcase %> < Adhearsion::CallController
4
4
  def run
5
5
  end
6
6
  end
@@ -1,10 +1,10 @@
1
1
  # encoding: utf-8
2
2
 
3
- describe <%= @controller_name %> do
3
+ describe <%= @controller_name.camelcase %> do
4
4
 
5
5
  let(:mock_call) { mock 'Call', :to => '1112223333', :from => "2223334444" }
6
6
  let(:metadata) { {} }
7
- subject { <%= @controller_name %>.new(mock_call, metadata) }
7
+ subject { <%= @controller_name.camelcase %>.new(mock_call, metadata) }
8
8
 
9
9
  it "should have empty metadata" do
10
10
  subject.metadata.should eq({})
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Adhearsion
4
- VERSION = '2.1.2'
4
+ VERSION = '2.1.3'
5
5
  end
@@ -89,6 +89,7 @@ module Adhearsion
89
89
  end
90
90
 
91
91
  it "joins the new call to the existing one on answer" do
92
+ flexmock(call).should_receive(:answer).once
92
93
  flexmock(other_mock_call).should_receive(:join).once.with(call)
93
94
 
94
95
  dial_in_thread
@@ -102,6 +103,7 @@ module Adhearsion
102
103
  end
103
104
 
104
105
  it "hangs up the new call when the dial unblocks" do
106
+ flexmock(call).should_receive(:answer).once
105
107
  flexmock(other_mock_call).should_receive(:join).once.with(call)
106
108
 
107
109
  dial_in_thread
@@ -148,6 +150,7 @@ module Adhearsion
148
150
 
149
151
  context "when the call is answered and joined" do
150
152
  it "has an overall dial status of :answer" do
153
+ flexmock(call).should_receive(:answer).once
151
154
  flexmock(other_mock_call).should_receive(:join).once.with(call)
152
155
 
153
156
  t = dial_in_thread
@@ -208,6 +211,7 @@ module Adhearsion
208
211
  end
209
212
 
210
213
  it "dials all parties and joins the first one to answer, hanging up the rest" do
214
+ flexmock(call).should_receive(:answer).once
211
215
  flexmock(other_mock_call).should_receive(:join).once.with(call)
212
216
  flexmock(second_other_mock_call).should_receive(:hangup).once
213
217
 
@@ -232,6 +236,7 @@ module Adhearsion
232
236
  end
233
237
 
234
238
  it "unblocks when the joined call unjoins, allowing it to proceed further" do
239
+ flexmock(call).should_receive(:answer).once
235
240
  flexmock(other_mock_call).should_receive(:join).once.with(call)
236
241
  flexmock(second_other_mock_call).should_receive(:hangup).once
237
242
 
@@ -344,6 +349,7 @@ module Adhearsion
344
349
 
345
350
  context "when a call is answered and joined, and the other ends with an error" do
346
351
  it "has an overall dial status of :answer" do
352
+ flexmock(call).should_receive(:answer).once
347
353
  flexmock(other_mock_call).should_receive(:join).once.with(call)
348
354
  flexmock(second_other_mock_call).should_receive(:hangup).once
349
355
 
@@ -392,6 +398,7 @@ module Adhearsion
392
398
  describe "if someone answers before the timeout elapses" do
393
399
  it "should not abort until the far end hangs up" do
394
400
  flexmock(other_mock_call).should_receive(:dial).once.with(to, hsh(:timeout => timeout))
401
+ flexmock(call).should_receive(:answer).once
395
402
  flexmock(other_mock_call).should_receive(:join).once.with(call)
396
403
  flexmock(other_mock_call).should_receive(:hangup).once
397
404
  flexmock(OutboundCall).should_receive(:new).and_return other_mock_call
@@ -464,6 +471,7 @@ module Adhearsion
464
471
  it "should join the calls if the call is still active after execution of the call controller" do
465
472
  flexmock(other_mock_call).should_receive(:hangup).once
466
473
  other_mock_call['confirm'] = true
474
+ flexmock(call).should_receive(:answer).once
467
475
  flexmock(other_mock_call).should_receive(:join).once.with(call)
468
476
 
469
477
  t = dial_in_thread
@@ -485,6 +493,7 @@ module Adhearsion
485
493
  other_mock_call << mock_end
486
494
  end
487
495
  other_mock_call['confirm'] = false
496
+ flexmock(call).should_receive(:answer).never
488
497
  flexmock(other_mock_call).should_receive(:join).never.with(call)
489
498
 
490
499
  t = dial_in_thread
@@ -79,9 +79,10 @@ module Adhearsion
79
79
  subject.detect_type(number).should be :numeric
80
80
  end
81
81
 
82
- it "returns text as a fallback" do
83
- output = "Hello"
84
- subject.detect_type(output).should be :text
82
+ ["Foo", "Foo bar", "The answer: foo", "The answer could be foo/bar"].each do |string|
83
+ it "detects '#{string}' as text" do
84
+ subject.detect_type(string).should be :text
85
+ end
85
86
  end
86
87
  end
87
88
  end
@@ -8,19 +8,23 @@ module Adhearsion
8
8
  include CallControllerTestHelpers
9
9
 
10
10
  describe "#record" do
11
+ let(:max_duration) { 5 }
11
12
  let(:options) do
12
13
  {
13
14
  :start_beep => true,
14
- :max_duration => 5000
15
+ :max_duration => max_duration
15
16
  }
16
17
  end
17
- let(:component) { Punchblock::Component::Record.new options }
18
+ let(:parsed_options) do
19
+ options.merge(max_duration: max_duration * 1000)
20
+ end
21
+ let(:component) { Punchblock::Component::Record.new parsed_options }
18
22
  let(:response) { Punchblock::Event::Complete.new }
19
23
 
20
24
  describe "with :async => true and an :on_complete callback" do
21
25
  before do
22
26
  component
23
- flexmock(Punchblock::Component::Record).should_receive(:new).once.with(options).and_return component
27
+ flexmock(Punchblock::Component::Record).should_receive(:new).once.with(parsed_options).and_return component
24
28
  expect_message_waiting_for_response component
25
29
  @rec = Queue.new
26
30
  subject.record(options.merge(async: true)) { |rec| @rec.push rec }
@@ -63,7 +67,7 @@ module Adhearsion
63
67
  describe "with :async => false" do
64
68
  before do
65
69
  component
66
- flexmock(Punchblock::Component::Record).should_receive(:new).once.with(options).and_return component
70
+ flexmock(Punchblock::Component::Record).should_receive(:new).once.with(parsed_options).and_return component
67
71
  expect_component_execution component
68
72
  @rec = Queue.new
69
73
  subject.record(options.merge(:async => false)) { |rec| @rec.push rec }
@@ -113,7 +117,7 @@ module Adhearsion
113
117
  describe "check for the return value" do
114
118
  it "returns a Record component" do
115
119
  component
116
- flexmock(Punchblock::Component::Record).should_receive(:new).once.with(options).and_return component
120
+ flexmock(Punchblock::Component::Record).should_receive(:new).once.with(parsed_options).and_return component
117
121
  expect_component_execution component
118
122
  subject.record(options.merge(:async => false)).should be == component
119
123
  component.request!
@@ -177,7 +177,7 @@ module Adhearsion
177
177
  call.should_receive(:answer).once.ordered
178
178
  subject.should_receive(:after).never.ordered
179
179
 
180
- CallController.exec subject
180
+ subject.exec
181
181
  end
182
182
 
183
183
  it "should execute after_call callbacks before passing control" do
@@ -185,7 +185,7 @@ module Adhearsion
185
185
  subject.should_receive(:foobar).once.ordered
186
186
  call.should_receive(:answer).once.ordered
187
187
 
188
- CallController.exec subject
188
+ subject.exec
189
189
  end
190
190
  end
191
191
 
@@ -735,20 +735,21 @@ module Adhearsion
735
735
 
736
736
  describe "#execute_controller" do
737
737
  let(:latch) { CountDownLatch.new 1 }
738
- let(:mock_controller) { flexmock 'CallController' }
738
+ let(:mock_controller) { flexmock CallController.new(subject) }
739
739
 
740
740
  before do
741
741
  flexmock subject.wrapped_object, :write_and_await_response => true
742
742
  end
743
743
 
744
- it "should call #execute on the controller instance" do
745
- flexmock(CallController).should_receive(:exec).once.with mock_controller
744
+ it "should call #bg_exec on the controller instance" do
745
+ mock_controller.should_receive(:exec).once
746
746
  subject.execute_controller mock_controller, lambda { |call| latch.countdown! }
747
747
  latch.wait(3).should be_true
748
748
  end
749
749
 
750
750
  it "should use the passed block as a controller if none is specified" do
751
- flexmock(CallController).should_receive(:exec).once.with CallController
751
+ mock_controller.should_receive(:exec).once
752
+ flexmock(CallController).should_receive(:new).once.and_return mock_controller
752
753
  subject.execute_controller nil, lambda { |call| latch.countdown! } do
753
754
  foo
754
755
  end
@@ -46,7 +46,7 @@ module Adhearsion
46
46
  context "by dead call object" do
47
47
  before do
48
48
  @call_id = deleted_call.id
49
- deleted_call.kill
49
+ Celluloid::Actor.kill deleted_call
50
50
  deleted_call.should_not be_alive
51
51
  subject.remove_inactive_call deleted_call
52
52
  end
@@ -55,7 +55,7 @@ module Adhearsion
55
55
 
56
56
  let(:call_id) { rand }
57
57
  let(:offer) { Punchblock::Event::Offer.new.tap { |o| o.target_call_id = call_id } }
58
- let(:mock_call) { flexmock('Call', :id => call_id).tap { |call| call.should_ignore_missing } }
58
+ let(:mock_call) { flexmock Call.new, :id => call_id }
59
59
 
60
60
  describe "starts the client with the default values" do
61
61
  subject { initialize_punchblock }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adhearsion
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
4
+ version: 2.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2012-09-16 00:00:00.000000000 Z
15
+ date: 2012-10-11 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activesupport
@@ -69,7 +69,10 @@ dependencies:
69
69
  requirements:
70
70
  - - ~>
71
71
  - !ruby/object:Gem::Version
72
- version: 0.11.0
72
+ version: '0.12'
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 0.12.1
73
76
  type: :runtime
74
77
  prerelease: false
75
78
  version_requirements: !ruby/object:Gem::Requirement
@@ -77,7 +80,10 @@ dependencies:
77
80
  requirements:
78
81
  - - ~>
79
82
  - !ruby/object:Gem::Version
80
- version: 0.11.0
83
+ version: '0.12'
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: 0.12.1
81
87
  - !ruby/object:Gem::Dependency
82
88
  name: countdownlatch
83
89
  requirement: !ruby/object:Gem::Requirement
@@ -624,7 +630,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
624
630
  version: '0'
625
631
  segments:
626
632
  - 0
627
- hash: -3730460706990513122
633
+ hash: -4325641181526579229
628
634
  required_rubygems_version: !ruby/object:Gem::Requirement
629
635
  none: false
630
636
  requirements:
@@ -633,7 +639,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
633
639
  version: '0'
634
640
  segments:
635
641
  - 0
636
- hash: -3730460706990513122
642
+ hash: -4325641181526579229
637
643
  requirements: []
638
644
  rubyforge_project:
639
645
  rubygems_version: 1.8.23