punchblock 1.6.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/CHANGELOG.md +8 -0
  2. data/Guardfile +1 -1
  3. data/Rakefile +1 -1
  4. data/lib/punchblock/client.rb +5 -8
  5. data/lib/punchblock/client/component_registry.rb +8 -1
  6. data/lib/punchblock/component/component_node.rb +1 -0
  7. data/lib/punchblock/component/output.rb +15 -1
  8. data/lib/punchblock/translator/asterisk.rb +1 -1
  9. data/lib/punchblock/translator/asterisk/call.rb +1 -1
  10. data/lib/punchblock/translator/asterisk/component/output.rb +5 -1
  11. data/lib/punchblock/translator/freeswitch.rb +7 -1
  12. data/lib/punchblock/translator/freeswitch/call.rb +10 -6
  13. data/lib/punchblock/translator/freeswitch/component.rb +1 -1
  14. data/lib/punchblock/version.rb +1 -1
  15. data/punchblock.gemspec +1 -2
  16. data/spec/punchblock/client/component_registry_spec.rb +7 -0
  17. data/spec/punchblock/client_spec.rb +14 -12
  18. data/spec/punchblock/command_node_spec.rb +2 -2
  19. data/spec/punchblock/component/component_node_spec.rb +10 -3
  20. data/spec/punchblock/component/input_spec.rb +1 -1
  21. data/spec/punchblock/component/output_spec.rb +32 -27
  22. data/spec/punchblock/component/record_spec.rb +5 -5
  23. data/spec/punchblock/connection/asterisk_spec.rb +7 -7
  24. data/spec/punchblock/connection/freeswitch_spec.rb +8 -8
  25. data/spec/punchblock/connection/xmpp_spec.rb +9 -9
  26. data/spec/punchblock/translator/asterisk/call_spec.rb +65 -65
  27. data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +17 -20
  28. data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +13 -16
  29. data/spec/punchblock/translator/asterisk/component/input_spec.rb +9 -12
  30. data/spec/punchblock/translator/asterisk/component/output_spec.rb +62 -29
  31. data/spec/punchblock/translator/asterisk/component/record_spec.rb +38 -42
  32. data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +2 -2
  33. data/spec/punchblock/translator/asterisk/component_spec.rb +5 -5
  34. data/spec/punchblock/translator/asterisk_spec.rb +55 -55
  35. data/spec/punchblock/translator/freeswitch/call_spec.rb +80 -54
  36. data/spec/punchblock/translator/freeswitch/component/flite_output_spec.rb +7 -10
  37. data/spec/punchblock/translator/freeswitch/component/input_spec.rb +8 -10
  38. data/spec/punchblock/translator/freeswitch/component/output_spec.rb +9 -12
  39. data/spec/punchblock/translator/freeswitch/component/record_spec.rb +31 -34
  40. data/spec/punchblock/translator/freeswitch/component/tts_output_spec.rb +7 -10
  41. data/spec/punchblock/translator/freeswitch/component_spec.rb +6 -6
  42. data/spec/punchblock/translator/freeswitch_spec.rb +27 -27
  43. data/spec/punchblock_spec.rb +5 -6
  44. data/spec/spec_helper.rb +2 -3
  45. data/spec/support/mock_connection_with_event_handler.rb +8 -19
  46. metadata +5 -21
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # [develop](https://github.com/adhearsion/punchblock)
2
2
 
3
+ # [v1.7.0](https://github.com/adhearsion/punchblock/compare/v1.6.1...v1.7.0) - [2012-12-13](https://rubygems.org/gems/punchblock/versions/1.7.0)
4
+ * Feature: Support for the renderer attribute added to the Output component.
5
+ * Feature: FreeSWITCH and Asterisk translators now use the :renderer attribute on Output
6
+ * Bugfix: Fixed scenario where executing the ANSWER application on FreeSWITCH on an already answered call caused FS to stop accepting commands.
7
+ * Bugfix: Plug a severe memory leak
8
+ * Bugfix: Raise an error immediately if trying to execute an invalid media engine on Asterisk
9
+ * Bugfix: Handle a wider variety of types when configuring media engines on Asterisk and FreeSWITCH, such as Strings instead of Symbols
10
+
3
11
  # [v1.6.1](https://github.com/adhearsion/punchblock/compare/v1.6.0...v1.6.1) - [2012-11-14](https://rubygems.org/gems/punchblock/versions/1.6.1)
