punchblock 1.9.4 → 2.0.0.beta1

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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -2
  4. data/CHANGELOG.md +17 -0
  5. data/Gemfile +1 -0
  6. data/Guardfile +4 -0
  7. data/README.markdown +6 -0
  8. data/Rakefile +16 -0
  9. data/benchmarks/ami_event_name_comparison.rb +14 -0
  10. data/benchmarks/channel.rb +27 -0
  11. data/lib/punchblock/client.rb +2 -6
  12. data/lib/punchblock/command/accept.rb +3 -24
  13. data/lib/punchblock/command/answer.rb +3 -24
  14. data/lib/punchblock/command/dial.rb +24 -76
  15. data/lib/punchblock/command/hangup.rb +3 -19
  16. data/lib/punchblock/command/join.rb +21 -70
  17. data/lib/punchblock/command/mute.rb +3 -3
  18. data/lib/punchblock/command/redirect.rb +6 -39
  19. data/lib/punchblock/command/reject.rb +14 -54
  20. data/lib/punchblock/command/unjoin.rb +8 -40
  21. data/lib/punchblock/command/unmute.rb +3 -3
  22. data/lib/punchblock/command_node.rb +0 -17
  23. data/lib/punchblock/component/asterisk/agi/command.rb +20 -127
  24. data/lib/punchblock/component/asterisk/ami/action.rb +30 -117
  25. data/lib/punchblock/component/component_node.rb +1 -1
  26. data/lib/punchblock/component/input.rb +89 -268
  27. data/lib/punchblock/component/output.rb +106 -154
  28. data/lib/punchblock/component/prompt.rb +51 -0
  29. data/lib/punchblock/component/record.rb +41 -130
  30. data/lib/punchblock/component.rb +1 -0
  31. data/lib/punchblock/connection/asterisk.rb +31 -4
  32. data/lib/punchblock/connection/xmpp.rb +6 -14
  33. data/lib/punchblock/core_ext/blather/stanza.rb +1 -1
  34. data/lib/punchblock/event/active_speaker.rb +2 -10
  35. data/lib/punchblock/event/answered.rb +3 -3
  36. data/lib/punchblock/event/asterisk/ami/event.rb +15 -47
  37. data/lib/punchblock/event/complete.rb +26 -48
  38. data/lib/punchblock/event/dtmf.rb +3 -13
  39. data/lib/punchblock/event/end.rb +10 -11
  40. data/lib/punchblock/event/joined.rb +5 -25
  41. data/lib/punchblock/event/offer.rb +4 -25
  42. data/lib/punchblock/event/ringing.rb +3 -3
  43. data/lib/punchblock/event/unjoined.rb +5 -25
  44. data/lib/punchblock/event.rb +0 -10
  45. data/lib/punchblock/has_headers.rb +20 -26
  46. data/lib/punchblock/rayo_node.rb +46 -23
  47. data/lib/punchblock/ref.rb +39 -18
  48. data/lib/punchblock/translator/asterisk/agi_app.rb +15 -0
  49. data/lib/punchblock/translator/asterisk/agi_command.rb +3 -1
  50. data/lib/punchblock/translator/asterisk/ami_error_converter.rb +20 -0
  51. data/lib/punchblock/translator/asterisk/call.rb +60 -39
  52. data/lib/punchblock/translator/asterisk/channel.rb +41 -0
  53. data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +4 -1
  54. data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +4 -4
  55. data/lib/punchblock/translator/asterisk/component/composed_prompt.rb +62 -0
  56. data/lib/punchblock/translator/asterisk/component/input.rb +1 -0
  57. data/lib/punchblock/translator/asterisk/component/mrcp_native_prompt.rb +56 -0
  58. data/lib/punchblock/translator/asterisk/component/mrcp_prompt.rb +53 -0
  59. data/lib/punchblock/translator/asterisk/component/mrcp_recog_prompt.rb +99 -0
  60. data/lib/punchblock/translator/asterisk/component/output.rb +30 -22
  61. data/lib/punchblock/translator/asterisk/component/record.rb +8 -6
  62. data/lib/punchblock/translator/asterisk/component.rb +6 -5
  63. data/lib/punchblock/translator/asterisk/unimrcp_app.rb +26 -0
  64. data/lib/punchblock/translator/asterisk.rb +24 -28
  65. data/lib/punchblock/translator/dtmf_recognizer.rb +39 -20
  66. data/lib/punchblock/translator/freeswitch/call.rb +15 -14
  67. data/lib/punchblock/translator/freeswitch/component/abstract_output.rb +5 -4
  68. data/lib/punchblock/translator/freeswitch/component/flite_output.rb +1 -1
  69. data/lib/punchblock/translator/freeswitch/component/input.rb +5 -0
  70. data/lib/punchblock/translator/freeswitch/component/output.rb +2 -2
  71. data/lib/punchblock/translator/freeswitch/component/record.rb +19 -13
  72. data/lib/punchblock/translator/freeswitch/component/tts_output.rb +2 -2
  73. data/lib/punchblock/translator/freeswitch/component.rb +2 -5
  74. data/lib/punchblock/translator/freeswitch.rb +2 -2
  75. data/lib/punchblock/translator/input_component.rb +33 -13
  76. data/lib/punchblock/uri_list.rb +21 -0
  77. data/lib/punchblock/version.rb +1 -1
  78. data/lib/punchblock.rb +4 -3
  79. data/punchblock.gemspec +7 -3
  80. data/spec/punchblock/client/component_registry_spec.rb +1 -1
  81. data/spec/punchblock/client_spec.rb +10 -26
  82. data/spec/punchblock/command/accept_spec.rb +41 -7
  83. data/spec/punchblock/command/answer_spec.rb +51 -7
  84. data/spec/punchblock/command/dial_spec.rb +56 -14
  85. data/spec/punchblock/command/hangup_spec.rb +41 -7
  86. data/spec/punchblock/command/join_spec.rb +53 -11
  87. data/spec/punchblock/command/mute_spec.rb +19 -4
  88. data/spec/punchblock/command/redirect_spec.rb +40 -10
  89. data/spec/punchblock/command/reject_spec.rb +43 -11
  90. data/spec/punchblock/command/unjoin_spec.rb +40 -9
  91. data/spec/punchblock/command/unmute_spec.rb +19 -4
  92. data/spec/punchblock/command_node_spec.rb +0 -4
  93. data/spec/punchblock/component/asterisk/agi/command_spec.rb +16 -39
  94. data/spec/punchblock/component/asterisk/ami/action_spec.rb +50 -53
  95. data/spec/punchblock/component/component_node_spec.rb +3 -5
  96. data/spec/punchblock/component/input_spec.rb +194 -61
  97. data/spec/punchblock/component/output_spec.rb +194 -62
  98. data/spec/punchblock/component/prompt_spec.rb +132 -0
  99. data/spec/punchblock/component/record_spec.rb +70 -32
  100. data/spec/punchblock/connection/asterisk_spec.rb +17 -3
  101. data/spec/punchblock/connection/freeswitch_spec.rb +4 -4
  102. data/spec/punchblock/connection/xmpp_spec.rb +20 -38
  103. data/spec/punchblock/event/answered_spec.rb +12 -10
  104. data/spec/punchblock/event/asterisk/ami/event_spec.rb +27 -22
  105. data/spec/punchblock/event/complete_spec.rb +15 -19
  106. data/spec/punchblock/event/dtmf_spec.rb +5 -6
  107. data/spec/punchblock/event/end_spec.rb +20 -10
  108. data/spec/punchblock/event/joined_spec.rb +8 -7
  109. data/spec/punchblock/event/offer_spec.rb +41 -12
  110. data/spec/punchblock/event/ringing_spec.rb +12 -10
  111. data/spec/punchblock/event/started_speaking_spec.rb +5 -6
  112. data/spec/punchblock/event/stopped_speaking_spec.rb +5 -6
  113. data/spec/punchblock/event/unjoined_spec.rb +7 -7
  114. data/spec/punchblock/ref_spec.rb +86 -9
  115. data/spec/punchblock/translator/asterisk/call_spec.rb +317 -154
  116. data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +28 -5
  117. data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +15 -13
  118. data/spec/punchblock/translator/asterisk/component/composed_prompt_spec.rb +237 -0
  119. data/spec/punchblock/translator/asterisk/component/input_spec.rb +171 -14
  120. data/spec/punchblock/translator/asterisk/component/mrcp_native_prompt_spec.rb +652 -0
  121. data/spec/punchblock/translator/asterisk/component/mrcp_prompt_spec.rb +646 -0
  122. data/spec/punchblock/translator/asterisk/component/output_spec.rb +127 -77
  123. data/spec/punchblock/translator/asterisk/component/record_spec.rb +17 -8
  124. data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +2 -2
  125. data/spec/punchblock/translator/asterisk/component_spec.rb +3 -7
  126. data/spec/punchblock/translator/asterisk_spec.rb +20 -24
  127. data/spec/punchblock/translator/freeswitch/call_spec.rb +103 -99
  128. data/spec/punchblock/translator/freeswitch/component/flite_output_spec.rb +17 -8
  129. data/spec/punchblock/translator/freeswitch/component/input_spec.rb +26 -14
  130. data/spec/punchblock/translator/freeswitch/component/output_spec.rb +30 -52
  131. data/spec/punchblock/translator/freeswitch/component/record_spec.rb +23 -19
  132. data/spec/punchblock/translator/freeswitch/component/tts_output_spec.rb +18 -8
  133. data/spec/punchblock/translator/freeswitch/component_spec.rb +4 -8
  134. data/spec/punchblock/translator/freeswitch_spec.rb +11 -14
  135. data/spec/punchblock/uri_list_spec.rb +49 -0
  136. data/spec/punchblock_spec.rb +11 -1
  137. data/spec/spec_helper.rb +7 -11
  138. data/spec/support/mock_connection_with_event_handler.rb +1 -1
  139. metadata +104 -24
  140. data/lib/punchblock/header.rb +0 -9
  141. data/lib/punchblock/key_value_pair_node.rb +0 -51
  142. data/spec/punchblock/header_spec.rb +0 -11
