punchblock 1.6.1 → 1.7.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 (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