4
12
  * Bugfix: Safer component attribute writer conversion
5
13
 
data/Guardfile CHANGED
@@ -1,4 +1,4 @@
1
- guard 'rspec', :version => 2, :cli => '--format documentation' do
1
+ guard 'rspec', :cli => '--format documentation' do
2
2
  watch(%r{^spec/.+_spec\.rb$})
3
3
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
4
  watch('spec/spec_helper.rb') { "spec/" }
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ require 'ci/reporter/rake/rspec'
7
7
  RSpec::Core::RakeTask.new(:spec) do |spec|
8
8
  spec.pattern = 'spec/**/*_spec.rb'
9
9
  spec.rspec_opts = '--color'
10
- spec.ruby_opts = "-w -r./spec/capture_warnings"
10
+ # spec.ruby_opts = "-w -r./spec/capture_warnings"
11
11
  end
12
12
 
13
13
  task :default => :spec
@@ -19,7 +19,6 @@ module Punchblock
19
19
  @event_queue = Queue.new
20
20
  @connection = options[:connection]
21
21
  @connection.event_handler = lambda { |event| self.handle_event event } if @connection
22
- register_initial_handlers
23
22
  @component_registry = ComponentRegistry.new
24
23
  @write_timeout = options[:write_timeout] || 3
25
24
  end
@@ -29,7 +28,7 @@ module Punchblock
29
28
  if event.source
30
29
  event.source.add_event event
31
30
  else
32
- trigger_handler :event, event
31
+ trigger_handler(:event, event) || event_queue.push(event)
33
32
  end
34
33
  end
35
34
 
@@ -37,12 +36,6 @@ module Punchblock
37
36
  register_handler :event, *guards, &block
38
37
  end
39
38
 
40
- def register_initial_handlers
41
- register_handler_with_priority :event, -10 do |event|
42
- event_queue.push event
43
- end
44
- end
45
-
46
39
  def register_component(component)
47
40
  component_registry << component
48
41
  end
@@ -51,6 +44,10 @@ module Punchblock
51
44
  component_registry.find_by_id component_id
52
45
  end
53
46
 
47
+ def delete_component_registration(component)
48
+ component_registry.delete component
49
+ end
50
+
54
51
  def execute_command(command, options = {})
55
52
  async = options.has_key?(:async) ? options.delete(:async) : true
56
53
  command.client = self
@@ -7,7 +7,7 @@ module Punchblock
7
7
  @mutex = Mutex.new
8
8
  @components = Hash.new
9
9
  end
10
-
10
+
11
11
  def <<(component)
12
12
  @mutex.synchronize do
13
13
  @components[component.component_id] = component
@@ -19,6 +19,13 @@ module Punchblock
19
19
  @components[component_id]
20
20
  end
21
21
  end
22
+
23
+ def delete(component)
24
+ @mutex.synchronize do
25
+ id = @components.key component
26
+ @components.delete id
27
+ end
28
+ end
22
29
  end
23
30
  end
24
31
  end
@@ -49,6 +49,7 @@ module Punchblock
49
49
 
50
50
  def complete_event=(other)
51
51
  return if @complete_event_resource.set_yet?
52
+ client.delete_component_registration self if client
52
53
  @complete_event_resource.resource = other
53
54
  complete!
54
55
  end
@@ -157,8 +157,22 @@ module Punchblock
157
157
  write_attr :'max-time', other, :to_i
158
158
  end
159
159
 
160
+ ##
161
+ # @return [String] the rendering engine requested by the component
162
+ #
163
+ def renderer
164
+ read_attr :renderer
165
+ end
166
+
167
+ ##
168
+ # @param [String] the rendering engine to use with this component
169
+ #
170
+ def renderer=(renderer)
171
+ write_attr :renderer, renderer
172
+ end
173
+
160
174
  def inspect_attributes
161
- super + [:voice, :ssml, :interrupt_on, :start_offset, :start_paused, :repeat_interval, :repeat_times, :max_time]
175
+ super + [:voice, :ssml, :interrupt_on, :start_offset, :start_paused, :repeat_interval, :repeat_times, :max_time, :renderer]
162
176
  end
163
177
 
164
178
  state_machine :state do
@@ -58,7 +58,7 @@ module Punchblock
58
58
 