@@ -6,27 +6,55 @@ module Punchblock
6
6
  module Component
7
7
  describe Record do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:record, 'urn:xmpp:rayo:record:1').should be == Record
9
+ RayoNode.class_from_registration(:record, 'urn:xmpp:rayo:record:1').should be == described_class
10
10
  end
11
11
 
12
12
  describe "when setting options in initializer" do
13
13
  subject do
14
14
  Record.new :format => 'WAV',
15
15
  :start_beep => true,
16
+ :stop_beep => true,
16
17
  :start_paused => false,
17
18
  :max_duration => 500000,
18
19
  :initial_timeout => 10000,
19
20
  :final_timeout => 30000,
20
- :direction => :duplex
21
+ :direction => :duplex,
22
+ :mix => true
21
23
  end
22
24
 
23
25
  its(:format) { should be == 'WAV' }
24
26
  its(:start_beep) { should be == true }
27
+ its(:stop_beep) { should be == true }
25
28
  its(:start_paused) { should be == false }
26
29
  its(:max_duration) { should be == 500000 }
27
30
  its(:initial_timeout) { should be == 10000 }
28
31
  its(:final_timeout) { should be == 30000 }
29
32
  its(:direction) { should be == :duplex }
33
+ its(:mix) { should be == true }
34
+
35
+ describe "exporting to Rayo" do
36
+ it "should export to XML that can be understood by its parser" do
37
+ new_instance = RayoNode.from_xml subject.to_rayo
38
+ new_instance.should be_instance_of described_class
39
+ new_instance.format.should be == 'WAV'
40
+ new_instance.start_beep.should be == true
41
+ new_instance.stop_beep.should be == true
42
+ new_instance.start_paused.should be == false
43
+ new_instance.max_duration.should be == 500000
44
+ new_instance.initial_timeout.should be == 10000
45
+ new_instance.final_timeout.should be == 30000
46
+ new_instance.direction.should be == :duplex
47
+ new_instance.mix.should be == true
48
+ end
49
+
50
+ it "should render to a parent node if supplied" do
51
+ doc = Nokogiri::XML::Document.new
52
+ parent = Nokogiri::XML::Node.new 'foo', doc
53
+ doc.root = parent
54
+ rayo_doc = subject.to_rayo(parent)
55
+ rayo_doc.should == parent
56
+ end
57
+ end
30
58
  end
