adzap-voice_form 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/README.markdown +75 -6
- data/Rakefile +2 -2
- data/examples/my_component.rb +10 -9
- data/lib/voice_form.rb +0 -8
- data/lib/voice_form/form.rb +2 -15
- data/lib/voice_form/form_field.rb +2 -1
- data/lib/voice_form/form_methods.rb +8 -8
- data/spec/form_field_spec.rb +1 -3
- data/spec/form_spec.rb +3 -3
- metadata +6 -4
data/History.txt
CHANGED
data/README.markdown
CHANGED
@@ -33,7 +33,6 @@ command to play your sound files.
|
|
33
33
|
|
34
34
|
class MyComponent
|
35
35
|
include VoiceForm
|
36
|
-
add_call_context :as => :call_context
|
37
36
|
|
38
37
|
voice_form do
|
39
38
|
|
@@ -53,6 +52,10 @@ command to play your sound files.
|
|
53
52
|
@age.to_i <= @max_age
|
54
53
|
end
|
55
54
|
|
55
|
+
confirm(:accept => 1, :reject => 2, :timeout => 3, :attempts => 3) do
|
56
|
+
"You entered #{@age}. Press 1 to continue or 2 to try again."
|
57
|
+
end
|
58
|
+
|
56
59
|
invalid do
|
57
60
|
call_context.speak "Your age must be less than #{@max_age}. Try again."
|
58
61
|
end
|
@@ -72,8 +75,7 @@ command to play your sound files.
|
|
72
75
|
In your dialplan:
|
73
76
|
|
74
77
|
general {
|
75
|
-
|
76
|
-
my_component.start_voice_form
|
78
|
+
MyComponent.new.start_voice_form(self)
|
77
79
|
}
|
78
80
|
|
79
81
|
That covers most of the functionality, and hopefully it makes sense pretty much straight
|
@@ -82,11 +84,14 @@ away.
|
|
82
84
|
You don't have to start the form from the dialplan, but it makes it simple. You could start it from
|
83
85
|
within a component method.
|
84
86
|
|
85
|
-
All blocks (setup, validate,
|
86
|
-
component methods and instance variables in them and they will work.
|
87
|
+
All callback blocks (setup, validate, timeout etc.) are evaluated in the component scope so you can use
|
88
|
+
component methods and instance variables in them and they will work. You don't have to define any
|
89
|
+
callbacks if the field is straight forward and only depends on its length.
|
87
90
|
|
88
91
|
For a more complete example see the examples folder.
|
89
92
|
|
93
|
+
## Commands
|
94
|
+
|
90
95
|
### voice_form
|
91
96
|
|
92
97
|
The flow of the form works like a stack. So each field and do_block are executed in order until the
|
@@ -137,8 +142,72 @@ There can only be one `prompt` but you can have multiple `reprompt`s. When you a
|
|
137
142
|
what the prompt is if there is input the first time or the input is invalid.
|
138
143
|
|
139
144
|
|
140
|
-
|
145
|
+
### Callbacks
|
146
|
+
|
147
|
+
The available callbacks that can be defined for a field are as follows
|
148
|
+
|
149
|
+
- setup
|
150
|
+
- timeout
|
151
|
+
- validate
|
152
|
+
- invalid
|
153
|
+
- confirm
|
154
|
+
- success
|
155
|
+
- failure
|
156
|
+
|
157
|
+
Each of them takes a block which is executed as a specific point in the process of getting form input.
|
158
|
+
All of them are optional. The block for a callback is evaluated in the scope of the component so any
|
159
|
+
instance variables and component methods are available to use including the call context.
|
160
|
+
|
161
|
+
The details of each callback are as follows
|
162
|
+
|
163
|
+
### setup
|
164
|
+
|
165
|
+
This is run once only for a field if defined before any prompts
|
166
|
+
|
167
|
+
### timeout
|
168
|
+
|
169
|
+
This is run if no input is received.
|
170
|
+
|
171
|
+
### validate
|
172
|
+
|
173
|
+
This is run after input of a valid length. The validate block is where you put validation logic of the
|
174
|
+
value just input by the user. The block should return `true` if the value is valid or `false` otherwise.
|
175
|
+
If the validate callback returns false then the invalid callback will be called next.
|
176
|
+
|
177
|
+
### invalid
|
178
|
+
|
179
|
+
The invalid callback is called if the input value is not of a valid length or the validate block returns
|
180
|
+
false.
|
181
|
+
|
182
|
+
### confirm
|
183
|
+
|
184
|
+
The confirm callback is called after the input has been validated. The confirm callback is a little different
|
185
|
+
from the others. Idea is that you return either an array or string of the audio files or TTS text, respectively,
|
186
|
+
you want to play as the prompt for confirming the value entered. The confirm block also takes a few options:
|
187
|
+
|
188
|
+
- :accept - the number to press to accept the field value entered. Default is 1.
|
189
|
+
- :reject - the number to press the reject the field value entered and try again. Default is 2.
|
190
|
+
- :attempts - the number of attempts to try to get a confirmation response. Default is 3
|
191
|
+
- :timeout - the number of seconds to wait for input after the confirmation response. Default is 3.
|
192
|
+
|
193
|
+
The value returned from the block should form the complete list of audio files or TTS text to prompt the user
|
194
|
+
including the values to accept of reject the value.
|
195
|
+
|
196
|
+
For example, in a field called my_field:
|
197
|
+
|
198
|
+
confirm(:accept => 1, :reject => 2, :attempts => 3) do
|
199
|
+
['you-entered', @my_field.scan(/\d/), 'is-this-correct', 'press-1-accept-2-try-again'].flatten
|
200
|
+
end
|
201
|
+
|
202
|
+
The above will `play` the array of audo files as the prompt for confirmation.
|
203
|
+
|
204
|
+
confirm(:accept => 1, :reject => 2, :attempts => 3) do
|
205
|
+
"You entered #{@my_field}. Is this correct? Press 1 to accept or 2 try again."
|
206
|
+
end
|
207
|
+
|
208
|
+
The above will `speak` the string as the prompt for confirmation.
|
141
209
|
|
210
|
+
If no valid input is entered for the confirmation
|
142
211
|
|
143
212
|
TODO: More docs
|
144
213
|
|
data/Rakefile
CHANGED
@@ -5,11 +5,11 @@ require 'date'
|
|
5
5
|
require 'spec/rake/spectask'
|
6
6
|
|
7
7
|
GEM = "voice_form"
|
8
|
-
GEM_VERSION = "0.
|
8
|
+
GEM_VERSION = "0.2.0"
|
9
9
|
AUTHOR = "Adam Meehan"
|
10
10
|
EMAIL = "adam.meehan@gmail.com"
|
11
11
|
HOMEPAGE = "http://github.com/adzap/voice_form"
|
12
|
-
SUMMARY = "A
|
12
|
+
SUMMARY = "A plugin for Adhearsion to create forms in the style of the VoiceXML form element."
|
13
13
|
|
14
14
|
spec = Gem::Specification.new do |s|
|
15
15
|
s.name = GEM
|
data/examples/my_component.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
class MyComponent
|
2
2
|
include VoiceForm
|
3
|
-
|
3
|
+
|
4
|
+
delegate :play, :speak, :to => :call_context
|
4
5
|
|
5
6
|
voice_form do
|
6
7
|
field(:age, :max_length => 3, :attempts => 3) do
|
@@ -12,20 +13,20 @@ class MyComponent
|
|
12
13
|
validate { @age.to_i < @max_age }
|
13
14
|
|
14
15
|
invalid do
|
15
|
-
|
16
|
+
speak "Your age must be less than #{@max_age}. Try again."
|
16
17
|
end
|
17
18
|
|
18
19
|
success do
|
19
|
-
|
20
|
+
speak "You are #{@age} years old."
|
20
21
|
end
|
21
22
|
|
22
23
|
failure do
|
23
|
-
|
24
|
+
speak "You could not enter your age. Thats a bad sign."
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
28
|
do_block do
|
28
|
-
|
29
|
+
speak "Get ready for the next question."
|
29
30
|
end
|
30
31
|
|
31
32
|
field(:postcode, :length => 4, :attempts => 5) do
|
@@ -35,19 +36,19 @@ class MyComponent
|
|
35
36
|
|
36
37
|
invalid do
|
37
38
|
if @postcode.size < 4
|
38
|
-
|
39
|
+
speak "Your postcode must 4 digits."
|
39
40
|
else
|
40
|
-
|
41
|
+
speak "Your postcode cannot start with a 0."
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
44
45
|
success do
|
45
|
-
|
46
|
+
speak "Your postcode is #{@postcode.scan(/\d/).join(', ')}."
|
46
47
|
end
|
47
48
|
|
48
49
|
failure do
|
49
50
|
if @age.empty?
|
50
|
-
|
51
|
+
speak "Lets start over shall we."
|
51
52
|
form.restart
|
52
53
|
end
|
53
54
|
end
|
data/lib/voice_form.rb
CHANGED
@@ -1,11 +1,3 @@
|
|
1
1
|
require 'voice_form/form_methods'
|
2
2
|
require 'voice_form/form'
|
3
3
|
require 'voice_form/form_field'
|
4
|
-
|
5
|
-
Adhearsion::Components::Behavior.module_eval do
|
6
|
-
include VoiceForm::FormMethods
|
7
|
-
end
|
8
|
-
|
9
|
-
Adhearsion::Components::Behavior::ClassMethods.module_eval do
|
10
|
-
include VoiceForm::MacroMethods
|
11
|
-
end
|
data/lib/voice_form/form.rb
CHANGED
@@ -6,22 +6,18 @@ module VoiceForm
|
|
6
6
|
attr_accessor :form_stack
|
7
7
|
attr_reader :current_field
|
8
8
|
|
9
|
-
def initialize(options={})
|
9
|
+
def initialize(options={}, &block)
|
10
10
|
@options = options
|
11
|
-
|
12
11
|
@form_stack = []
|
13
12
|
@stack_index = 0
|
13
|
+
self.instance_eval(&block)
|
14
14
|
end
|
15
15
|
|
16
16
|
def run(component)
|
17
17
|
@component = component
|
18
18
|
|
19
|
-
alias_call_context
|
20
|
-
|
21
19
|
add_field_accessors
|
22
|
-
|
23
20
|
run_setup
|
24
|
-
|
25
21
|
run_form_stack
|
26
22
|
end
|
27
23
|
|
@@ -92,15 +88,6 @@ module VoiceForm
|
|
92
88
|
slot.is_a?(VoiceForm::FormField)
|
93
89
|
end
|
94
90
|
|
95
|
-
def alias_call_context
|
96
|
-
# hack to avoid setting the call_context in each field for different context name
|
97
|
-
if context_name = @options[:call_context] && !@component.respond_to?(:call_context)
|
98
|
-
@component.class_eval do
|
99
|
-
alias_method :call_context, context_name
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
91
|
end
|
105
92
|
|
106
93
|
end
|
@@ -4,11 +4,12 @@ module VoiceForm
|
|
4
4
|
attr_reader :name
|
5
5
|
attr_accessor :prompts
|
6
6
|
|
7
|
-
def initialize(name, options, component)
|
7
|
+
def initialize(name, options, component, &block)
|
8
8
|
@name, @options, @component = name, options, component
|
9
9
|
@options.reverse_merge!(:attempts => 5, :call_context => 'call_context')
|
10
10
|
@callbacks = {}
|
11
11
|
@prompts = []
|
12
|
+
self.instance_eval(&block)
|
12
13
|
end
|
13
14
|
|
14
15
|
def prompt(options)
|
@@ -16,7 +16,7 @@ module VoiceForm
|
|
16
16
|
include InstanceMethods
|
17
17
|
|
18
18
|
cattr_accessor :voice_form_options
|
19
|
-
attr_accessor :form
|
19
|
+
attr_accessor :form, :call_context
|
20
20
|
end
|
21
21
|
|
22
22
|
self.voice_form_options = [options, block]
|
@@ -26,10 +26,11 @@ module VoiceForm
|
|
26
26
|
|
27
27
|
module InstanceMethods
|
28
28
|
|
29
|
-
def start_voice_form
|
29
|
+
def start_voice_form(call)
|
30
30
|
raise "No voice form defined" unless self.voice_form_options
|
31
|
-
|
32
|
-
|
31
|
+
options, block = *self.class.voice_form_options
|
32
|
+
@call_context = call
|
33
|
+
self.form = VoiceForm::Form.new(options, &block)
|
33
34
|
self.form.run(self)
|
34
35
|
end
|
35
36
|
|
@@ -39,12 +40,11 @@ module VoiceForm
|
|
39
40
|
|
40
41
|
# Can be used in a form or stand-alone in a component method
|
41
42
|
def field(field_name, options={}, &block)
|
42
|
-
raise
|
43
|
+
raise "A field requires a block" unless block_given?
|
43
44
|
|
44
|
-
form_field = VoiceForm::FormField.new(field_name, options, self)
|
45
|
+
form_field = VoiceForm::FormField.new(field_name, options, self, &block)
|
45
46
|
|
46
|
-
form_field.
|
47
|
-
raise 'At least one prompt is required' if form_field.prompts.empty?
|
47
|
+
raise 'A field requires a prompt to be defined' if form_field.prompts.empty?
|
48
48
|
|
49
49
|
if self.class == VoiceForm::Form
|
50
50
|
self.form_stack << form_field
|
data/spec/form_field_spec.rb
CHANGED
@@ -267,9 +267,7 @@ describe VoiceForm::FormField do
|
|
267
267
|
|
268
268
|
def form_field(field, options={}, &block)
|
269
269
|
self.class.class_eval { attr_accessor field }
|
270
|
-
item = VoiceForm::FormField.new(field, {:attempts => 3}.merge(options), self )
|
271
|
-
|
272
|
-
item.instance_eval(&block)
|
270
|
+
item = VoiceForm::FormField.new(field, {:attempts => 3}.merge(options), self, &block)
|
273
271
|
item
|
274
272
|
end
|
275
273
|
end
|
data/spec/form_spec.rb
CHANGED
@@ -16,7 +16,7 @@ describe VoiceForm::Form do
|
|
16
16
|
|
17
17
|
self.class.voice_form &call_me
|
18
18
|
|
19
|
-
start_voice_form
|
19
|
+
start_voice_form(@call_context)
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should call setup block" do
|
@@ -203,8 +203,8 @@ describe VoiceForm::Form do
|
|
203
203
|
|
204
204
|
def new_voice_form
|
205
205
|
self.class.voice_form { }
|
206
|
-
|
207
|
-
self.form.
|
206
|
+
options, block = *self.class.voice_form_options
|
207
|
+
self.form = VoiceForm::Form.new(options, &block)
|
208
208
|
end
|
209
209
|
|
210
210
|
def run_form
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adzap-voice_form
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Meehan
|
@@ -9,11 +9,11 @@ autorequire: voice_form
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-01-16 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description: A
|
16
|
+
description: A plugin for Adhearsion to create forms in the style of the VoiceXML form element.
|
17
17
|
email: adam.meehan@gmail.com
|
18
18
|
executables: []
|
19
19
|
|
@@ -27,10 +27,12 @@ files:
|
|
27
27
|
- Rakefile
|
28
28
|
- lib/voice_form.rb
|
29
29
|
- lib/voice_form
|
30
|
+
- lib/voice_form/simple_menu.rb
|
30
31
|
- lib/voice_form/form.rb
|
31
32
|
- lib/voice_form/form_field.rb
|
32
33
|
- lib/voice_form/form_methods.rb
|
33
34
|
- spec/form_field_spec.rb
|
35
|
+
- spec/simple_menu_spec.rb
|
34
36
|
- spec/spec_helper.rb
|
35
37
|
- spec/form_spec.rb
|
36
38
|
- examples/my_component.rb
|
@@ -60,6 +62,6 @@ rubyforge_project:
|
|
60
62
|
rubygems_version: 1.2.0
|
61
63
|
signing_key:
|
62
64
|
specification_version: 2
|
63
|
-
summary: A
|
65
|
+
summary: A plugin for Adhearsion to create forms in the style of the VoiceXML form element.
|
64
66
|
test_files: []
|
65
67
|
|