59
59
  def shutdown
60
60
  @calls.values.each(&:shutdown!)
61
- current_actor.terminate!
61
+ terminate
62
62
  end
63
63
 
64
64
  def handle_ami_event(event)
@@ -45,7 +45,7 @@ module Punchblock
45
45
  end
46
46
 
47
47
  def shutdown
48
- current_actor.terminate!
48
+ terminate
49
49
  end
50
50
 
51
51
  def to_s
@@ -27,7 +27,9 @@ module Punchblock
27
27
 
28
28
  output_component = current_actor
29
29
 
30
- case @media_engine
30
+ rendering_engine = @component_node.renderer ? @component_node.renderer : @media_engine
31
+
32
+ case rendering_engine.to_sym
31
33
  when :asterisk, nil
32
34
  raise OptionError, "A voice value is unsupported on Asterisk." if @component_node.voice
33
35
  raise OptionError, 'Interrupt digits are not allowed with early media.' if early && @component_node.interrupt_on
@@ -61,6 +63,8 @@ module Punchblock
61
63
  @call.send_agi_action! 'EXEC Swift', swift_doc do |complete_event|
62
64
  output_component.send_complete_event! success_reason
63
65
  end
66
+ else
67
+ raise OptionError, 'The renderer foobar is unsupported.'
64
68
  end
65
69
  rescue UnrenderableDocError => e
66
70
  with_error 'unrenderable document error', e.message
@@ -8,6 +8,7 @@ module Punchblock
8
8
  class Freeswitch
9
9
  include Celluloid
10
10
  include HasGuardedHandlers
11
+ include DeadActorSafety
11
12
 
12
13
  extend ActiveSupport::Autoload
13
14
 
@@ -65,6 +66,7 @@ module Punchblock
65
66
  register_handler :es, [:has_key?, :other_leg_unique_id] => true do |event|
66
67
  call = call_with_id event[:other_leg_unique_id]
67
68
  call.handle_es_event! event if call
69
+ throw :pass
68
70
  end
69
71
 
70
72
  register_handler :es, lambda { |event| es_event_known_call? event } do |event|
@@ -78,7 +80,11 @@ module Punchblock
78
80
  end
79
81
 
80
82
  def finalize
81
- @calls.values.each(&:terminate)
83
+ @calls.values.each do |call|
84
+ safe_from_dead_actors do
85
+ call.terminate
86
+ end
87
+ end
82
88
  end
83
89
 
84
90
  def handle_es_event(event)
@@ -68,6 +68,7 @@ module Punchblock
68
68
  register_handler :es, :event_name => 'CHANNEL_ANSWER' do
69
69
  @answered = true
70
70
  send_pb_event Event::Answered.new
71
+ throw :pass
71
72
  end
72
73
 
73
74
  register_handler :es, :event_name => 'CHANNEL_STATE', [:[], :channel_call_state] => 'RINGING' do
@@ -99,7 +100,6 @@ module Punchblock
99
100
  send_pb_event Event::Unjoined.new(:call_id => other_call_id)
100
101
  end
101
102
 
102
-
103
103
  register_handler :es, [:has_key?, :scope_variable_punchblock_component_id] => true do |event|
104
104
  if component = component_with_id(event[:scope_variable_punchblock_component_id])
105
105
  safe_from_dead_actors { component.handle_es_event event if component.alive? }
@@ -176,12 +176,15 @@ module Punchblock
176
176
  application 'respond', '180 Ringing'
177
177
  command.response = true
178
178
  when Command::Answer
179
- command_id = Punchblock.new_uuid
180
- register_tmp_handler :es, :event_name => 'CHANNEL_ANSWER', [:[], :scope_variable_punchblock_command_id] => command_id do
181
- @answered = true
179
+ if answered?
182
180
  command.response = true
181
+ else
182
+ command_id = Punchblock.new_uuid
183
+ register_tmp_handler :es, :event_name => 'CHANNEL_ANSWER', [:[], :scope_variable_punchblock_command_id] => command_id do
184
+ command.response = true
185
+ end
186
+ application 'answer', "%[punchblock_command_id=#{command_id}]"
183
187
  end
184
- application 'answer', "%[punchblock_command_id=#{command_id}]"
185
188
  when Command::Hangup
186
189
  hangup
187
190
  command.response = true