31
59
 
32
60
  describe "from a stanza" do
@@ -35,31 +63,35 @@ module Punchblock
35
63
  <record xmlns="urn:xmpp:rayo:record:1"
36
64
  format="WAV"
37
65
  start-beep="true"
66
+ stop-beep="true"
38
67
  start-paused="false"
39
68
  max-duration="500000"
40
69
  initial-timeout="10000"
41
70
  direction="duplex"
71
+ mix="true"
42
72
  final-timeout="30000"/>
43
73
  MESSAGE
44
74
  end
45
75
 
46
- subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
76
+ subject { RayoNode.from_xml parse_stanza(stanza).root, '9f00061', '1' }
47
77
 
48
78
  it { should be_instance_of Record }
49
79
 
50
80
  its(:format) { should be == 'WAV' }
51
81
  its(:start_beep) { should be == true }
82
+ its(:stop_beep) { should be == true }
52
83
  its(:start_paused) { should be == false }
53
84
  its(:max_duration) { should be == 500000 }
54
85
  its(:initial_timeout) { should be == 10000 }
55
86
  its(:final_timeout) { should be == 30000 }
56
87
  its(:direction) { should be == :duplex }
88
+ its(:mix) { should be == true }
57
89
  end
58
90
 
59
91
  describe "with a direction" do
60
92
  [nil, :duplex, :send, :recv].each do |direction|
61
93
  describe direction do
62
- subject { Record.new :direction => direction }
94
+ subject { described_class.new :direction => direction }
63
95
 
64
96
  its(:direction) { should be == direction }
65
97
  end
@@ -73,14 +105,14 @@ module Punchblock
73
105
 
74
106
  describe "blahblahblah" do
75
107
  it "should raise an error" do
76
- expect { Record.new(:direction => :blahblahblah) }.to raise_error ArgumentError
108
+ expect { described_class.new(:direction => :blahblahblah) }.to raise_error ArgumentError
77
109
  end
78
110
  end
79
111
  end
80
112
 
81
113
  describe "actions" do
82
- let(:mock_client) { mock 'Client' }
83
- let(:command) { Record.new }
114
+ let(:mock_client) { double 'Client' }
115
+ let(:command) { described_class.new }
84
116
 
85
117
  before do
86
118
  command.component_id = 'abc123'
@@ -185,7 +217,7 @@ module Punchblock
185
217
  </complete>
