adhearsion 2.0.0.alpha3 → 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.
- data/CHANGELOG.md +13 -1
- data/README.markdown +1 -1
- data/features/cli_daemon.feature +2 -3
- data/features/step_definitions/cli_steps.rb +0 -1
- data/lib/adhearsion/call.rb +50 -32
- data/lib/adhearsion/call_controller.rb +53 -4
- data/lib/adhearsion/call_controller/dial.rb +26 -6
- data/lib/adhearsion/call_controller/input.rb +1 -1
- data/lib/adhearsion/call_controller/menu.rb +2 -2
- data/lib/adhearsion/call_controller/output.rb +11 -11
- data/lib/adhearsion/call_controller/utility.rb +3 -3
- data/lib/adhearsion/calls.rb +0 -4
- data/lib/adhearsion/cli_commands.rb +2 -3
- data/lib/adhearsion/console.rb +42 -14
- data/lib/adhearsion/foundation/thread_safety.rb +8 -2
- data/lib/adhearsion/generators/app/templates/Rakefile +1 -4
- data/lib/adhearsion/initializer.rb +12 -5
- data/lib/adhearsion/logging.rb +23 -4
- data/lib/adhearsion/process.rb +11 -2
- data/lib/adhearsion/punchblock_plugin.rb +8 -0
- data/lib/adhearsion/punchblock_plugin/initializer.rb +11 -4
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/dial_spec.rb +123 -85
- data/spec/adhearsion/call_controller/output_spec.rb +10 -0
- data/spec/adhearsion/call_controller_spec.rb +59 -52
- data/spec/adhearsion/call_spec.rb +47 -3
- data/spec/adhearsion/console_spec.rb +10 -1
- data/spec/adhearsion/initializer_spec.rb +5 -5
- data/spec/adhearsion/logging_spec.rb +17 -1
- data/spec/adhearsion/process_spec.rb +18 -0
- data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +20 -8
- data/spec/adhearsion/punchblock_plugin_spec.rb +42 -0
- data/spec/spec_helper.rb +5 -1
- metadata +64 -64
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# 2.0.0.beta1 - 2012-03-07
|
2
|
+
* Bugfix: #speak now correctly casts the argument to string if it is not SSML
|
3
|
+
* Bugfix: The console pauses controllers on a call while taking control
|
4
|
+
* Feature: Reopen logfiles on SIGHUP
|
5
|
+
* Feature: Toggle :trace logging on SIGALRM (useful for debugging a live process)
|
6
|
+
* Feature: It is now possible to execute a global component (using `Adhearsion::PunchblockPlugin.execute_component`)
|
7
|
+
* Feature: Now set XMPP JID resource to a concatenation of hostname and process ID for ID/debugging purposes
|
8
|
+
* Feature: CallController#dial now returns a DialStatus object indicating the status of the dial command
|
9
|
+
* Feature: Punchblock plugin can now configure the active media engine (mostly for use on Asterisk)
|
10
|
+
* Bugfix: Fix forcing Adhearsion to stop with enough SIGTERM or CTRL+C
|
11
|
+
|
1
12
|
# 2.0.0.alpha3 - 2012-02-21
|
2
13
|
* Feature: Add `ahn generate` command to allow invocation of generators
|
3
14
|
* Feature: Add simple generator for call controllers
|
@@ -10,11 +21,12 @@
|
|
10
21
|
* Feature: The console can take control of a call
|
11
22
|
* Bugfix: CallController#dial now blocks until all outbound calls complete
|
12
23
|
* Bugfix: Call commands timing out now raise a timeout exception in the caller, but do not crash the actor
|
24
|
+
* Feature: It is now possible to pause/resume call controllers
|
13
25
|
* Bugfix: CallController#dial now unblocks immediately if the original call ends
|
14
26
|
* Bugfix: CallController#dial now unblocks when the connected outbound call unjoins, rather than ending, incase post-processing on the outbound call is required
|
15
27
|
* Bugfix: CallController#dial now hangs up outbound legs when it unblocks
|
16
28
|
* Feature: CallController#dial now defaults the outbound caller ID to that of the controller's call
|
17
|
-
* Change: The command to take control of a call is now 'take' rather than 'use'.
|
29
|
+
* Change: The command to take control of a call is now 'take' rather than 'use'. 'take' called without a call ID present a list of currently running calls
|
18
30
|
|
19
31
|
# 2.0.0.alpha2 - 2012-01-30
|
20
32
|
* Change: Plugins no longer load dialplan/event/rpc/console methods using corresponding class methods
|
data/README.markdown
CHANGED
data/features/cli_daemon.feature
CHANGED
@@ -9,8 +9,7 @@ Feature: Adhearsion Ahn CLI (daemon)
|
|
9
9
|
When I run `ahn daemon path/somewhere`
|
10
10
|
And I cd to "path/somewhere"
|
11
11
|
And I terminate the process using the pid file "adhearsion.pid"
|
12
|
-
Then the
|
13
|
-
And the exit status should be 0
|
12
|
+
Then the exit status should be 0
|
14
13
|
|
15
14
|
Scenario: Command daemon with pid option
|
16
15
|
Given JRuby skip test
|
@@ -18,4 +17,4 @@ Feature: Adhearsion Ahn CLI (daemon)
|
|
18
17
|
When I run `ahn daemon path/somewhere --pid-file=ahn.pid`
|
19
18
|
And I cd to "path/somewhere"
|
20
19
|
And I terminate the process using the pid file "ahn.pid"
|
21
|
-
Then the
|
20
|
+
Then the exit status should be 0
|
data/lib/adhearsion/call.rb
CHANGED
@@ -1,33 +1,27 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
|
-
module Celluloid
|
4
|
-
module ClassMethods
|
5
|
-
def ===(other)
|
6
|
-
other.kind_of? self
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
class ActorProxy
|
11
|
-
def is_a?(klass)
|
12
|
-
Actor.call @mailbox, :is_a?, klass
|
13
|
-
end
|
14
|
-
|
15
|
-
def kind_of?(klass)
|
16
|
-
Actor.call @mailbox, :kind_of?, klass
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
3
|
module Adhearsion
|
22
4
|
##
|
23
5
|
# Encapsulates call-related data and behavior.
|
24
6
|
#
|
25
7
|
class Call
|
26
8
|
|
9
|
+
ExpiredError = Class.new Celluloid::DeadActorError
|
10
|
+
|
27
11
|
include Celluloid
|
28
12
|
include HasGuardedHandlers
|
29
13
|
|
30
|
-
|
14
|
+
def self.new(*args, &block)
|
15
|
+
super.tap do |proxy|
|
16
|
+
def proxy.method_missing(*args)
|
17
|
+
super
|
18
|
+
rescue Celluloid::DeadActorError => e
|
19
|
+
raise ExpiredError, "This call is expired and is no longer accessible"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :offer, :client, :end_reason, :commands, :variables, :controllers
|
31
25
|
|
32
26
|
delegate :[], :[]=, :to => :variables
|
33
27
|
delegate :to, :from, :to => :offer, :allow_nil => true
|
@@ -35,9 +29,10 @@ module Adhearsion
|
|
35
29
|
def initialize(offer = nil)
|
36
30
|
register_initial_handlers
|
37
31
|
|
38
|
-
@tags
|
39
|
-
@commands
|
40
|
-
@variables
|
32
|
+
@tags = []
|
33
|
+
@commands = CommandRegistry.new
|
34
|
+
@variables = {}
|
35
|
+
@controllers = []
|
41
36
|
|
42
37
|
self << offer if offer
|
43
38
|
end
|
@@ -77,7 +72,7 @@ module Adhearsion
|
|
77
72
|
|
78
73
|
alias << deliver_message
|
79
74
|
|
80
|
-
def register_initial_handlers
|
75
|
+
def register_initial_handlers # :nodoc:
|
81
76
|
register_event_handler Punchblock::Event::Offer do |offer|
|
82
77
|
@offer = offer
|
83
78
|
@client = offer.client
|
@@ -93,10 +88,14 @@ module Adhearsion
|
|
93
88
|
clear_from_active_calls
|
94
89
|
@end_reason = event.reason
|
95
90
|
commands.terminate
|
96
|
-
after(
|
91
|
+
after(after_end_hold_time) { current_actor.terminate! }
|
97
92
|
end
|
98
93
|
end
|
99
94
|
|
95
|
+
def after_end_hold_time # :nodoc:
|
96
|
+
30
|
97
|
+
end
|
98
|
+
|
100
99
|
def on_end(&block)
|
101
100
|
register_event_handler Punchblock::Event::End do |event|
|
102
101
|
block.call event
|
@@ -126,7 +125,7 @@ module Adhearsion
|
|
126
125
|
write_and_await_response Punchblock::Command::Hangup.new(:headers => headers)
|
127
126
|
end
|
128
127
|
|
129
|
-
def clear_from_active_calls
|
128
|
+
def clear_from_active_calls # :nodoc:
|
130
129
|
Adhearsion.active_calls.remove_inactive_call current_actor
|
131
130
|
end
|
132
131
|
|
@@ -180,17 +179,17 @@ module Adhearsion
|
|
180
179
|
end
|
181
180
|
|
182
181
|
def write_command(command)
|
183
|
-
abort Hangup.new unless active? || command.is_a?(Punchblock::Command::Hangup)
|
182
|
+
abort Hangup.new(@end_reason) unless active? || command.is_a?(Punchblock::Command::Hangup)
|
184
183
|
variables.merge! command.headers_hash if command.respond_to? :headers_hash
|
185
|
-
logger.
|
184
|
+
logger.debug "Executing command #{command.inspect}"
|
186
185
|
client.execute_command command, :call_id => id
|
187
186
|
end
|
188
187
|
|
189
|
-
def logger_id
|
188
|
+
def logger_id # :nodoc:
|
190
189
|
"#{self.class}: #{id}"
|
191
190
|
end
|
192
191
|
|
193
|
-
def logger
|
192
|
+
def logger # :nodoc:
|
194
193
|
super
|
195
194
|
end
|
196
195
|
|
@@ -198,8 +197,15 @@ module Adhearsion
|
|
198
197
|
[current_actor]
|
199
198
|
end
|
200
199
|
|
200
|
+
def inspect
|
201
|
+
attrs = [:offer, :end_reason, :commands, :variables, :controllers, :to, :from].map do |attr|
|
202
|
+
"#{attr}=#{send(attr).inspect}"
|
203
|
+
end
|
204
|
+
"#<#{self.class}:#{id} #{attrs.join ', '}>"
|
205
|
+
end
|
206
|
+
|
201
207
|
def execute_controller(controller, latch = nil)
|
202
|
-
|
208
|
+
Thread.new do
|
203
209
|
catching_standard_errors do
|
204
210
|
begin
|
205
211
|
CallController.exec controller
|
@@ -208,10 +214,22 @@ module Adhearsion
|
|
208
214
|
end
|
209
215
|
latch.countdown! if latch
|
210
216
|
end
|
211
|
-
end
|
217
|
+
end.tap { |t| Adhearsion::Process.important_threads << t }
|
218
|
+
end
|
219
|
+
|
220
|
+
def register_controller(controller)
|
221
|
+
@controllers << controller
|
222
|
+
end
|
223
|
+
|
224
|
+
def pause_controllers
|
225
|
+
controllers.each &:pause!
|
226
|
+
end
|
227
|
+
|
228
|
+
def resume_controllers
|
229
|
+
controllers.each &:resume!
|
212
230
|
end
|
213
231
|
|
214
|
-
class CommandRegistry < ThreadSafeArray
|
232
|
+
class CommandRegistry < ThreadSafeArray # :nodoc:
|
215
233
|
def terminate
|
216
234
|
hangup = Hangup.new
|
217
235
|
each { |command| command.response = hangup if command.requested? }
|
@@ -54,13 +54,13 @@ module Adhearsion
|
|
54
54
|
|
55
55
|
delegate :[], :[]=, :to => :@metadata
|
56
56
|
delegate :variables, :logger, :to => :call
|
57
|
-
delegate :write_and_await_response, :answer, :reject, :mute, :unmute, :join, :to => :call
|
58
57
|
|
59
58
|
def initialize(call, metadata = nil, &block)
|
60
59
|
@call, @metadata, @block = call, metadata || {}, block
|
61
60
|
end
|
62
61
|
|
63
|
-
def execute!(*options)
|
62
|
+
def execute!(*options) # :nodoc:
|
63
|
+
call.register_controller! self
|
64
64
|
execute_callbacks :before_call
|
65
65
|
run
|
66
66
|
rescue Hangup
|
@@ -84,7 +84,7 @@ module Adhearsion
|
|
84
84
|
throw :pass_controller, controller_class.new(call, metadata)
|
85
85
|
end
|
86
86
|
|
87
|
-
def execute_callbacks(type)
|
87
|
+
def execute_callbacks(type) # :nodoc:
|
88
88
|
self.class.callbacks[type].each do |callback|
|
89
89
|
catching_standard_errors do
|
90
90
|
instance_exec &callback
|
@@ -92,15 +92,21 @@ module Adhearsion
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
-
def after_call
|
95
|
+
def after_call # :nodoc:
|
96
96
|
@after_call ||= execute_callbacks :after_call
|
97
97
|
end
|
98
98
|
|
99
99
|
def hangup(headers = nil)
|
100
|
+
block_until_resumed
|
100
101
|
hangup_response = call.hangup headers
|
101
102
|
after_call unless hangup_response == false
|
102
103
|
end
|
103
104
|
|
105
|
+
def write_and_await_response(command)
|
106
|
+
block_until_resumed
|
107
|
+
call.write_and_await_response command
|
108
|
+
end
|
109
|
+
|
104
110
|
def execute_component_and_await_completion(component)
|
105
111
|
write_and_await_response component
|
106
112
|
|
@@ -110,5 +116,48 @@ module Adhearsion
|
|
110
116
|
raise StandardError, complete_event.reason.details if complete_event.reason.is_a? Punchblock::Event::Complete::Error
|
111
117
|
component
|
112
118
|
end
|
119
|
+
|
120
|
+
def answer(*args)
|
121
|
+
block_until_resumed
|
122
|
+
call.answer *args
|
123
|
+
end
|
124
|
+
|
125
|
+
def reject(*args)
|
126
|
+
block_until_resumed
|
127
|
+
call.reject *args
|
128
|
+
end
|
129
|
+
|
130
|
+
def mute(*args)
|
131
|
+
block_until_resumed
|
132
|
+
call.mute *args
|
133
|
+
end
|
134
|
+
|
135
|
+
def unmute(*args)
|
136
|
+
block_until_resumed
|
137
|
+
call.unmute *args
|
138
|
+
end
|
139
|
+
|
140
|
+
def join(*args)
|
141
|
+
block_until_resumed
|
142
|
+
call.join *args
|
143
|
+
end
|
144
|
+
|
145
|
+
def block_until_resumed # :nodoc:
|
146
|
+
@pause_latch && @pause_latch.wait
|
147
|
+
end
|
148
|
+
|
149
|
+
def pause! # :nodoc:
|
150
|
+
@pause_latch = CountDownLatch.new 1
|
151
|
+
end
|
152
|
+
|
153
|
+
def resume! # :nodoc:
|
154
|
+
return unless @pause_latch
|
155
|
+
@pause_latch.countdown!
|
156
|
+
@pause_latch = nil
|
157
|
+
end
|
158
|
+
|
159
|
+
def inspect
|
160
|
+
"#<#{self.class} call=#{call.id}, metadata=#{metadata.inspect}>"
|
161
|
+
end
|
113
162
|
end#class
|
114
163
|
end
|
@@ -32,6 +32,7 @@ module Adhearsion
|
|
32
32
|
#
|
33
33
|
def dial(to, options = {}, latch = nil)
|
34
34
|
targets = Array(to)
|
35
|
+
status = DialStatus.new
|
35
36
|
|
36
37
|
latch ||= CountDownLatch.new targets.size
|
37
38
|
|
@@ -43,7 +44,7 @@ module Adhearsion
|
|
43
44
|
options[:from] ||= call.from
|
44
45
|
|
45
46
|
calls = targets.map do |target|
|
46
|
-
new_call = OutboundCall.new
|
47
|
+
new_call = OutboundCall.new
|
47
48
|
|
48
49
|
new_call.on_answer do |event|
|
49
50
|
calls.each do |call_to_hangup, target|
|
@@ -57,17 +58,18 @@ module Adhearsion
|
|
57
58
|
end
|
58
59
|
|
59
60
|
new_call.register_event_handler Punchblock::Event::Unjoined, :other_call_id => call.id do |event|
|
60
|
-
new_call[
|
61
|
+
new_call["dial_countdown_#{call.id}"] = true
|
61
62
|
latch.countdown!
|
62
63
|
throw :pass
|
63
64
|
end
|
64
65
|
|
65
66
|
logger.debug "Joining call #{new_call.id} to #{call.id} due to a #dial"
|
66
67
|
new_call.join call
|
68
|
+
status.answer!
|
67
69
|
end
|
68
70
|
|
69
71
|
new_call.on_end do |event|
|
70
|
-
latch.countdown! unless new_call[
|
72
|
+
latch.countdown! unless new_call["dial_countdown_#{call.id}"]
|
71
73
|
end
|
72
74
|
|
73
75
|
[new_call, target]
|
@@ -78,7 +80,10 @@ module Adhearsion
|
|
78
80
|
call
|
79
81
|
end
|
80
82
|
|
81
|
-
|
83
|
+
status.calls = calls
|
84
|
+
|
85
|
+
no_timeout = latch.wait options[:timeout]
|
86
|
+
status.timeout! unless no_timeout
|
82
87
|
|
83
88
|
logger.debug "#dial finished. Hanging up #{calls.size} outbound calls #{calls.inspect}."
|
84
89
|
calls.each do |outbound_call|
|
@@ -90,9 +95,24 @@ module Adhearsion
|
|
90
95
|
end
|
91
96
|
end
|
92
97
|
|
93
|
-
|
98
|
+
status
|
99
|
+
end
|
100
|
+
|
101
|
+
class DialStatus
|
102
|
+
attr_accessor :calls
|
103
|
+
attr_reader :result
|
104
|
+
|
105
|
+
def initialize
|
106
|
+
@result = :no_answer
|
107
|
+
end
|
108
|
+
|
109
|
+
def answer!
|
110
|
+
@result = :answer
|
111
|
+
end
|
94
112
|
|
95
|
-
|
113
|
+
def timeout!
|
114
|
+
@result = :timeout
|
115
|
+
end
|
96
116
|
end
|
97
117
|
|
98
118
|
end#module Dial
|
@@ -7,7 +7,7 @@ module Adhearsion
|
|
7
7
|
# @param [Integer] the timeout to wait before returning, in seconds. nil or -1 mean no timeout.
|
8
8
|
# @return [String|nil] the pressed key, or nil if timeout was reached.
|
9
9
|
#
|
10
|
-
def wait_for_digit(timeout = 1)
|
10
|
+
def wait_for_digit(timeout = 1) # :nodoc:
|
11
11
|
timeout = nil if timeout == -1
|
12
12
|
timeout *= 1_000 if timeout
|
13
13
|
input_component = execute_component_and_await_completion ::Punchblock::Component::Input.new :mode => :dtmf,
|
@@ -103,7 +103,7 @@ module Adhearsion
|
|
103
103
|
return :done
|
104
104
|
end
|
105
105
|
|
106
|
-
def play_sound_files_for_menu(menu_instance, sound_files)
|
106
|
+
def play_sound_files_for_menu(menu_instance, sound_files) # :nodoc:
|
107
107
|
digit = nil
|
108
108
|
if sound_files.any? && menu_instance.digit_buffer_empty?
|
109
109
|
digit = interruptible_play *sound_files
|
@@ -111,7 +111,7 @@ module Adhearsion
|
|
111
111
|
digit || wait_for_digit(menu_instance.timeout)
|
112
112
|
end
|
113
113
|
|
114
|
-
def jump_to(match_object, overrides = nil)
|
114
|
+
def jump_to(match_object, overrides = nil) # :nodoc:
|
115
115
|
if match_object.block
|
116
116
|
instance_exec overrides[:extension], &match_object.block
|
117
117
|
else
|
@@ -2,7 +2,7 @@ module Adhearsion
|
|
2
2
|
class CallController
|
3
3
|
module Output
|
4
4
|
def speak(text, options = {})
|
5
|
-
play_ssml(text, options) || output(:text, text, options)
|
5
|
+
play_ssml(text, options) || output(:text, text.to_s, options)
|
6
6
|
end
|
7
7
|
|
8
8
|
#
|
@@ -110,18 +110,18 @@ module Adhearsion
|
|
110
110
|
play_ssml ssml_for_audio(argument, options)
|
111
111
|
end
|
112
112
|
|
113
|
-
def play_ssml(ssml, options = {})
|
113
|
+
def play_ssml(ssml, options = {}) # :nodoc:
|
114
114
|
if [RubySpeech::SSML::Speak, Nokogiri::XML::Document].include? ssml.class
|
115
115
|
output :ssml, ssml.to_s, options
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
-
def output(type, content, options = {})
|
119
|
+
def output(type, content, options = {}) # :nodoc:
|
120
120
|
options.merge! type => content
|
121
121
|
execute_component_and_await_completion ::Punchblock::Component::Output.new(options)
|
122
122
|
end
|
123
123
|
|
124
|
-
def output!(type, content, options = {})
|
124
|
+
def output!(type, content, options = {}) # :nodoc:
|
125
125
|
options.merge! type => content
|
126
126
|
execute_component_and_await_completion ::Punchblock::Component::Output.new(options)
|
127
127
|
end
|
@@ -170,7 +170,7 @@ module Adhearsion
|
|
170
170
|
result
|
171
171
|
end
|
172
172
|
|
173
|
-
def detect_type(output)
|
173
|
+
def detect_type(output) # :nodoc:
|
174
174
|
result = nil
|
175
175
|
result = :time if [Date, Time, DateTime].include? output.class
|
176
176
|
result = :numeric if output.kind_of?(Numeric) || output =~ /^\d+$/
|
@@ -178,7 +178,7 @@ module Adhearsion
|
|
178
178
|
result ||= :text
|
179
179
|
end
|
180
180
|
|
181
|
-
def play_ssml_for(*args)
|
181
|
+
def play_ssml_for(*args) # :nodoc:
|
182
182
|
play_ssml ssml_for(args)
|
183
183
|
end
|
184
184
|
|
@@ -189,7 +189,7 @@ module Adhearsion
|
|
189
189
|
# @param [String|Hash|RubySpeech::SSML::Speak] the argument with options as accepted by the play_ methods, or an SSML document
|
190
190
|
# @return [RubySpeech::SSML::Speak] an SSML document
|
191
191
|
#
|
192
|
-
def ssml_for(*args)
|
192
|
+
def ssml_for(*args) # :nodoc:
|
193
193
|
return args[0] if args.size == 1 && args[0].is_a?(RubySpeech::SSML::Speak)
|
194
194
|
argument, options = args.flatten
|
195
195
|
options ||= {}
|
@@ -197,11 +197,11 @@ module Adhearsion
|
|
197
197
|
send "ssml_for_#{type}", argument, options
|
198
198
|
end
|
199
199
|
|
200
|
-
def ssml_for_text(argument, options = {})
|
200
|
+
def ssml_for_text(argument, options = {}) # :nodoc:
|
201
201
|
RubySpeech::SSML.draw { argument }
|
202
202
|
end
|
203
203
|
|
204
|
-
def ssml_for_time(argument, options = {})
|
204
|
+
def ssml_for_time(argument, options = {}) # :nodoc:
|
205
205
|
interpretation = case argument
|
206
206
|
when Date then 'date'
|
207
207
|
when Time then 'time'
|
@@ -217,13 +217,13 @@ module Adhearsion
|
|
217
217
|
end
|
218
218
|
end
|
219
219
|
|
220
|
-
def ssml_for_numeric(argument, options = {})
|
220
|
+
def ssml_for_numeric(argument, options = {}) # :nodoc:
|
221
221
|
RubySpeech::SSML.draw do
|
222
222
|
say_as(:interpret_as => 'cardinal') { argument.to_s }
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
|
-
def ssml_for_audio(argument, options = {})
|
226
|
+
def ssml_for_audio(argument, options = {}) # :nodoc:
|
227
227
|
fallback = (options || {}).delete :fallback
|
228
228
|
RubySpeech::SSML.draw do
|
229
229
|
audio(:src => argument) { fallback }
|