adhearsion 2.0.0.alpha3 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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 }
|