186
218
  MESSAGE
187
219
  end
188
- let(:event) { RayoNode.import(parse_stanza(stanza).root) }
220
+ let(:event) { RayoNode.from_xml(parse_stanza(stanza).root) }
189
221
 
190
222
  before do
191
223
  subject.request!
@@ -235,31 +267,37 @@ module Punchblock
235
267
  end
236
268
  end
237
269
 
238
- describe Record::Complete::Success do
239
- let :stanza do
240
- <<-MESSAGE
241
- <complete xmlns='urn:xmpp:rayo:ext:1'>
242
- <success xmlns='urn:xmpp:rayo:record:complete:1'/>
243
- <recording xmlns='urn:xmpp:rayo:record:complete:1' uri="file:/tmp/rayo7451601434771683422.mp3" duration="34000" size="23450"/>
244
- </complete>
245
- MESSAGE
246
- end
270
+ {
271
+ Record::Complete::MaxDuration => :'max-duration',
272
+ Record::Complete::InitialTimeout => :'initial-timeout',
273
+ Record::Complete::FinalTimeout => :'final-timeout',
274
+ }.each do |klass, element_name|
275
+ describe klass do
276
+ let :stanza do
277
+ <<-MESSAGE
278
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
279
+ <#{element_name} xmlns='urn:xmpp:rayo:record:complete:1'/>
280
+ <recording xmlns='urn:xmpp:rayo:record:complete:1' uri="file:/tmp/rayo7451601434771683422.mp3" duration="34000" size="23450"/>
281
+ </complete>
282
+ MESSAGE
283
+ end
247
284
 
248
- describe "#reason" do
249
- subject { RayoNode.import(parse_stanza(stanza).root).reason }
285
+ describe "#reason" do
286
+ subject { RayoNode.from_xml(parse_stanza(stanza).root).reason }
250
287
 
251
- it { should be_instance_of Record::Complete::Success }
288
+ it { should be_instance_of klass }
252
289
 
253
- its(:name) { should be == :success }
254
- end
290
+ its(:name) { should be == element_name }
291
+ end
255
292
 
256
- describe "#recording" do
257
- subject { RayoNode.import(parse_stanza(stanza).root).recording }
293
+ describe "#recording" do
294
+ subject { RayoNode.from_xml(parse_stanza(stanza).root).recording }
258
295
 
259
- it { should be_instance_of Record::Recording }
260
- its(:uri) { should be == "file:/tmp/rayo7451601434771683422.mp3" }
261
- its(:duration) { should be == 34000 }
262
- its(:size) { should be == 23450 }
296
+ it { should be_instance_of Record::Recording }
297
+ its(:uri) { should be == "file:/tmp/rayo7451601434771683422.mp3" }
298
+ its(:duration) { should be == 34000 }
299
+ its(:size) { should be == 23450 }
300
+ end
263
301
  end
264
302
  end
265
303
 
@@ -274,7 +312,7 @@ module Punchblock
274
312
  end
275
313
 
276
314
  describe "#reason" do
277
- subject { RayoNode.import(parse_stanza(stanza).root).reason }
315
+ subject { RayoNode.from_xml(parse_stanza(stanza).root).reason }
278
316
 
279
317
  it { should be_instance_of Event::Complete::Stop }
280
318
 
@@ -282,7 +320,7 @@ module Punchblock
282
320
  end
283
321
 
284
322
  describe "#recording" do
285
- subject { RayoNode.import(parse_stanza(stanza).root).recording }
323
+ subject { RayoNode.from_xml(parse_stanza(stanza).root).recording }
286
324
 
287
325
  it { should be_instance_of Record::Recording }
288
326
  its(:uri) { should be == "file:/tmp/rayo7451601434771683422.mp3" }
@@ -300,7 +338,7 @@ module Punchblock
300
338
  end
301
339
 
302
340
  describe "#reason" do
303
- subject { RayoNode.import(parse_stanza(stanza).root).reason }
341
+ subject { RayoNode.from_xml(parse_stanza(stanza).root).reason }
304
342
 
305
343
  it { should be_instance_of Event::Complete::Hangup }
306
344
 
@@ -308,7 +346,7 @@ module Punchblock
308
346
  end
309
347
 
310
348
  describe "#recording" do
311
- subject { RayoNode.import(parse_stanza(stanza).root).recording }
349
+ subject { RayoNode.from_xml(parse_stanza(stanza).root).recording }
312
350
 
313
351
  it { should be_instance_of Record::Recording }
314
352
  its(:uri) { should be == "file:/tmp/rayo7451601434771683422.mp3" }
@@ -15,7 +15,7 @@ module Punchblock
15
15
  }
16
16
  end
17
17
 
18
- let(:mock_event_handler) { stub('Event Handler').as_null_object }
18
+ let(:mock_event_handler) { double('Event Handler').as_null_object }
19
19
 
20
20
  let(:connection) { Asterisk.new options }
21
21
 