@@ -195,7 +198,8 @@ module Punchblock
195
198
  hangup REJECT_TO_HANGUP_REASON[command.reason]
196
199
  command.response = true
197
200
  when Punchblock::Component::Output
198
- case media_engine
201
+ media_renderer = command.renderer ? command.renderer : media_engine
202
+ case media_renderer.to_sym
199
203
  when :freeswitch, :native, nil
200
204
  execute_component Component::Output, command
201
205
  when :flite
@@ -47,7 +47,7 @@ module Punchblock
47
47
  c << recording if recording
48
48
  end
49
49
  send_event event
50
- current_actor.terminate!
50
+ terminate
51
51
  end
52
52
 
53
53
  def send_event(event)
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Punchblock
4
- VERSION = "1.6.1"
4
+ VERSION = "1.7.0"
5
5
  end
data/punchblock.gemspec CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
27
27
  s.add_runtime_dependency %q<activesupport>, ["~> 3.0"]
28
28
  s.add_runtime_dependency %q<state_machine>, ["~> 1.0"]
29
29
  s.add_runtime_dependency %q<future-resource>, ["~> 1.0"]
30
- s.add_runtime_dependency %q<has-guarded-handlers>, ["~> 1.3"]
30
+ s.add_runtime_dependency %q<has-guarded-handlers>, ["~> 1.5"]
31
31
  s.add_runtime_dependency %q<celluloid>, ["~> 0.12", ">= 0.12.3"]
32
32
  s.add_runtime_dependency %q<ruby_ami>, ["~> 1.2", ">= 1.2.1"]
33
33
  s.add_runtime_dependency %q<ruby_fs>, ["~> 1.0"]
@@ -38,7 +38,6 @@ Gem::Specification.new do |s|
38
38
  s.add_development_dependency %q<ci_reporter>, ["~> 1.6"]
39
39
  s.add_development_dependency %q<yard>, ["~> 0.6"]
40
40
  s.add_development_dependency %q<rake>, [">= 0"]
41
- s.add_development_dependency %q<mocha>, [">= 0"]
42
41
  s.add_development_dependency %q<i18n>, [">= 0"]
43
42
  s.add_development_dependency %q<countdownlatch>, [">= 0"]
44
43
  s.add_development_dependency %q<guard-rspec>
@@ -12,6 +12,13 @@ module Punchblock
12
12
  subject << component
13
13
  subject.find_by_id(component_id).should be component
14
14
  end
15
+
16
+ it 'should allow deletion of components' do
17
+ subject << component
18
+ subject.find_by_id(component_id).should be component
19
+ subject.delete component
20
+ subject.find_by_id(component_id).should be_nil
21
+ end
15
22
  end
16
23
  end
17
24
  end
@@ -13,27 +13,27 @@ module Punchblock
13
13
  its(:component_registry) { should be_a Client::ComponentRegistry }
14
14
 
15
15
  let(:call_id) { 'abc123' }
16
- let(:mock_event) { stub_everything 'Event' }
16
+ let(:mock_event) { stub('Event').as_null_object }
17
17
  let(:component_id) { 'abc123' }
18
18
  let(:mock_component) { stub 'Component', :component_id => component_id }
19
19
  let(:mock_command) { stub 'Command' }
20
20
 
21
21
  describe '#run' do
22
22
  it 'should start up the connection' do
23
- connection.expects(:run).once
23
+ connection.should_receive(:run).once
24
24
  subject.run
25
25
  end
26
26
  end
27
27
 
28
28
  describe '#stop' do
29
29
  it 'should stop the connection' do
30
- connection.expects(:stop).once
30
+ connection.should_receive(:stop).once
31
31
  subject.stop
32
32
  end
33
33
  end
34
34
 
35
35
  it 'should handle connection events' do
36
- subject.expects(:handle_event).with(mock_event).once
36
+ subject.should_receive(:handle_event).with(mock_event).once
37
37
  connection.event_handler.call mock_event
38
38
  end
39
39
 
@@ -46,8 +46,8 @@ module Punchblock
46
46
 
47
47
  context 'if the event can be associated with a source component' do
48
48
  before do
49
- mock_event.stubs(:source).returns mock_component
50
- mock_component.expects(:add_event).with mock_event
49
+ mock_event.stub :source => mock_component
50
+ mock_component.should_receive(:add_event).with mock_event
51
51
  end
