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.
- 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
|