@@ -25,7 +25,8 @@ module Punchblock
25
25
  subject.event_handler = mock_event_handler
26
26
  end
27
27
 
28
- its(:ami_client) { should be_a RubyAMI::Stream }
28
+ its(:ami_client) { should be_a RubyAMIStreamProxy }
29
+ its('ami_client.stream') { should be_a RubyAMI::Stream }
29
30
 
30
31
  it 'should set the connection on the translator' do
31
32
  subject.translator.connection.should be subject
@@ -42,6 +43,19 @@ module Punchblock
42
43
  end
43
44
  lambda { subject.run }.should raise_error DisconnectedError
44
45
  end
46
+
47
+ it 'rebuilds the RubyAMI::Stream if dead' do
48
+ subject.ami_client.async.should_receive(:run).once do
49
+ subject.ami_client.terminate
50
+ end
51
+ lambda { subject.run }.should raise_error DisconnectedError
52
+ subject.ami_client.alive?.should be_false
53
+ subject.should_receive(:new_ami_stream).once do
54
+ subject.ami_client.alive?.should be_true
55
+ subject.ami_client.async.should_receive(:run).once
56
+ end
57
+ lambda { subject.run }.should_not raise_error DisconnectedError
58
+ end
45
59
  end
46
60
 
47
61
  describe '#stop' do
@@ -65,7 +79,7 @@ module Punchblock
65
79
 
66
80
  describe '#write' do
67
81
  it 'sends a command to the translator' do
68
- command = mock 'Command'
82
+ command = double 'Command'
69
83
  options = {:foo => :bar}
70
84
  subject.translator.async.should_receive(:execute_command).once.with command, options
71
85
  subject.write command, options
@@ -17,11 +17,11 @@ module Punchblock
17
17
  }
18
18
  end
19
19
 
20
- let(:mock_event_handler) { stub('Event Handler').as_null_object }
20
+ let(:mock_event_handler) { double('Event Handler').as_null_object }
21
21
 
22
22
  let(:connection) { described_class.new options }
23
23
 
24
- let(:mock_stream) { mock 'RubyFS::Stream' }
24
+ let(:mock_stream) { double 'RubyFS::Stream' }
25
25
 
26
26
  subject { connection }
27
27
 
@@ -62,7 +62,7 @@ module Punchblock
62
62
  end
63
63
 
64
64
  it 'sends events from RubyFS to the translator' do
65
- event = mock 'RubyFS::Event'
65
+ event = double 'RubyFS::Event'
66
66
  subject.translator.async.should_receive(:handle_es_event).once.with event
67
67
  subject.translator.async.should_receive(:handle_es_event).once.with RubyFS::Stream::Disconnected.new
68
68
  subject.stream.fire_event event
@@ -70,7 +70,7 @@ module Punchblock
70
70
 
71
71
  describe '#write' do
72
72
  it 'sends a command to the translator' do
73
- command = mock 'Command'
73
+ command = double 'Command'
74
74
  options = {:foo => :bar}
75
75
  subject.translator.async.should_receive(:execute_command).once.with command, options
76
76
  subject.write command, options
@@ -8,7 +8,7 @@ module Punchblock
8
8
  let(:options) { { :root_domain => 'rayo.net' } }
9
9
  let(:connection) { XMPP.new({:username => '1@app.rayo.net', :password => 1}.merge(options)) }
10
10
 
11
- let(:mock_event_handler) { stub('Event Handler').as_null_object }
11
+ let(:mock_event_handler) { double('Event Handler').as_null_object }
12
12
 
13
13
  before do
14
14
  connection.event_handler = mock_event_handler
@@ -21,40 +21,18 @@ module Punchblock
21
21
  let(:options) { { :username => '1@app.rayo.net' } }
22
22
 
23
23
  its(:root_domain) { should be == 'app.rayo.net' }
24
- its(:calls_domain) { should be == 'calls.app.rayo.net' }
25
- its(:mixers_domain) { should be == 'mixers.app.rayo.net' }
26
24
  end
27
25
 
28
26
  context "with only a rayo domain set" do
29
27
  let(:options) { { :rayo_domain => 'rayo.org' } }
30
28
 
31
29
  its(:root_domain) { should be == 'rayo.org' }
32
- its(:calls_domain) { should be == 'calls.rayo.org' }
33
- its(:mixers_domain) { should be == 'mixers.rayo.org' }
34
30
  end
35
31
 
36
32
  context "with only a root domain set" do
37
33
  let(:options) { { :root_domain => 'rayo.org' } }
38
34
 
39
35
  its(:root_domain) { should be == 'rayo.org' }
