punchblock 1.9.4 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -2
- data/CHANGELOG.md +17 -0
- data/Gemfile +1 -0
- data/Guardfile +4 -0
- data/README.markdown +6 -0
- data/Rakefile +16 -0
- data/benchmarks/ami_event_name_comparison.rb +14 -0
- data/benchmarks/channel.rb +27 -0
- data/lib/punchblock/client.rb +2 -6
- data/lib/punchblock/command/accept.rb +3 -24
- data/lib/punchblock/command/answer.rb +3 -24
- data/lib/punchblock/command/dial.rb +24 -76
- data/lib/punchblock/command/hangup.rb +3 -19
- data/lib/punchblock/command/join.rb +21 -70
- data/lib/punchblock/command/mute.rb +3 -3
- data/lib/punchblock/command/redirect.rb +6 -39
- data/lib/punchblock/command/reject.rb +14 -54
- data/lib/punchblock/command/unjoin.rb +8 -40
- data/lib/punchblock/command/unmute.rb +3 -3
- data/lib/punchblock/command_node.rb +0 -17
- data/lib/punchblock/component/asterisk/agi/command.rb +20 -127
- data/lib/punchblock/component/asterisk/ami/action.rb +30 -117
- data/lib/punchblock/component/component_node.rb +1 -1
- data/lib/punchblock/component/input.rb +89 -268
- data/lib/punchblock/component/output.rb +106 -154
- data/lib/punchblock/component/prompt.rb +51 -0
- data/lib/punchblock/component/record.rb +41 -130
- data/lib/punchblock/component.rb +1 -0
- data/lib/punchblock/connection/asterisk.rb +31 -4
- data/lib/punchblock/connection/xmpp.rb +6 -14
- data/lib/punchblock/core_ext/blather/stanza.rb +1 -1
- data/lib/punchblock/event/active_speaker.rb +2 -10
- data/lib/punchblock/event/answered.rb +3 -3
- data/lib/punchblock/event/asterisk/ami/event.rb +15 -47
- data/lib/punchblock/event/complete.rb +26 -48
- data/lib/punchblock/event/dtmf.rb +3 -13
- data/lib/punchblock/event/end.rb +10 -11
- data/lib/punchblock/event/joined.rb +5 -25
- data/lib/punchblock/event/offer.rb +4 -25
- data/lib/punchblock/event/ringing.rb +3 -3
- data/lib/punchblock/event/unjoined.rb +5 -25
- data/lib/punchblock/event.rb +0 -10
- data/lib/punchblock/has_headers.rb +20 -26
- data/lib/punchblock/rayo_node.rb +46 -23
- data/lib/punchblock/ref.rb +39 -18
- data/lib/punchblock/translator/asterisk/agi_app.rb +15 -0
- data/lib/punchblock/translator/asterisk/agi_command.rb +3 -1
- data/lib/punchblock/translator/asterisk/ami_error_converter.rb +20 -0
- data/lib/punchblock/translator/asterisk/call.rb +60 -39
- data/lib/punchblock/translator/asterisk/channel.rb +41 -0
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +4 -1
- data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +4 -4
- data/lib/punchblock/translator/asterisk/component/composed_prompt.rb +62 -0
- data/lib/punchblock/translator/asterisk/component/input.rb +1 -0
- data/lib/punchblock/translator/asterisk/component/mrcp_native_prompt.rb +56 -0
- data/lib/punchblock/translator/asterisk/component/mrcp_prompt.rb +53 -0
- data/lib/punchblock/translator/asterisk/component/mrcp_recog_prompt.rb +99 -0
- data/lib/punchblock/translator/asterisk/component/output.rb +30 -22
- data/lib/punchblock/translator/asterisk/component/record.rb +8 -6
- data/lib/punchblock/translator/asterisk/component.rb +6 -5
- data/lib/punchblock/translator/asterisk/unimrcp_app.rb +26 -0
- data/lib/punchblock/translator/asterisk.rb +24 -28
- data/lib/punchblock/translator/dtmf_recognizer.rb +39 -20
- data/lib/punchblock/translator/freeswitch/call.rb +15 -14
- data/lib/punchblock/translator/freeswitch/component/abstract_output.rb +5 -4
- data/lib/punchblock/translator/freeswitch/component/flite_output.rb +1 -1
- data/lib/punchblock/translator/freeswitch/component/input.rb +5 -0
- data/lib/punchblock/translator/freeswitch/component/output.rb +2 -2
- data/lib/punchblock/translator/freeswitch/component/record.rb +19 -13
- data/lib/punchblock/translator/freeswitch/component/tts_output.rb +2 -2
- data/lib/punchblock/translator/freeswitch/component.rb +2 -5
- data/lib/punchblock/translator/freeswitch.rb +2 -2
- data/lib/punchblock/translator/input_component.rb +33 -13
- data/lib/punchblock/uri_list.rb +21 -0
- data/lib/punchblock/version.rb +1 -1
- data/lib/punchblock.rb +4 -3
- data/punchblock.gemspec +7 -3
- data/spec/punchblock/client/component_registry_spec.rb +1 -1
- data/spec/punchblock/client_spec.rb +10 -26
- data/spec/punchblock/command/accept_spec.rb +41 -7
- data/spec/punchblock/command/answer_spec.rb +51 -7
- data/spec/punchblock/command/dial_spec.rb +56 -14
- data/spec/punchblock/command/hangup_spec.rb +41 -7
- data/spec/punchblock/command/join_spec.rb +53 -11
- data/spec/punchblock/command/mute_spec.rb +19 -4
- data/spec/punchblock/command/redirect_spec.rb +40 -10
- data/spec/punchblock/command/reject_spec.rb +43 -11
- data/spec/punchblock/command/unjoin_spec.rb +40 -9
- data/spec/punchblock/command/unmute_spec.rb +19 -4
- data/spec/punchblock/command_node_spec.rb +0 -4
- data/spec/punchblock/component/asterisk/agi/command_spec.rb +16 -39
- data/spec/punchblock/component/asterisk/ami/action_spec.rb +50 -53
- data/spec/punchblock/component/component_node_spec.rb +3 -5
- data/spec/punchblock/component/input_spec.rb +194 -61
- data/spec/punchblock/component/output_spec.rb +194 -62
- data/spec/punchblock/component/prompt_spec.rb +132 -0
- data/spec/punchblock/component/record_spec.rb +70 -32
- data/spec/punchblock/connection/asterisk_spec.rb +17 -3
- data/spec/punchblock/connection/freeswitch_spec.rb +4 -4
- data/spec/punchblock/connection/xmpp_spec.rb +20 -38
- data/spec/punchblock/event/answered_spec.rb +12 -10
- data/spec/punchblock/event/asterisk/ami/event_spec.rb +27 -22
- data/spec/punchblock/event/complete_spec.rb +15 -19
- data/spec/punchblock/event/dtmf_spec.rb +5 -6
- data/spec/punchblock/event/end_spec.rb +20 -10
- data/spec/punchblock/event/joined_spec.rb +8 -7
- data/spec/punchblock/event/offer_spec.rb +41 -12
- data/spec/punchblock/event/ringing_spec.rb +12 -10
- data/spec/punchblock/event/started_speaking_spec.rb +5 -6
- data/spec/punchblock/event/stopped_speaking_spec.rb +5 -6
- data/spec/punchblock/event/unjoined_spec.rb +7 -7
- data/spec/punchblock/ref_spec.rb +86 -9
- data/spec/punchblock/translator/asterisk/call_spec.rb +317 -154
- data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +28 -5
- data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +15 -13
- data/spec/punchblock/translator/asterisk/component/composed_prompt_spec.rb +237 -0
- data/spec/punchblock/translator/asterisk/component/input_spec.rb +171 -14
- data/spec/punchblock/translator/asterisk/component/mrcp_native_prompt_spec.rb +652 -0
- data/spec/punchblock/translator/asterisk/component/mrcp_prompt_spec.rb +646 -0
- data/spec/punchblock/translator/asterisk/component/output_spec.rb +127 -77
- data/spec/punchblock/translator/asterisk/component/record_spec.rb +17 -8
- data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +2 -2
- data/spec/punchblock/translator/asterisk/component_spec.rb +3 -7
- data/spec/punchblock/translator/asterisk_spec.rb +20 -24
- data/spec/punchblock/translator/freeswitch/call_spec.rb +103 -99
- data/spec/punchblock/translator/freeswitch/component/flite_output_spec.rb +17 -8
- data/spec/punchblock/translator/freeswitch/component/input_spec.rb +26 -14
- data/spec/punchblock/translator/freeswitch/component/output_spec.rb +30 -52
- data/spec/punchblock/translator/freeswitch/component/record_spec.rb +23 -19
- data/spec/punchblock/translator/freeswitch/component/tts_output_spec.rb +18 -8
- data/spec/punchblock/translator/freeswitch/component_spec.rb +4 -8
- data/spec/punchblock/translator/freeswitch_spec.rb +11 -14
- data/spec/punchblock/uri_list_spec.rb +49 -0
- data/spec/punchblock_spec.rb +11 -1
- data/spec/spec_helper.rb +7 -11
- data/spec/support/mock_connection_with_event_handler.rb +1 -1
- metadata +104 -24
- data/lib/punchblock/header.rb +0 -9
- data/lib/punchblock/key_value_pair_node.rb +0 -51
- data/spec/punchblock/header_spec.rb +0 -11
@@ -17,7 +17,7 @@ module Punchblock
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def filenames
|
20
|
-
@filenames ||= @component_node.
|
20
|
+
@filenames ||= @component_node.render_documents.first.value.children.map do |node|
|
21
21
|
case node
|
22
22
|
when RubySpeech::SSML::Audio
|
23
23
|
node.src
|
@@ -42,7 +42,7 @@ module Punchblock
|
|
42
42
|
def complete_reason_for_event(event)
|
43
43
|
case event[:application_response]
|
44
44
|
when 'FILE PLAYED'
|
45
|
-
|
45
|
+
finish_reason
|
46
46
|
else
|
47
47
|
Punchblock::Event::Complete::Error.new(:details => "Engine error: #{event[:application_response]}")
|
48
48
|
end
|
@@ -13,11 +13,11 @@ module Punchblock
|
|
13
13
|
|
14
14
|
def execute
|
15
15
|
max_duration = @component_node.max_duration || -1
|
16
|
+
initial_timeout = @component_node.initial_timeout || -1
|
17
|
+
final_timeout = @component_node.final_timeout || -1
|
16
18
|
|
17
19
|
raise OptionError, 'A start-beep value of true is unsupported.' if @component_node.start_beep
|
18
20
|
raise OptionError, 'A start-paused value of true is unsupported.' if @component_node.start_paused
|
19
|
-
raise OptionError, 'An initial-timeout value is unsupported.' if @component_node.initial_timeout && @component_node.initial_timeout != -1
|
20
|
-
raise OptionError, 'A final-timeout value is unsupported.' if @component_node.final_timeout && @component_node.final_timeout != -1
|
21
21
|
raise OptionError, 'A max-duration value that is negative (and not -1) is invalid.' unless max_duration >= -1
|
22
22
|
|
23
23
|
@format = @component_node.format || 'wav'
|
@@ -29,16 +29,18 @@ module Punchblock
|
|
29
29
|
|
30
30
|
record_args = ['start', filename]
|
31
31
|
record_args << max_duration/1000 unless max_duration == -1
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
when :recv
|
36
|
-
|
37
|
-
else
|
38
|
-
call.uuid_foo :setvar, "RECORD_STEREO true"
|
32
|
+
|
33
|
+
direction = case @component_node.direction
|
34
|
+
when :send then :RECORD_WRITE_ONLY
|
35
|
+
when :recv then :RECORD_READ_ONLY
|
36
|
+
else :RECORD_STEREO
|
39
37
|
end
|
40
|
-
|
38
|
+
setvar direction, true
|
39
|
+
|
40
|
+
setvar :RECORD_INITIAL_TIMEOUT_MS, initial_timeout > -1 ? initial_timeout : 0
|
41
|
+
setvar :RECORD_FINAL_TIMEOUT_MS, final_timeout > -1 ? final_timeout : 0
|
41
42
|
|
43
|
+
call.uuid_foo :record, record_args.join(' ')
|
42
44
|
send_ref
|
43
45
|
rescue OptionError => e
|
44
46
|
with_error 'option error', e.message
|
@@ -56,11 +58,15 @@ module Punchblock
|
|
56
58
|
end
|
57
59
|
|
58
60
|
def finished
|
59
|
-
send_complete_event(@complete_reason ||
|
61
|
+
send_complete_event(@complete_reason || max_duration_reason)
|
60
62
|
end
|
61
63
|
|
62
64
|
private
|
63
65
|
|
66
|
+
def setvar(key, value)
|
67
|
+
call.uuid_foo :setvar, "#{key} #{value}"
|
68
|
+
end
|
69
|
+
|
64
70
|
def filename
|
65
71
|
File.join RECORDING_BASE_PATH, [id, @format].join('.')
|
66
72
|
end
|
@@ -73,8 +79,8 @@ module Punchblock
|
|
73
79
|
Punchblock::Event::Complete::Stop.new
|
74
80
|
end
|
75
81
|
|
76
|
-
def
|
77
|
-
Punchblock::Component::Record::Complete::
|
82
|
+
def max_duration_reason
|
83
|
+
Punchblock::Component::Record::Complete::MaxDuration.new
|
78
84
|
end
|
79
85
|
|
80
86
|
def send_complete_event(reason)
|
@@ -9,14 +9,14 @@ module Punchblock
|
|
9
9
|
|
10
10
|
def do_output(engine, default_voice = nil)
|
11
11
|
register_handler :es, :event_name => 'CHANNEL_EXECUTE_COMPLETE' do |event|
|
12
|
-
send_complete_event
|
12
|
+
send_complete_event finish_reason
|
13
13
|
end
|
14
14
|
voice = @component_node.voice || default_voice || 'kal'
|
15
15
|
application :speak, [engine, voice, document].join('|')
|
16
16
|
end
|
17
17
|
|
18
18
|
def document
|
19
|
-
@component_node.
|
19
|
+
@component_node.render_documents.first.value.to_s
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -45,10 +45,7 @@ module Punchblock
|
|
45
45
|
def send_complete_event(reason, recording = nil)
|
46
46
|
return if @complete
|
47
47
|
@complete = true
|
48
|
-
event = Punchblock::Event::Complete.new
|
49
|
-
c.reason = reason
|
50
|
-
c << recording if recording
|
51
|
-
end
|
48
|
+
event = Punchblock::Event::Complete.new reason: reason, recording: recording
|
52
49
|
send_event event
|
53
50
|
terminate
|
54
51
|
end
|
@@ -82,7 +79,7 @@ module Punchblock
|
|
82
79
|
end
|
83
80
|
|
84
81
|
def send_ref
|
85
|
-
set_node_response Ref.new :
|
82
|
+
set_node_response Ref.new uri: id
|
86
83
|
end
|
87
84
|
|
88
85
|
def with_error(name, text)
|
@@ -5,15 +5,9 @@ module Punchblock
|
|
5
5
|
module InputComponent
|
6
6
|
def execute
|
7
7
|
validate
|
8
|
-
|
9
|
-
@recognizer = DTMFRecognizer.new self,
|
10
|
-
@component_node.grammar.value,
|
11
|
-
(@component_node.initial_timeout || -1),
|
12
|
-
(@component_node.inter_digit_timeout || -1)
|
13
|
-
|
8
|
+
setup_dtmf_recognizer
|
14
9
|
send_ref
|
15
|
-
|
16
|
-
@dtmf_handler_id = register_dtmf_event_handler
|
10
|
+
start_timers
|
17
11
|
rescue OptionError => e
|
18
12
|
with_error 'option error', e.message
|
19
13
|
end
|
@@ -32,8 +26,8 @@ module Punchblock
|
|
32
26
|
end
|
33
27
|
end
|
34
28
|
|
35
|
-
def match(
|
36
|
-
complete
|
29
|
+
def match(match)
|
30
|
+
complete success_reason(match)
|
37
31
|
end
|
38
32
|
|
39
33
|
def nomatch
|
@@ -46,14 +40,40 @@ module Punchblock
|
|
46
40
|
|
47
41
|
private
|
48
42
|
|
43
|
+
def input_node
|
44
|
+
@component_node
|
45
|
+
end
|
46
|
+
|
49
47
|
def validate
|
50
|
-
raise OptionError, 'A grammar document is required.' unless
|
51
|
-
raise OptionError, '
|
48
|
+
raise OptionError, 'A grammar document is required.' unless input_node.grammars.first
|
49
|
+
raise OptionError, 'Only a single grammar is supported.' unless input_node.grammars.size == 1
|
50
|
+
raise OptionError, 'A mode value other than DTMF is unsupported.' unless input_node.mode == :dtmf
|
51
|
+
end
|
52
|
+
|
53
|
+
def setup_dtmf_recognizer
|
54
|
+
@recognizer = DTMFRecognizer.new self,
|
55
|
+
input_node.grammars.first.value,
|
56
|
+
(input_node.initial_timeout || -1),
|
57
|
+
(input_node.inter_digit_timeout || -1),
|
58
|
+
input_node.terminator
|
59
|
+
end
|
60
|
+
|
61
|
+
def start_timers
|
62
|
+
@recognizer.start_timers
|
63
|
+
end
|
64
|
+
|
65
|
+
def success_reason(match)
|
66
|
+
nlsml = RubySpeech::NLSML.draw do
|
67
|
+
interpretation confidence: match.confidence do
|
68
|
+
instance match.interpretation
|
69
|
+
input match.utterance, mode: match.mode
|
70
|
+
end
|
71
|
+
end
|
72
|
+
Punchblock::Component::Input::Complete::Match.new :nlsml => nlsml
|
52
73
|
end
|
53
74
|
|
54
75
|
def complete(reason)
|
55
76
|
unregister_dtmf_event_handler
|
56
|
-
@recognizer.finalize if @recognizer
|
57
77
|
send_complete_event reason
|
58
78
|
end
|
59
79
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Punchblock
|
4
|
+
class URIList < SimpleDelegator
|
5
|
+
def self.import(string)
|
6
|
+
new string.strip.split("\n").map(&:strip)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(*list)
|
10
|
+
super list.flatten
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
join("\n")
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
self.__getobj__ == other.to_ary
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/punchblock/version.rb
CHANGED
data/lib/punchblock.rb
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
active_support/dependencies/autoload
|
5
5
|
active_support/core_ext/object/blank
|
6
6
|
active_support/core_ext/module/delegation
|
7
|
+
active_support/inflector
|
7
8
|
future-resource
|
8
9
|
has_guarded_handlers
|
9
10
|
ruby_speech
|
@@ -22,11 +23,11 @@ module Punchblock
|
|
22
23
|
autoload :DeadActorSafety
|
23
24
|
autoload :DisconnectedError
|
24
25
|
autoload :HasHeaders
|
25
|
-
autoload :Header
|
26
26
|
autoload :MediaNode
|
27
27
|
autoload :ProtocolError
|
28
28
|
autoload :RayoNode
|
29
29
|
autoload :Translator
|
30
|
+
autoload :URIList
|
30
31
|
|
31
32
|
class << self
|
32
33
|
def logger
|
@@ -50,7 +51,7 @@ module Punchblock
|
|
50
51
|
# @return [Punchblock::Client] a punchblock client object
|
51
52
|
#
|
52
53
|
def client_with_connection(type, options)
|
53
|
-
connection = Connection.const_get(type.to_s.classify).new options
|
54
|
+
connection = Connection.const_get(type == :xmpp ? 'XMPP' : type.to_s.classify).new options
|
54
55
|
Client.new :connection => connection
|
55
56
|
rescue NameError
|
56
57
|
raise ArgumentError, "Connection type #{type.inspect} is not valid."
|
@@ -74,7 +75,7 @@ module Punchblock
|
|
74
75
|
RAYO_VERSION = '1'
|
75
76
|
RAYO_NAMESPACES = {:core => [BASE_RAYO_NAMESPACE, RAYO_VERSION].compact.join(':')}
|
76
77
|
|
77
|
-
[:ext, :record, :output, :input].each do |ns|
|
78
|
+
[:ext, :record, :output, :input, :prompt].each do |ns|
|
78
79
|
RAYO_NAMESPACES[ns] = [BASE_RAYO_NAMESPACE, ns.to_s, RAYO_VERSION].compact.join(':')
|
79
80
|
RAYO_NAMESPACES[:"#{ns}_complete"] = [BASE_RAYO_NAMESPACE, ns.to_s, 'complete', RAYO_VERSION].compact.join(':')
|
80
81
|
end
|
data/punchblock.gemspec
CHANGED
@@ -22,17 +22,18 @@ Gem::Specification.new do |s|
|
|
22
22
|
|
23
23
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.3.7") if s.respond_to? :required_rubygems_version=
|
24
24
|
|
25
|
-
s.add_runtime_dependency %q<niceogiri>, ["~> 1.1"]
|
26
25
|
s.add_runtime_dependency %q<nokogiri>, ["~> 1.5", ">= 1.5.6"]
|
27
26
|
s.add_runtime_dependency %q<blather>, [">= 0.7.0"]
|
28
|
-
s.add_runtime_dependency %q<activesupport>, ["
|
27
|
+
s.add_runtime_dependency %q<activesupport>, [">= 3.0.0", "< 5.0.0"]
|
29
28
|
s.add_runtime_dependency %q<state_machine>, ["~> 1.0"]
|
30
29
|
s.add_runtime_dependency %q<future-resource>, ["~> 1.0"]
|
31
30
|
s.add_runtime_dependency %q<has-guarded-handlers>, ["~> 1.5"]
|
32
31
|
s.add_runtime_dependency %q<celluloid>, ["~> 0.14"]
|
33
32
|
s.add_runtime_dependency %q<ruby_ami>, ["~> 2.0"]
|
34
33
|
s.add_runtime_dependency %q<ruby_fs>, ["~> 1.1"]
|
35
|
-
s.add_runtime_dependency %q<ruby_speech>, ["~>
|
34
|
+
s.add_runtime_dependency %q<ruby_speech>, ["~> 2.0"]
|
35
|
+
s.add_runtime_dependency %q<virtus>
|
36
|
+
s.add_runtime_dependency %q<ruby_jid>, ["~> 1.0"]
|
36
37
|
|
37
38
|
s.add_development_dependency %q<bundler>, ["~> 1.0"]
|
38
39
|
s.add_development_dependency %q<rspec>, ["~> 2.7"]
|
@@ -43,4 +44,7 @@ Gem::Specification.new do |s|
|
|
43
44
|
s.add_development_dependency %q<countdownlatch>, [">= 0"]
|
44
45
|
s.add_development_dependency %q<guard-rspec>
|
45
46
|
s.add_development_dependency %q<rb-fsevent>, ['~> 0.9']
|
47
|
+
s.add_development_dependency %q<coveralls>, ['>= 0']
|
48
|
+
s.add_development_dependency %q<guard-rake>
|
49
|
+
s.add_development_dependency %q<benchmark_suite>
|
46
50
|
end
|
@@ -6,7 +6,7 @@ module Punchblock
|
|
6
6
|
class Client
|
7
7
|
describe ComponentRegistry do
|
8
8
|
let(:component_id) { 'abc123' }
|
9
|
-
let(:component) {
|
9
|
+
let(:component) { double 'Component', :component_id => component_id }
|
10
10
|
|
11
11
|
it 'should store components and allow lookup by ID' do
|
12
12
|
subject << component
|
@@ -8,15 +8,14 @@ module Punchblock
|
|
8
8
|
|
9
9
|
subject { Client.new :connection => connection }
|
10
10
|
|
11
|
-
its(:event_queue) { should be_a Queue }
|
12
11
|
its(:connection) { should be connection }
|
13
12
|
its(:component_registry) { should be_a Client::ComponentRegistry }
|
14
13
|
|
15
14
|
let(:call_id) { 'abc123' }
|
16
|
-
let(:mock_event) {
|
15
|
+
let(:mock_event) { double('Event').as_null_object }
|
17
16
|
let(:component_id) { 'abc123' }
|
18
|
-
let(:mock_component) {
|
19
|
-
let(:mock_command) {
|
17
|
+
let(:mock_component) { double 'Component', :component_id => component_id }
|
18
|
+
let(:mock_command) { double 'Command' }
|
20
19
|
|
21
20
|
describe '#run' do
|
22
21
|
it 'should start up the connection' do
|
@@ -50,13 +49,8 @@ module Punchblock
|
|
50
49
|
mock_component.should_receive(:add_event).with mock_event
|
51
50
|
end
|
52
51
|
|
53
|
-
it 'should not queue up the event' do
|
54
|
-
subject.handle_event mock_event
|
55
|
-
subject.event_queue.should be_empty
|
56
|
-
end
|
57
|
-
|
58
52
|
it 'should not call event handlers' do
|
59
|
-
handler =
|
53
|
+
handler = double 'handler'
|
60
54
|
handler.should_receive(:call).never
|
61
55
|
subject.register_event_handler do |event|
|
62
56
|
handler.call event
|
@@ -70,23 +64,13 @@ module Punchblock
|
|
70
64
|
mock_event.stub :source => nil
|
71
65
|
end
|
72
66
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
handler.call event
|
79
|
-
end
|
80
|
-
subject.handle_event mock_event
|
81
|
-
subject.event_queue.should be_empty
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
context 'if event handlers have not been set' do
|
86
|
-
it 'should queue up the event' do
|
87
|
-
subject.handle_event mock_event
|
88
|
-
subject.event_queue.pop(true).should be == mock_event
|
67
|
+
it 'should call registered event handlers' do
|
68
|
+
handler = double 'handler'
|
69
|
+
handler.should_receive(:call).once.with mock_event
|
70
|
+
subject.register_event_handler do |event|
|
71
|
+
handler.call event
|
89
72
|
end
|
73
|
+
subject.handle_event mock_event
|
90
74
|
end
|
91
75
|
end
|
92
76
|
end
|
@@ -6,18 +6,52 @@ module Punchblock
|
|
6
6
|
module Command
|
7
7
|
describe Accept do
|
8
8
|
it 'registers itself' do
|
9
|
-
RayoNode.class_from_registration(:accept, 'urn:xmpp:rayo:1').should be ==
|
9
|
+
RayoNode.class_from_registration(:accept, 'urn:xmpp:rayo:1').should be == described_class
|
10
10
|
end
|
11
11
|
|
12
|
-
it_should_behave_like 'command_headers'
|
13
|
-
|
14
12
|
describe "from a stanza" do
|
15
|
-
let(:stanza)
|
13
|
+
let(:stanza) do
|
14
|
+
<<-STANZA
|
15
|
+
<accept xmlns="urn:xmpp:rayo:1">
|
16
|
+
<header name="X-skill" value="agent" />
|
17
|
+
<header name="X-customer-id" value="8877" />
|
18
|
+
</accept>
|
19
|
+
STANZA
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { RayoNode.from_xml parse_stanza(stanza).root, '9f00061', '1' }
|
23
|
+
|
24
|
+
it { should be_instance_of described_class }
|
25
|
+
its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
26
|
+
|
27
|
+
context "with no headers provided" do
|
28
|
+
let(:stanza) { '<accept xmlns="urn:xmpp:rayo:1"/>' }
|
29
|
+
|
30
|
+
its(:headers) { should == {} }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "when setting options in initializer" do
|
35
|
+
subject { described_class.new headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
36
|
+
|
37
|
+
its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
16
38
|
|
17
|
-
|
39
|
+
describe "exporting to Rayo" do
|
40
|
+
it "should export to XML that can be understood by its parser" do
|
41
|
+
new_instance = RayoNode.from_xml subject.to_rayo
|
42
|
+
new_instance.should be_instance_of described_class
|
43
|
+
new_instance.headers.should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' }
|
44
|
+
end
|
18
45
|
|
19
|
-
|
46
|
+
it "should render to a parent node if supplied" do
|
47
|
+
doc = Nokogiri::XML::Document.new
|
48
|
+
parent = Nokogiri::XML::Node.new 'foo', doc
|
49
|
+
doc.root = parent
|
50
|
+
rayo_doc = subject.to_rayo(parent)
|
51
|
+
rayo_doc.should == parent
|
52
|
+
end
|
53
|
+
end
|
20
54
|
end
|
21
55
|
end
|
22
56
|
end
|
23
|
-
end
|
57
|
+
end
|
@@ -6,18 +6,62 @@ module Punchblock
|
|
6
6
|
module Command
|
7
7
|
describe Answer do
|
8
8
|
it 'registers itself' do
|
9
|
-
RayoNode.class_from_registration(:answer, 'urn:xmpp:rayo:1').should be ==
|
9
|
+
RayoNode.class_from_registration(:answer, 'urn:xmpp:rayo:1').should be == described_class
|
10
10
|
end
|
11
11
|
|
12
|
-
it_should_behave_like 'command_headers'
|
13
|
-
|
14
12
|
describe "from a stanza" do
|
15
|
-
let(:stanza)
|
13
|
+
let(:stanza) do
|
14
|
+
<<-STANZA
|
15
|
+
<answer xmlns="urn:xmpp:rayo:1">
|
16
|
+
<header name="X-skill" value="agent" />
|
17
|
+
<header name="X-customer-id" value="8877" />
|
18
|
+
</answer>
|
19
|
+
STANZA
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { RayoNode.from_xml parse_stanza(stanza).root, '9f00061', '1' }
|
23
|
+
|
24
|
+
it { should be_instance_of described_class }
|
25
|
+
its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
26
|
+
|
27
|
+
context "with no headers provided" do
|
28
|
+
let(:stanza) { '<answer xmlns="urn:xmpp:rayo:1"/>' }
|
29
|
+
|
30
|
+
its(:headers) { should == {} }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "when setting options in initializer" do
|
35
|
+
subject { described_class.new headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
36
|
+
|
37
|
+
its(:headers) { should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
38
|
+
|
39
|
+
describe "exporting to Rayo" do
|
40
|
+
it "should export to XML that can be understood by its parser" do
|
41
|
+
new_instance = RayoNode.from_xml subject.to_rayo
|
42
|
+
new_instance.should be_instance_of described_class
|
43
|
+
new_instance.headers.should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' }
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should render to a parent node if supplied" do
|
47
|
+
doc = Nokogiri::XML::Document.new
|
48
|
+
parent = Nokogiri::XML::Node.new 'foo', doc
|
49
|
+
doc.root = parent
|
50
|
+
rayo_doc = subject.to_rayo(parent)
|
51
|
+
rayo_doc.should == parent
|
52
|
+
end
|
16
53
|
|
17
|
-
|
54
|
+
context "with multiple headers of the same name" do
|
55
|
+
subject { described_class.new headers: { 'X-skill' => ['sales', 'complaints'] } }
|
18
56
|
|
19
|
-
|
57
|
+
it "should export to XML that can be understood by its parser" do
|
58
|
+
new_instance = RayoNode.from_xml subject.to_rayo
|
59
|
+
new_instance.should be_instance_of described_class
|
60
|
+
new_instance.headers.should == { 'X-skill' => ['sales', 'complaints'] }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
20
64
|
end
|
21
65
|
end
|
22
66
|
end
|
23
|
-
end
|
67
|
+
end
|
@@ -7,61 +7,103 @@ module Punchblock
|
|
7
7
|
describe Dial do
|
8
8
|
|
9
9
|
it 'registers itself' do
|
10
|
-
RayoNode.class_from_registration(:dial, 'urn:xmpp:rayo:1').should be ==
|
10
|
+
RayoNode.class_from_registration(:dial, 'urn:xmpp:rayo:1').should be == described_class
|
11
11
|
end
|
12
12
|
|
13
|
-
let(:join_params) { {:
|
13
|
+
let(:join_params) { {:call_uri => 'abc123'} }
|
14
14
|
|
15
15
|
describe "when setting options in initializer" do
|
16
|
-
subject {
|
17
|
-
|
18
|
-
it_should_behave_like 'command_headers'
|
16
|
+
subject { described_class.new to: 'tel:+14155551212', from: 'tel:+13035551212', timeout: 30000, headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' }, join: join_params }
|
19
17
|
|
20
18
|
its(:to) { should be == 'tel:+14155551212' }
|
21
19
|
its(:from) { should be == 'tel:+13035551212' }
|
22
20
|
its(:timeout) { should be == 30000 }
|
23
21
|
its(:join) { should be == Join.new(join_params) }
|
22
|
+
its(:headers) { should be == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
23
|
+
|
24
|
+
describe "exporting to Rayo" do
|
25
|
+
it "should export to XML that can be understood by its parser" do
|
26
|
+
new_instance = RayoNode.from_xml subject.to_rayo
|
27
|
+
new_instance.should be_instance_of described_class
|
28
|
+
new_instance.to.should == 'tel:+14155551212'
|
29
|
+
new_instance.from.should == 'tel:+13035551212'
|
30
|
+
new_instance.timeout.should == 30000
|
31
|
+
new_instance.join.should == Join.new(join_params)
|
32
|
+
new_instance.headers.should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' }
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should render to a parent node if supplied" do
|
36
|
+
doc = Nokogiri::XML::Document.new
|
37
|
+
parent = Nokogiri::XML::Node.new 'foo', doc
|
38
|
+
doc.root = parent
|
39
|
+
rayo_doc = subject.to_rayo(parent)
|
40
|
+
rayo_doc.should == parent
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when attributes are not set" do
|
44
|
+
subject { described_class.new to: 'abc123' }
|
45
|
+
|
46
|
+
it "should not include them in the XML representation" do
|
47
|
+
subject.to_rayo['to'].should == 'abc123'
|
48
|
+
subject.to_rayo['from'].should be_nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
24
52
|
end
|
25
53
|
|
26
54
|
describe "from a stanza" do
|
27
55
|
let :stanza do
|
28
56
|
<<-MESSAGE
|
29
57
|
<dial to='tel:+14155551212' from='tel:+13035551212' timeout='30000' xmlns='urn:xmpp:rayo:1'>
|
30
|
-
<join call-
|
58
|
+
<join call-uri="abc123" />
|
31
59
|
<header name="X-skill" value="agent" />
|
32
60
|
<header name="X-customer-id" value="8877" />
|
33
61
|
</dial>
|
34
62
|
MESSAGE
|
35
63
|
end
|
36
64
|
|
37
|
-
subject { RayoNode.
|
65
|
+
subject { RayoNode.from_xml parse_stanza(stanza).root, '9f00061', '1' }
|
38
66
|
|
39
|
-
it { should be_instance_of
|
40
|
-
|
41
|
-
it_should_behave_like 'event_headers'
|
67
|
+
it { should be_instance_of described_class }
|
42
68
|
|
43
69
|
its(:to) { should be == 'tel:+14155551212' }
|
44
70
|
its(:from) { should be == 'tel:+13035551212' }
|
45
71
|
its(:timeout) { should be == 30000 }
|
46
72
|
its(:join) { should be == Join.new(join_params) }
|
73
|
+
its(:headers) { should be == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
74
|
+
|
75
|
+
context "with no headers provided" do
|
76
|
+
let(:stanza) { '<dial xmlns="urn:xmpp:rayo:1"/>' }
|
77
|
+
|
78
|
+
its(:headers) { should == {} }
|
79
|
+
end
|
47
80
|
end
|
48
81
|
|
49
82
|
describe "#response=" do
|
50
83
|
before { subject.request! }
|
51
84
|
|
52
85
|
let(:call_id) { 'abc123' }
|
86
|
+
let(:domain) { 'rayo.net' }
|
53
87
|
|
54
88
|
let :ref do
|
55
|
-
Ref.new
|
56
|
-
|
57
|
-
|
89
|
+
Ref.new uri: "xmpp:#{call_id}@#{domain}"
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should set the transport from the ref" do
|
93
|
+
subject.response = ref
|
94
|
+
subject.transport.should be == 'xmpp'
|
58
95
|
end
|
59
96
|
|
60
97
|
it "should set the call ID from the ref" do
|
61
98
|
subject.response = ref
|
62
99
|
subject.target_call_id.should be == call_id
|
63
100
|
end
|
101
|
+
|
102
|
+
it "should set the domain from the ref" do
|
103
|
+
subject.response = ref
|
104
|
+
subject.domain.should be == domain
|
105
|
+
end
|
64
106
|
end
|
65
107
|
end
|
66
108
|
end
|
67
|
-
end
|
109
|
+
end
|