punchblock 0.7.1 → 0.7.2
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.
- data/CHANGELOG.md +12 -0
- data/lib/punchblock.rb +1 -0
- data/lib/punchblock/command/join.rb +6 -6
- data/lib/punchblock/command/unjoin.rb +6 -6
- data/lib/punchblock/command_node.rb +1 -0
- data/lib/punchblock/component/input.rb +24 -4
- data/lib/punchblock/component/output.rb +5 -1
- data/lib/punchblock/component/tropo/ask.rb +3 -1
- data/lib/punchblock/connection/xmpp.rb +28 -10
- data/lib/punchblock/event/joined.rb +6 -6
- data/lib/punchblock/event/unjoined.rb +6 -6
- data/lib/punchblock/media_container.rb +6 -5
- data/lib/punchblock/protocol_error.rb +5 -0
- data/lib/punchblock/rayo_node.rb +1 -1
- data/lib/punchblock/translator/asterisk.rb +3 -3
- data/lib/punchblock/translator/asterisk/call.rb +9 -3
- data/lib/punchblock/translator/asterisk/component.rb +35 -0
- data/lib/punchblock/translator/asterisk/component/asterisk.rb +1 -0
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +14 -23
- data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +5 -18
- data/lib/punchblock/translator/asterisk/component/asterisk/output.rb +94 -0
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +1 -0
- data/spec/punchblock/command/join_spec.rb +4 -4
- data/spec/punchblock/command/unjoin_spec.rb +4 -4
- data/spec/punchblock/component/input_spec.rb +28 -31
- data/spec/punchblock/component/output_spec.rb +23 -5
- data/spec/punchblock/component/tropo/ask_spec.rb +31 -34
- data/spec/punchblock/connection/xmpp_spec.rb +105 -3
- data/spec/punchblock/event/joined_spec.rb +4 -4
- data/spec/punchblock/event/unjoined_spec.rb +4 -4
- data/spec/punchblock/protocol_error_spec.rb +32 -1
- data/spec/punchblock/translator/asterisk/call_spec.rb +17 -3
- data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +17 -0
- data/spec/punchblock/translator/asterisk/component/asterisk/output_spec.rb +489 -0
- data/spec/punchblock/translator/asterisk_spec.rb +14 -3
- metadata +53 -44
- data/assets/ozone/ask-1.0.xsd +0 -56
- data/assets/ozone/conference-1.0.xsd +0 -17
- data/assets/ozone/ozone-1.0.xsd +0 -127
- data/assets/ozone/say-1.0.xsd +0 -24
- data/assets/ozone/transfer-1.0.xsd +0 -32
|
@@ -10,6 +10,41 @@ module Punchblock
|
|
|
10
10
|
include Celluloid
|
|
11
11
|
|
|
12
12
|
attr_reader :id
|
|
13
|
+
|
|
14
|
+
def initialize(component_node, call = nil)
|
|
15
|
+
@component_node, @call = component_node, call
|
|
16
|
+
@id = UUIDTools::UUID.random_create.to_s
|
|
17
|
+
setup
|
|
18
|
+
pb_logger.debug "Starting up..."
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def setup
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def set_node_response(value)
|
|
25
|
+
pb_logger.debug "Setting response on component node to #{value}"
|
|
26
|
+
@component_node.response = value
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def send_ref
|
|
30
|
+
set_node_response Ref.new :id => id
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def with_error(name, text)
|
|
34
|
+
set_node_response ProtocolError.new(name, text)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def complete_event(reason)
|
|
38
|
+
Punchblock::Event::Complete.new.tap do |c|
|
|
39
|
+
c.reason = reason
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def send_event(event)
|
|
44
|
+
event.component_id = id
|
|
45
|
+
pb_logger.debug "Sending event #{event}"
|
|
46
|
+
@component_node.add_event event
|
|
47
|
+
end
|
|
13
48
|
end
|
|
14
49
|
end
|
|
15
50
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'uri'
|
|
2
|
+
require 'active_support/core_ext/string/filters'
|
|
2
3
|
|
|
3
4
|
module Punchblock
|
|
4
5
|
module Translator
|
|
@@ -8,11 +9,8 @@ module Punchblock
|
|
|
8
9
|
class AGICommand < Component
|
|
9
10
|
attr_reader :action
|
|
10
11
|
|
|
11
|
-
def
|
|
12
|
-
@component_node, @call = component_node, call
|
|
13
|
-
@id = UUIDTools::UUID.random_create.to_s
|
|
12
|
+
def setup
|
|
14
13
|
@action = create_action
|
|
15
|
-
pb_logger.debug "Starting up..."
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def execute
|
|
@@ -40,42 +38,35 @@ module Punchblock
|
|
|
40
38
|
private
|
|
41
39
|
|
|
42
40
|
def create_action
|
|
43
|
-
RubyAMI::Action.new 'AGI', 'Channel' => @call.channel, 'Command' =>
|
|
41
|
+
RubyAMI::Action.new 'AGI', 'Channel' => @call.channel, 'Command' => agi_command, 'CommandID' => id do |response|
|
|
44
42
|
handle_response response
|
|
45
43
|
end
|
|
46
44
|
end
|
|
47
45
|
|
|
46
|
+
def agi_command
|
|
47
|
+
"#{@component_node.name} #{@component_node.params_array.map { |arg| quote_arg(arg) }.join(' ')}".squish
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Arguments surrounded by quotes; quotes backslash-escaped.
|
|
51
|
+
# See parse_args in asterisk/res/res_agi.c (Asterisk 1.4.21.1)
|
|
52
|
+
def quote_arg(arg)
|
|
53
|
+
'"' + arg.to_s.gsub(/["\\]/) { |m| "\\#{m}" } + '"'
|
|
54
|
+
end
|
|
55
|
+
|
|
48
56
|
def handle_response(response)
|
|
49
57
|
pb_logger.debug "Handling response: #{response.inspect}"
|
|
50
58
|
case response
|
|
51
59
|
when RubyAMI::Error
|
|
52
60
|
set_node_response false
|
|
53
61
|
when RubyAMI::Response
|
|
54
|
-
|
|
62
|
+
send_ref
|
|
55
63
|
end
|
|
56
64
|
end
|
|
57
65
|
|
|
58
|
-
def set_node_response(value)
|
|
59
|
-
pb_logger.debug "Setting response on component node to #{value}"
|
|
60
|
-
@component_node.response = value
|
|
61
|
-
end
|
|
62
|
-
|
|
63
66
|
def success_reason(event)
|
|
64
67
|
code, result, data = parse_agi_result event['Result']
|
|
65
68
|
Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new :code => code, :result => result, :data => data
|
|
66
69
|
end
|
|
67
|
-
|
|
68
|
-
def complete_event(reason)
|
|
69
|
-
Punchblock::Event::Complete.new.tap do |c|
|
|
70
|
-
c.reason = reason
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def send_event(event)
|
|
75
|
-
event.component_id = id
|
|
76
|
-
pb_logger.debug "Sending event #{event.inspect}"
|
|
77
|
-
@component_node.add_event event
|
|
78
|
-
end
|
|
79
70
|
end
|
|
80
71
|
end
|
|
81
72
|
end
|
|
@@ -7,10 +7,13 @@ module Punchblock
|
|
|
7
7
|
attr_reader :action
|
|
8
8
|
|
|
9
9
|
def initialize(component_node, translator)
|
|
10
|
-
|
|
10
|
+
super
|
|
11
|
+
@translator = translator
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def setup
|
|
11
15
|
@action = create_action
|
|
12
16
|
@id = @action.action_id
|
|
13
|
-
pb_logger.debug "Starting up..."
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
def execute
|
|
@@ -34,10 +37,6 @@ module Punchblock
|
|
|
34
37
|
@translator.send_ami_action! @action
|
|
35
38
|
end
|
|
36
39
|
|
|
37
|
-
def send_ref
|
|
38
|
-
@component_node.response = Ref.new :id => @action.action_id
|
|
39
|
-
end
|
|
40
|
-
|
|
41
40
|
def handle_response(response)
|
|
42
41
|
pb_logger.debug "Handling response #{response.inspect}"
|
|
43
42
|
case response
|
|
@@ -60,12 +59,6 @@ module Punchblock
|
|
|
60
59
|
Punchblock::Component::Asterisk::AMI::Action::Complete::Success.new :message => headers.delete('Message'), :attributes => headers
|
|
61
60
|
end
|
|
62
61
|
|
|
63
|
-
def complete_event(reason)
|
|
64
|
-
Punchblock::Event::Complete.new.tap do |c|
|
|
65
|
-
c.reason = reason
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
62
|
def send_events
|
|
70
63
|
return unless @action.has_causal_events?
|
|
71
64
|
@action.events.each do |e|
|
|
@@ -82,12 +75,6 @@ module Punchblock
|
|
|
82
75
|
headers.delete 'ActionID'
|
|
83
76
|
Event::Asterisk::AMI::Event.new :name => ami_event.name, :attributes => headers
|
|
84
77
|
end
|
|
85
|
-
|
|
86
|
-
def send_event(event)
|
|
87
|
-
event.component_id = id
|
|
88
|
-
pb_logger.debug "Sending event #{event}"
|
|
89
|
-
@component_node.add_event event
|
|
90
|
-
end
|
|
91
78
|
end
|
|
92
79
|
end
|
|
93
80
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require 'active_support/core_ext/string/filters'
|
|
2
|
+
|
|
3
|
+
module Punchblock
|
|
4
|
+
module Translator
|
|
5
|
+
class Asterisk
|
|
6
|
+
module Component
|
|
7
|
+
module Asterisk
|
|
8
|
+
class Output < Component
|
|
9
|
+
|
|
10
|
+
def setup
|
|
11
|
+
@media_engine = @call.translator.media_engine
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def execute
|
|
15
|
+
return with_error 'option error', 'An SSML document is required.' unless @component_node.ssml
|
|
16
|
+
|
|
17
|
+
return with_error 'option error', 'An interrupt-on value of speech is unsupported.' if @component_node.interrupt_on == :speech
|
|
18
|
+
|
|
19
|
+
[:start_offset, :start_paused, :repeat_interval, :repeat_times, :max_time].each do |opt|
|
|
20
|
+
return with_error 'option error', "A #{opt} value is unsupported on Asterisk." if @component_node.send opt
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
case @media_engine
|
|
24
|
+
when :asterisk, nil
|
|
25
|
+
return with_error 'option error', "A voice value is unsupported on Asterisk." if @component_node.voice
|
|
26
|
+
|
|
27
|
+
@execution_elements = @component_node.ssml.children.map do |node|
|
|
28
|
+
case node
|
|
29
|
+
when RubySpeech::SSML::Audio
|
|
30
|
+
lambda { current_actor.play_audio! node.src }
|
|
31
|
+
end
|
|
32
|
+
end.compact
|
|
33
|
+
|
|
34
|
+
@pending_actions = @execution_elements.count
|
|
35
|
+
|
|
36
|
+
send_ref
|
|
37
|
+
|
|
38
|
+
@interrupt_digits = '0123456789*#' if [:any, :dtmf].include? @component_node.interrupt_on
|
|
39
|
+
|
|
40
|
+
@execution_elements.each do |element|
|
|
41
|
+
element.call
|
|
42
|
+
wait :continue
|
|
43
|
+
process_playback_completion
|
|
44
|
+
end
|
|
45
|
+
when :unimrcp
|
|
46
|
+
doc = @component_node.ssml.to_s.squish.gsub(/["\\]/) { |m| "\\#{m}" }
|
|
47
|
+
send_ref
|
|
48
|
+
@call.send_agi_action! 'EXEC MRCPSynth', doc, mrcpsynth_options do |complete_event|
|
|
49
|
+
pb_logger.debug "MRCPSynth completed with #{complete_event}."
|
|
50
|
+
send_event complete_event(success_reason)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def process_playback_completion
|
|
56
|
+
@pending_actions -= 1
|
|
57
|
+
pb_logger.debug "Received action completion. Now waiting on #{@pending_actions} actions."
|
|
58
|
+
if @pending_actions < 1
|
|
59
|
+
pb_logger.debug "Sending complete event"
|
|
60
|
+
send_event complete_event(success_reason)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def continue(event = nil)
|
|
65
|
+
signal :continue, event
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def play_audio(path)
|
|
69
|
+
pb_logger.debug "Playing an audio file (#{path}) via STREAM FILE"
|
|
70
|
+
op = current_actor
|
|
71
|
+
@call.send_agi_action! 'STREAM FILE', path, @interrupt_digits do |complete_event|
|
|
72
|
+
pb_logger.debug "STREAM FILE completed with #{complete_event}. Signalling to continue execution."
|
|
73
|
+
op.continue! complete_event
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def mrcpsynth_options
|
|
80
|
+
[].tap do |opts|
|
|
81
|
+
opts << 'i=any' if [:any, :dtmf].include? @component_node.interrupt_on
|
|
82
|
+
opts << "v=#{@component_node.voice}" if @component_node.voice
|
|
83
|
+
end.join '&'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def success_reason
|
|
87
|
+
Punchblock::Component::Output::Complete::Success.new
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/punchblock/version.rb
CHANGED
data/punchblock.gemspec
CHANGED
|
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
|
|
|
30
30
|
s.add_runtime_dependency %q<has-guarded-handlers>, [">= 0.1.0"]
|
|
31
31
|
s.add_runtime_dependency %q<celluloid>, [">= 0.6.0"]
|
|
32
32
|
s.add_runtime_dependency %q<ruby_ami>, [">= 0.1.3"]
|
|
33
|
+
s.add_runtime_dependency %q<ruby_speech>, [">= 0.3.4"]
|
|
33
34
|
|
|
34
35
|
s.add_development_dependency %q<bundler>, ["~> 1.0.0"]
|
|
35
36
|
s.add_development_dependency %q<rspec>, [">= 2.5.0"]
|
|
@@ -9,10 +9,10 @@ module Punchblock
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
describe "when setting options in initializer" do
|
|
12
|
-
subject { Join.new :other_call_id => 'abc123', :
|
|
12
|
+
subject { Join.new :other_call_id => 'abc123', :mixer_name => 'blah', :direction => :duplex, :media => :bridge }
|
|
13
13
|
|
|
14
14
|
its(:other_call_id) { should == 'abc123' }
|
|
15
|
-
its(:
|
|
15
|
+
its(:mixer_name) { should == 'blah' }
|
|
16
16
|
its(:direction) { should == :duplex }
|
|
17
17
|
its(:media) { should == :bridge }
|
|
18
18
|
end
|
|
@@ -22,7 +22,7 @@ module Punchblock
|
|
|
22
22
|
<<-MESSAGE
|
|
23
23
|
<join xmlns="urn:xmpp:rayo:1"
|
|
24
24
|
call-id="abc123"
|
|
25
|
-
mixer-
|
|
25
|
+
mixer-name="blah"
|
|
26
26
|
direction="duplex"
|
|
27
27
|
media="bridge" />
|
|
28
28
|
MESSAGE
|
|
@@ -33,7 +33,7 @@ module Punchblock
|
|
|
33
33
|
it { should be_instance_of Join }
|
|
34
34
|
|
|
35
35
|
its(:other_call_id) { should == 'abc123' }
|
|
36
|
-
its(:
|
|
36
|
+
its(:mixer_name) { should == 'blah' }
|
|
37
37
|
its(:direction) { should == :duplex }
|
|
38
38
|
its(:media) { should == :bridge }
|
|
39
39
|
end
|
|
@@ -9,10 +9,10 @@ module Punchblock
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
describe "when setting options in initializer" do
|
|
12
|
-
subject { Unjoin.new :other_call_id => 'abc123', :
|
|
12
|
+
subject { Unjoin.new :other_call_id => 'abc123', :mixer_name => 'blah' }
|
|
13
13
|
|
|
14
14
|
its(:other_call_id) { should == 'abc123' }
|
|
15
|
-
its(:
|
|
15
|
+
its(:mixer_name) { should == 'blah' }
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
describe "from a stanza" do
|
|
@@ -20,7 +20,7 @@ module Punchblock
|
|
|
20
20
|
<<-MESSAGE
|
|
21
21
|
<unjoin xmlns="urn:xmpp:rayo:1"
|
|
22
22
|
call-id="abc123"
|
|
23
|
-
mixer-
|
|
23
|
+
mixer-name="blah" />
|
|
24
24
|
MESSAGE
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -29,7 +29,7 @@ module Punchblock
|
|
|
29
29
|
it { should be_instance_of Unjoin }
|
|
30
30
|
|
|
31
31
|
its(:other_call_id) { should == 'abc123' }
|
|
32
|
-
its(:
|
|
32
|
+
its(:mixer_name) { should == 'blah' }
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
end
|
|
@@ -81,9 +81,19 @@ module Punchblock
|
|
|
81
81
|
its(:min_confidence) { should == 0.5 }
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
def grxml_doc(mode = :dtmf)
|
|
85
|
+
RubySpeech::GRXML.draw :mode => mode.to_s, :root => 'digits' do
|
|
86
|
+
rule id: 'digits' do
|
|
87
|
+
one_of do
|
|
88
|
+
0.upto(1) { |d| item { d.to_s } }
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
84
94
|
describe Input::Grammar do
|
|
85
|
-
describe "when not passing a
|
|
86
|
-
subject { Input::Grammar.new :value =>
|
|
95
|
+
describe "when not passing a content type" do
|
|
96
|
+
subject { Input::Grammar.new :value => grxml_doc }
|
|
87
97
|
its(:content_type) { should == 'application/grammar+grxml' }
|
|
88
98
|
end
|
|
89
99
|
|
|
@@ -98,40 +108,27 @@ module Punchblock
|
|
|
98
108
|
end
|
|
99
109
|
|
|
100
110
|
describe 'with a GRXML grammar' do
|
|
101
|
-
subject { Input::Grammar.new :value =>
|
|
102
|
-
|
|
103
|
-
let :grxml do
|
|
104
|
-
<<-GRXML
|
|
105
|
-
<grammar xmlns="http://www.w3.org/2001/06/grammar" root="MAINRULE">
|
|
106
|
-
<rule id="MAINRULE">
|
|
107
|
-
<one-of>
|
|
108
|
-
<item>
|
|
109
|
-
<item repeat="0-1"> need a</item>
|
|
110
|
-
<item repeat="0-1"> i need a</item>
|
|
111
|
-
<one-of>
|
|
112
|
-
<item> clue </item>
|
|
113
|
-
</one-of>
|
|
114
|
-
<tag> out.concept = "clue";</tag>
|
|
115
|
-
</item>
|
|
116
|
-
<item>
|
|
117
|
-
<item repeat="0-1"> have an</item>
|
|
118
|
-
<item repeat="0-1"> i have an</item>
|
|
119
|
-
<one-of>
|
|
120
|
-
<item> answer </item>
|
|
121
|
-
</one-of>
|
|
122
|
-
<tag> out.concept = "answer";</tag>
|
|
123
|
-
</item>
|
|
124
|
-
</one-of>
|
|
125
|
-
</rule>
|
|
126
|
-
</grammar>
|
|
127
|
-
GRXML
|
|
128
|
-
end
|
|
111
|
+
subject { Input::Grammar.new :value => grxml_doc, :content_type => 'application/grammar+grxml' }
|
|
129
112
|
|
|
130
|
-
|
|
113
|
+
its(:content_type) { should == 'application/grammar+grxml' }
|
|
114
|
+
|
|
115
|
+
let(:expected_message) { "<![CDATA[ #{grxml_doc} ]]>" }
|
|
131
116
|
|
|
132
117
|
it "should wrap GRXML in CDATA" do
|
|
133
118
|
subject.child.to_xml.should == expected_message.strip
|
|
134
119
|
end
|
|
120
|
+
|
|
121
|
+
its(:value) { should == grxml_doc }
|
|
122
|
+
|
|
123
|
+
describe "comparison" do
|
|
124
|
+
let(:grammar2) { Input::Grammar.new :value => '<grammar xmlns="http://www.w3.org/2001/06/grammar" version="1.0" xml:lang="en-US" mode="dtmf" root="digits"><rule id="digits"><one-of><item>0</item><item>1</item></one-of></rule></grammar>' }
|
|
125
|
+
let(:grammar3) { Input::Grammar.new :value => grxml_doc }
|
|
126
|
+
let(:grammar4) { Input::Grammar.new :value => grxml_doc(:speech) }
|
|
127
|
+
|
|
128
|
+
it { should == grammar2 }
|
|
129
|
+
it { should == grammar3 }
|
|
130
|
+
it { should_not == grammar4 }
|
|
131
|
+
end
|
|
135
132
|
end
|
|
136
133
|
end
|
|
137
134
|
|
|
@@ -7,6 +7,16 @@ module Punchblock
|
|
|
7
7
|
RayoNode.class_from_registration(:output, 'urn:xmpp:rayo:output:1').should == Output
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
describe 'default values' do
|
|
11
|
+
its(:interrupt_on) { should be nil }
|
|
12
|
+
its(:start_offset) { should be nil }
|
|
13
|
+
its(:start_paused) { should be false }
|
|
14
|
+
its(:repeat_interval) { should be nil }
|
|
15
|
+
its(:repeat_times) { should be nil }
|
|
16
|
+
its(:max_time) { should be nil }
|
|
17
|
+
its(:voice) { should be nil }
|
|
18
|
+
end
|
|
19
|
+
|
|
10
20
|
describe "when setting options in initializer" do
|
|
11
21
|
subject do
|
|
12
22
|
Output.new :interrupt_on => :speech,
|
|
@@ -63,18 +73,26 @@ module Punchblock
|
|
|
63
73
|
end
|
|
64
74
|
|
|
65
75
|
describe "for SSML" do
|
|
66
|
-
|
|
76
|
+
def ssml_doc(mode = :ordinal)
|
|
77
|
+
RubySpeech::SSML.draw do
|
|
78
|
+
say_as(:interpret_as => mode) { 100 }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
subject { Output.new :ssml => ssml_doc, :voice => 'kate' }
|
|
67
83
|
|
|
68
84
|
its(:voice) { should == 'kate' }
|
|
69
85
|
|
|
70
|
-
its(:ssml) { should ==
|
|
86
|
+
its(:ssml) { should == ssml_doc }
|
|
71
87
|
|
|
72
88
|
describe "comparison" do
|
|
73
|
-
let(:output2) { Output.new :ssml => '<
|
|
74
|
-
let(:output3) { Output.new :ssml =>
|
|
89
|
+
let(:output2) { Output.new :ssml => '<speak xmlns="http://www.w3.org/2001/10/synthesis" version="1.0" xml:lang="en-US"><say-as interpret-as="ordinal"/></speak>', :voice => 'kate' }
|
|
90
|
+
let(:output3) { Output.new :ssml => ssml_doc, :voice => 'kate' }
|
|
91
|
+
let(:output4) { Output.new :ssml => ssml_doc(:normal), :voice => 'kate' }
|
|
75
92
|
|
|
76
93
|
it { should == output2 }
|
|
77
|
-
it {
|
|
94
|
+
it { should == output3 }
|
|
95
|
+
it { should_not == output4 }
|
|
78
96
|
end
|
|
79
97
|
end
|
|
80
98
|
|