40
- its(:calls_domain) { should be == 'calls.rayo.org' }
41
- its(:mixers_domain) { should be == 'mixers.rayo.org' }
42
- end
43
-
44
- context "with a root domain and calls domain set" do
45
- let(:options) { { :root_domain => 'rayo.org', :calls_domain => 'phone_calls.rayo.org' } }
46
-
47
- its(:root_domain) { should be == 'rayo.org' }
48
- its(:calls_domain) { should be == 'phone_calls.rayo.org' }
49
- its(:mixers_domain) { should be == 'mixers.rayo.org' }
50
- end
51
-
52
- context "with a root domain and mixers domain set" do
53
- let(:options) { { :root_domain => 'rayo.org', :mixers_domain => 'conferences.rayo.org' } }
54
-
55
- its(:root_domain) { should be == 'rayo.org' }
56
- its(:calls_domain) { should be == 'calls.rayo.org' }
57
- its(:mixers_domain) { should be == 'conferences.rayo.org' }
58
36
  end
59
37
  end
60
38
 
@@ -185,15 +163,10 @@ module Punchblock
185
163
  event.should be_instance_of Event::Offer
186
164
  event.target_call_id.should be == '9f00061'
187
165
  event.domain.should be == 'call.rayo.net'
166
+ event.transport.should be == 'xmpp'
188
167
  end
189
168
  handle_presence
190
169
  end
191
-
192
- it "should populate the call map with the domain for the call ID" do
193
- handle_presence
194
- callmap = connection.instance_variable_get(:'@callmap')
195
- callmap['9f00061'].should be == 'call.rayo.net'
196
- end
197
170
  end
198
171
 
199
172
  describe "from something that's not a real event" do
@@ -220,7 +193,7 @@ module Punchblock
220
193
  let(:component_id) { 'abc123' }
221
194
  let :error_xml do
222
195
  <<-MSG
223
- <iq type="error" id="blather000e" from="f6d437f4-1e18-457b-99f8-b5d853f50347@10.0.1.11/abc123" to="usera@10.0.1.11/voxeo">
196
+ <iq type="error" id="blather000e" from="f6d437f4-1e18-457b-99f8-b5d853f50347@call.rayo.net/abc123" to="usera@rayo.net">
224
197
  <output xmlns="urn:xmpp:rayo:output:1"/>
225
198
  <error type="cancel">
226
199
  <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
@@ -271,17 +244,26 @@ module Punchblock
271
244
  end
272
245
 
273
246
  context "with a call command" do
274
- let(:command) { Command::Answer.new.tap { |a| a.target_call_id = 'abc123' } }
275
- let(:expected_jid) { 'abc123@calls.rayo.net' }
247
+ let(:command) { Command::Answer.new target_call_id: 'abc123' }
248
+ let(:expected_jid) { 'abc123@rayo.net' }
276
249
 
277
250
  it "should use the correct JID" do
278
251
  stanza.to.should be == expected_jid
279
252
  end
253
+
254
+ context "with a domain specified" do
255
+ let(:expected_jid) { 'abc123@calls.rayo.net' }
256
+
257
+ it "should use the specified domain in the JID" do
258
+ stanza = subject.prep_command_for_execution command, domain: 'calls.rayo.net'
259
+ stanza.to.should be == expected_jid
260
+ end
261
+ end
280
262
  end
281
263
 
282
264
  context "with a call component" do
283
265
  let(:command) { Component::Output.new :target_call_id => 'abc123' }
284
- let(:expected_jid) { 'abc123@calls.rayo.net' }
266
+ let(:expected_jid) { 'abc123@rayo.net' }
285
267
 
286
268
  it "should use the correct JID" do
287
269
  stanza.to.should be == expected_jid
@@ -290,7 +272,7 @@ module Punchblock
290
272
 
291
273
  context "with a call component command" do
292
274
  let(:command) { Component::Stop.new :target_call_id => 'abc123', :component_id => 'foobar' }
293
- let(:expected_jid) { 'abc123@calls.rayo.net/foobar' }
275
+ let(:expected_jid) { 'abc123@rayo.net/foobar' }
294
276
 
295
277
  it "should use the correct JID" do
296
278
  stanza.to.should be == expected_jid
@@ -299,7 +281,7 @@ module Punchblock
299
281
 
300
282
  context "with a mixer component" do
301
283
  let(:command) { Component::Output.new :target_mixer_name => 'abc123' }
302
- let(:expected_jid) { 'abc123@mixers.rayo.net' }
284
+ let(:expected_jid) { 'abc123@rayo.net' }
303
285
 
304
286
  it "should use the correct JID" do
305
287
  stanza.to.should be == expected_jid
@@ -308,7 +290,7 @@ module Punchblock
308
290
 
309
291
  context "with a mixer component command" do
310
292
  let(:command) { Component::Stop.new :target_mixer_name => 'abc123', :component_id => 'foobar' }
311
- let(:expected_jid) { 'abc123@mixers.rayo.net/foobar' }
293
+ let(:expected_jid) { 'abc123@rayo.net/foobar' }
312
294
 
313
295
  it "should use the correct JID" do
314
296
  stanza.to.should be == expected_jid
@@ -325,7 +307,7 @@ module Punchblock
325
307
 
326
308
  let :active_speaker_xml do
327
309
  <<-MSG