52
52
 
53
53
  it 'should not queue up the event' do
@@ -57,23 +57,25 @@ module Punchblock
57
57
 
58
58
  it 'should not call event handlers' do
59
59
  handler = mock 'handler'
60
- handler.expects(:call).never
60
+ handler.should_receive(:call).never
61
61
  subject.register_event_handler do |event|
62
62
  handler.call event
63
- throw :halt
64
63
  end
65
64
  subject.handle_event mock_event
66
65
  end
67
66
  end
68
67
 
69
68
  context 'if the event cannot be associated with a source component' do
69
+ before do
70
+ mock_event.stub :source => nil
71
+ end
72
+
70
73
  context 'if event handlers have been set' do
71
74
  it 'should call the event handler and not queue up the event' do
72
75
  handler = mock 'handler'
73
- handler.expects(:call).once.with mock_event
76
+ handler.should_receive(:call).once.with mock_event
74
77
  subject.register_event_handler do |event|
75
78
  handler.call event
76
- throw :halt
77
79
  end
78
80
  subject.handle_event mock_event
79
81
  subject.event_queue.should be_empty
@@ -99,7 +101,7 @@ module Punchblock
99
101
  let(:event) { Event::Complete.new }
100
102
 
101
103
  before do
102
- connection.expects(:write).once.with component, :call_id => call_id
104
+ connection.should_receive(:write).once.with component, :call_id => call_id
103
105
  end
104
106
 
105
107
  let :execute_command do
@@ -116,7 +118,7 @@ module Punchblock
116
118
  end
117
119
 
118
120
  it "should handle a component's events" do
119
- subject.expects(:trigger_handler).with(:event, event).once
121
+ subject.should_receive(:trigger_handler).with(:event, event).once
120
122
  execute_command
121
123
  component.request!
122
124
  component.execute!
@@ -70,12 +70,12 @@ module Punchblock
70
70
 
71
71
  describe "#response=" do
72
72
  it "should set the command to executing status" do
73
- subject.expects(:execute!).once
73
+ subject.should_receive(:execute!).once
74
74
  subject.response = :foo
75
75
  end
76
76
 
77
77
  it "should be a no-op if the response has already been set" do
78
- subject.expects(:execute!).once
78
+ subject.should_receive(:execute!).once
79
79
  subject.response = :foo
80
80
  lambda { subject.response = :bar }.should_not raise_error
81
81
  end
@@ -28,7 +28,7 @@ module Punchblock
28
28
  end
29
29
 
30
30
  it "should call #complete!" do
31
- subject.expects(:complete!).once
31
+ subject.should_receive(:complete!).once
32
32
  add_event
33
33
  end
34
34
  end
@@ -55,7 +55,7 @@ module Punchblock
55
55
  let(:handler) { mock 'Response' }
56
56
 
57
57
  before do
58
- handler.expects(:call).once.with(event)
58
+ handler.should_receive(:call).once.with(event)
59
59
  subject.register_event_handler { |event| handler.call event }
60
60
  end
61
61
 
@@ -89,7 +89,9 @@ module Punchblock
89
89
  describe "#complete_event=" do
90
90
  before do
91
91
  subject.request!
92
- subject.execute!
92
+ subject.client = Client.new
93
+ subject.response = Ref.new id: 'abc'
94
+ subject.client.find_component_by_id('abc').should be subject
93
95
  end
94
96
 
95
97
  it "should set the command to executing status" do
@@ -102,6 +104,11 @@ module Punchblock
102
104
  lambda { subject.complete_event = :bar }.should_not raise_error
103
105
  subject.complete_event(0.5).should be == :foo
104
106
  end
107
+
108
+ it "should remove the component from the registry" do
109
+ subject.complete_event = :foo
110
+ subject.client.find_component_by_id('abc').should be_nil
111
+ end
105
112
  end
106
113
  end # ComponentNode
107
114
  end # Component
@@ -163,7 +163,7 @@ module Punchblock
163
163
  end
164
164
 
165
165
  it "should send its command properly" do
166
- mock_client.expects(:execute_command).with(command.stop_action, :target_call_id => '123abc', :component_id => 'abc123')
166
+ mock_client.should_receive(:execute_command).with(command.stop_action, :target_call_id => '123abc', :component_id => 'abc123')
167
167
  command.stop!
168
168
  end
169
169
  end