328
- <presence to='16577@app.rayo.net/1' from='foomixer@mixers.rayo.net'>
310
+ <presence to='16577@app.rayo.net/1' from='foomixer@rayo.net'>
329
311
  <started-speaking xmlns="urn:xmpp:rayo:1" call-id="foocall"/>
330
312
  </presence>
331
313
  MSG
@@ -338,7 +320,7 @@ module Punchblock
338
320
  event.should be_instance_of Event::StartedSpeaking
339
321
  event.target_mixer_name.should be == 'foomixer'
340
322
  event.target_call_id.should be nil
341
- event.domain.should be == 'mixers.rayo.net'
323
+ event.domain.should be == 'rayo.net'
342
324
  end
343
325
  connection.__send__ :handle_presence, active_speaker_event
344
326
  end
@@ -6,7 +6,7 @@ module Punchblock
6
6
  class Event
7
7
  describe Answered do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:answered, 'urn:xmpp:rayo:1').should be == Answered
9
+ RayoNode.class_from_registration(:answered, 'urn:xmpp:rayo:1').should be == described_class
10
10
  end
11
11
 
12
12
  describe "from a stanza" do
@@ -20,23 +20,25 @@ module Punchblock
20
20
  MESSAGE
21
21
  end
22
22
 
23
- subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
23
+ subject { RayoNode.from_xml parse_stanza(stanza).root, '9f00061', '1' }
24
24
 
25
- it { should be_instance_of Answered }
25
+ it { should be_instance_of described_class }
26
26
 
27
27
  it_should_behave_like 'event'
28
- it_should_behave_like 'event_headers'
28
+ its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
29
29
 
30
- its(:xmlns) { should be == 'urn:xmpp:rayo:1' }
30
+ context "with no headers provided" do
31
+ let(:stanza) { '<answered xmlns="urn:xmpp:rayo:1"/>' }
32
+
33
+ its(:headers) { should == {} }
34
+ end
31
35
  end
32
36
 
33
37
  describe "when setting options in initializer" do
34
- subject do
35
- Answered.new :headers => { :x_skill => "agent", :x_customer_id => "8877" }
36
- end
38
+ subject { described_class.new headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
37
39
 
38
- it_should_behave_like 'command_headers'
40
+ its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
39
41
  end
40
42
  end
41
43
  end
42
- end # Punchblock
44
+ end
@@ -8,7 +8,7 @@ module Punchblock
8
8
  module AMI
9
9
  describe Event do
10
10
  it 'registers itself' do
11
- RayoNode.class_from_registration(:event, 'urn:xmpp:rayo:asterisk:ami:1').should be == Event
11
+ RayoNode.class_from_registration(:event, 'urn:xmpp:rayo:asterisk:ami:1').should be == described_class
12
12
  end
13
13
 
14
14
  describe "from a stanza" do
@@ -17,46 +17,51 @@ module Punchblock
17
17
  <event xmlns="urn:xmpp:rayo:asterisk:ami:1" name="Newchannel">
18
18
  <attribute name="Channel" value="SIP/101-3f3f"/>
19
19
  <attribute name="State" value="Ring"/>
20
- <attribute name="Callerid" value="101"/>
21
- <attribute name="Uniqueid" value="1094154427.10"/>
22
20
  </event>
23
21
  MESSAGE
24
22
  end
25
23
 
26
- subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
24
+ subject { RayoNode.from_xml parse_stanza(stanza).root, '9f00061', '1' }
27
25
 
28
26
  it { should be_instance_of Event }
29
27
 
30
28
  it_should_behave_like 'event'
31
29
 
32
- its(:name) { should be == 'Newchannel' }
33
- its(:attributes) { should be == [Event::Attribute.new('Channel', 'SIP/101-3f3f'), Event::Attribute.new('State', 'Ring'), Event::Attribute.new('Callerid', '101'), Event::Attribute.new('Uniqueid', '1094154427.10')]}
34
- its(:attributes_hash) { should be == {:channel => 'SIP/101-3f3f', :state => 'Ring', :callerid => '101', :uniqueid => '1094154427.10'} }
30
+ its(:name) { should be == 'Newchannel' }
31
+ its(:headers) { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} }
32
+ its(:attributes) { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} } # For BC
35
33
  end
36
34
 
37
35
  describe "when setting options in initializer" do
38
36
  subject do
39
- Event.new :name => 'Newchannel',
40
- :attributes => {:channel => 'SIP/101-3f3f',
41
- :state => 'Ring',
42
- :callerid => '101',
43
- :uniqueid => '1094154427.10'}
37
+ described_class.new name: 'Newchannel',
38
+ headers: {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'}
44
39
  end
45
40
 
46
- its(:name) { should be == 'Newchannel' }
47
- its(:attributes) { should be == [Event::Attribute.new(:channel, 'SIP/101-3f3f'), Event::Attribute.new(:state, 'Ring'), Event::Attribute.new(:callerid, '101'), Event::Attribute.new(:uniqueid, '1094154427.10')]}
48
- its(:attributes_hash) { should be == {:channel => 'SIP/101-3f3f', :state => 'Ring', :callerid => '101', :uniqueid => '1094154427.10'} }
49
- end
41
+ its(:name) { should be == 'Newchannel' }
42
+ its(:headers) { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} }
43
+ its(:attributes) { should be == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} } # For BC
44
+
45
+ describe "exporting to Rayo" do
46
+ it "should export to XML that can be understood by its parser" do
47
+ new_instance = RayoNode.from_xml subject.to_rayo
48
+ new_instance.should be_instance_of described_class
49
+ new_instance.name.should == 'Newchannel'
50
+ new_instance.headers.should == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'}
51
+ new_instance.attributes.should == {'Channel' => 'SIP/101-3f3f', 'State' => 'Ring'} # For BC
52
+ end
50
53
 
51
- class Event
52
- describe Attribute do
53
- let(:class_name) { Attribute }
54
- let(:element_name) { 'attribute' }
55
- it_should_behave_like 'key_value_pairs'
54
+ it "should render to a parent node if supplied" do
55
+ doc = Nokogiri::XML::Document.new
56
+ parent = Nokogiri::XML::Node.new 'foo', doc
57
+ doc.root = parent
58
+ rayo_doc = subject.to_rayo(parent)
59
+ rayo_doc.should == parent
60
+ end
56
61
  end
57
62
  end
58
63
  end
59
64
  end
60
65
  end
61
66
  end
62
- end # Punchblock
67
+ end
@@ -6,11 +6,11 @@ module Punchblock
6
6
  class Event
7
7
  describe Complete do
8
8
  it 'registers itself' do
9
- RayoNode.class_from_registration(:complete, 'urn:xmpp:rayo:ext:1').should be == Complete
9
+ RayoNode.class_from_registration(:complete, 'urn:xmpp:rayo:ext:1').should be == described_class
10
10
  end
11
11
 
12
12
  describe "setting a reason" do
13
- let(:reason) { Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new }
13
+ let(:reason) { Complete::Stop.new }
14
14
 
15
15
  subject { described_class.new }
16
16
 
@@ -21,19 +21,15 @@ module Punchblock
21
21
 
22
22
  describe "comparing for equality" do
23
23
  subject do
24
- Complete.new.tap do |c|
25
- c.reason = Complete::Stop.new
26
- c.target_call_id = '1234'
27
- c.component_id = 'abcd'
28
- end
24
+ described_class.new reason: Complete::Stop.new,
25
+ target_call_id: '1234',
26
+ component_id: 'abcd'
29
27
  end
30
28
 
31
29
  let :other_complete do
32
- Complete.new.tap do |c|
33
- c.reason = reason
34
- c.target_call_id = call_id
35
- c.component_id = component_id
36
- end
30
+ described_class.new reason: reason,
31
+ target_call_id: call_id,
32
+ component_id: component_id
37
33
  end
38
34
 
39
35
  context 'with reason, call id and component id the same' do
@@ -81,18 +77,18 @@ module Punchblock
81
77
  let :stanza do
82
78
  <<-MESSAGE
83
79
  <complete xmlns='urn:xmpp:rayo:ext:1'>
84
- <success xmlns='urn:xmpp:rayo:output:complete:1' />
80
+ <stop xmlns='urn:xmpp:rayo:ext:complete:1' />
85
81
  </complete>
86
82
  MESSAGE
87
83
  end
88
84
 
89
- subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
85
+ subject { RayoNode.from_xml parse_stanza(stanza).root, '9f00061', '1' }
90
86
 
91
- it { should be_instance_of Complete }
87
+ it { should be_instance_of described_class }
92
88
 
93
89
  it_should_behave_like 'event'
94
90
 
95
- its(:reason) { should be_instance_of Component::Output::Complete::Success }
91
+ its(:reason) { should be_instance_of Complete::Stop }
96
92
  end
97
93
  end
98
94
 
@@ -105,7 +101,7 @@ module Punchblock
105
101
  MESSAGE
106
102
  end
107
103
 
108
- subject { RayoNode.import(parse_stanza(stanza).root).reason }
104
+ subject { RayoNode.from_xml(parse_stanza(stanza).root).reason }
109
105
 
110
106
  it { should be_instance_of Complete::Stop }
111
107
 
@@ -121,7 +117,7 @@ module Punchblock
121
117
  MESSAGE
122
118
  end
123
119
 
124
- subject { RayoNode.import(parse_stanza(stanza).root).reason }
120
+ subject { RayoNode.from_xml(parse_stanza(stanza).root).reason }
125
121
 
126
122
  it { should be_instance_of Complete::Hangup }
127
123
 
@@ -139,7 +135,7 @@ module Punchblock
139
135
  MESSAGE
140
136
  end
141
137
 
142
- subject { RayoNode.import(parse_stanza(stanza).root).reason }
138
+ subject { RayoNode.from_xml(parse_stanza(stanza).root).reason }
143
139
 
144
140
  it { should be_instance_of Complete::Error }
145
141