voice_form 0.3.0

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.
@@ -0,0 +1,24 @@
1
+ == 0.3.0 2009-03-22
2
+ * Lots of refactoring for prompts. Prompts can now be a symbol for a component instance method.
3
+ * Added prompt bargein option to set whether the caller can interrupt a prompt
4
+ * Added Simon game example in the examples folder
5
+ * Added as_digits helper
6
+ * Added component start_voice_form class method to start the form without creating component instance first.
7
+
8
+ == 0.2.0 2009-01-16
9
+ * Updated to work with Adhearsion 0.8.0 release. Examples updated to match new usage.
10
+
11
+ == 0.1.1 2008-11-12
12
+ * refactored to auto include voice_form method in components, no more 'include VoiceForm'
13
+
14
+ == 0.1.0 2008-11-11
15
+ * Gemified using newgem
16
+
17
+ == 2008-11-11
18
+ * Added form field confirm callback to enable confirmation of input
19
+
20
+ == 2008-11-04
21
+ * Changed form.exit_form to form.exit
22
+
23
+ == 2008-11-03
24
+ * First commit
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Adam Meehan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,251 @@
1
+ # VoiceForm
2
+
3
+ A plugin for Adhearsion to add form functionality and flow, similar to VoiceXML style forms.
4
+
5
+ By Adam Meehan (adam.meehan@gmail.com, [http://duckpunching.com/](http://duckpunching.com/))
6
+
7
+ Released under the MIT license.
8
+
9
+ ## Introduction
10
+
11
+ The plugin adds form features to Adhearsion components to quickly and semantically setup data
12
+ input for your voice application. You define a form and form fields in which to collect data and
13
+ setup callbacks to instruct the caller, give feedback, confirm input and validate input.
14
+
15
+
16
+ ## Install
17
+
18
+ sudo gem install adzap-voice_form --source=http://gems.github.com/
19
+
20
+ At the bottom your projects startup.rb file put
21
+
22
+ require 'voice_form'
23
+
24
+ ## Example
25
+
26
+ Here is the Adhearsion example Simon game redone using voice_form:
27
+
28
+ class SimonGame
29
+ include VoiceForm
30
+
31
+ voice_form do
32
+ setup do
33
+ @number = ''
34
+ end
35
+
36
+ field(:attempt, :attempts => 1) do
37
+ prompt :play => :current_number, :bargein => false, :timeout => 2
38
+
39
+ setup do
40
+ @number << random_number
41
+ end
42
+
43
+ validate do
44
+ @attempt == @number
45
+ end
46
+
47
+ success do
48
+ call.play 'good'
49
+ form.restart
50
+ end
51
+
52
+ failure do
53
+ call.play %W[#{@number.length-1} times wrong-try-again-smarty]
54
+ @number = ''
55
+ form.restart
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ def random_number
62
+ rand(10).to_s
63
+ end
64
+
65
+ def current_number
66
+ as_digits(@number)
67
+ end
68
+ end
69
+
70
+ That covers most of the functionality, and hopefully it makes sense pretty much straight
71
+ away.
72
+
73
+ To start the form, in your dialplan:
74
+
75
+ For Adhearsion 0.7.999
76
+
77
+ general {
78
+ simon_game = new_simon_game
79
+ simon_game.start_voice_form(self)
80
+ }
81
+
82
+ For Adhearsion 0.8.x
83
+
84
+ general {
85
+ SimonGame.start_voice_form(self)
86
+ }
87
+
88
+ You don't have to start the form from the dialplan, but it makes it simple. You could start it from
89
+ within a component method.
90
+
91
+ All callback blocks (setup, validate, timeout etc.) are evaluated in the component scope so you can use
92
+ component methods and instance variables in them and they will work. You don't have to define any
93
+ callbacks if the field is straight forward and only depends on its length.
94
+
95
+ For a more complete example see the examples folder.
96
+
97
+ ## Commands
98
+
99
+ ### voice_form
100
+
101
+ The flow of the form works like a stack. So each field and do_block are executed in order until the
102
+ end of the form is reached. You can jump around the stack by using `form.goto :field_name` which
103
+ will move the *stack pointer* to the field after the current field is completed and move forward
104
+ through the form stack from that point, regardless whether a field has already been completed.
105
+
106
+ You can also use `form.restart` to start the form over from the beginning.
107
+
108
+ The form setup block is only run once and is not executed again, even with a `form.restart`.
109
+
110
+
111
+ ### field
112
+
113
+ This defines the field with the name given to collect on the form. The field method can be used
114
+ in a `voice_form` or on its own inside a component method.
115
+
116
+ The options available are:
117
+
118
+ - :length - the number of digits to accept
119
+ - :min_length - minimum number of digits to accept
120
+ - :max_length - maximum number of digits to accept
121
+ - :attempts - number of tries to get a valid input
122
+ - :call - the method name for the call context if other than 'call'. Used for standalone fields not in a form.
123
+
124
+ All fields defined get an accessor method defined of the same name in the component class.
125
+ This means you can access its value using the instance variable or the accessor method inside any of
126
+ the field callbacks and in other fields on a form.
127
+
128
+ The `prompt` and `reprompt` methods are a wrapper around the input command. And as such is always
129
+ interruptable or you can _bargein_ when you want to starting keying numbers. You pass in a
130
+ hash of options to control the prompt such as:
131
+
132
+ - :play - play one or more sound files
133
+ - :speak - play TTS text (needs my Adhearsion hack for speak in input command)
134
+ - :timeout - number of seconds to wait for input. Default is 5.
135
+ - :repeats - number of attempts to use this prompt until the next one is used
136
+ - :bargein - whether to allow caller to interrupt prompt. Default is true.
137
+
138
+ The length expected for the input is taken from the options passed to the `field` method.
139
+
140
+ You can only use one of :play or :speak.
141
+
142
+ There can only be one `prompt` but you can have multiple `reprompt`s. When you add a reprompt it changes
143
+ what the prompt is if there is input the first time or the input is invalid.
144
+
145
+
146
+ ### Callbacks
147
+
148
+ The available callbacks that can be defined for a field are as follows
149
+
150
+ - setup
151
+ - timeout
152
+ - validate
153
+ - invalid
154
+ - confirm
155
+ - success
156
+ - failure
157
+
158
+ Each of them takes a block which is executed as a specific point in the process of getting form input.
159
+ All of them are optional. The block for a callback is evaluated in the scope of the component so any
160
+ instance variables and component methods are available to use including the call context.
161
+
162
+ The details of each callback are as follows
163
+
164
+ #### setup
165
+
166
+ This is run once only for a field if defined before any prompts
167
+
168
+ #### timeout
169
+
170
+ This is run if no input is received or input is not of a valid length as defined by length or min_length
171
+ field options.
172
+
173
+ #### validate
174
+
175
+ This is run after input of a valid length. The validate block is where you put validation logic of the
176
+ value just input by the user. The block should return `true` if the value is valid or `false` otherwise.
177
+ If the validate callback returns false then the invalid callback will be called next.
178
+
179
+ #### invalid
180
+
181
+ The invalid callback is called if validate block returns false.
182
+
183
+ #### confirm
184
+
185
+ The confirm callback is called after the input has been validated. The confirm callback is a little different
186
+ from the others. Idea is that you return either an array or string of the audio files or TTS text, respectively,
187
+ you want to play as the prompt for confirming the value entered. The confirm block also takes a few options:
188
+
189
+ - :accept - the number to press to accept the field value entered. Default is 1.
190
+ - :reject - the number to press the reject the field value entered and try again. Default is 2.
191
+ - :attempts - the number of attempts to try to get a confirmation response. Default is 3
192
+ - :timeout - the number of seconds to wait for input after the confirmation response. Default is 3.
193
+
194
+ The value returned from the block should form the complete list of audio files or TTS text to prompt the user
195
+ including the values to accept of reject the value.
196
+
197
+ For example, in a field called my_field:
198
+
199
+ confirm(:accept => 1, :reject => 2, :attempts => 3) do
200
+ ['you-entered', as_digits(@my_field), 'is-this-correct', 'press-1-accept-2-try-again'].flatten
201
+ end
202
+
203
+ The above will `play` the array of audio files as the prompt for confirmation.
204
+
205
+ confirm(:accept => 1, :reject => 2, :attempts => 3) do
206
+ "You entered #{@my_field}. Is this correct? Press 1 to accept or 2 try again."
207
+ end
208
+
209
+ The above will `speak` the string as the prompt for confirmation.
210
+
211
+ If no valid input is entered for the confirmation then another you will be reprompted to enter the field value.
212
+
213
+
214
+ ### Form methods
215
+
216
+ Inside a callback you have the `form` method available. The returns the instance of the current form. The form
217
+ has some methods to allow you to perform form actions which manipulate the form stack. These actions are as follows:
218
+
219
+ #### form.goto
220
+
221
+ Inside any callback you can use the `goto` command to designate which field the form should run after the
222
+ current field. Normally the form will progress through the fields in the order defined, but a goto with shift
223
+ the current form position to the field name pass to it like so:
224
+
225
+ failure do
226
+ form.goto :other_field_name
227
+ end
228
+
229
+ The form continues from the field in the goto run each subsequent field in order. If the goto field is above the
230
+ current field then the current field will be executed again when it is reached in the stack. If the goto field
231
+ is below the current field then form will continue there, skipping whatever fields may lie between the current
232
+ and the goto field.
233
+
234
+
235
+ #### form.restart
236
+
237
+ The form may be restarted from the start at any point with `form.restart`. This will go back to the top of the
238
+ form and proceed through each field again. The form setup will not be run again however.
239
+
240
+
241
+ #### form.exit
242
+
243
+ To exit the form after the current field is complete just execute `form.exit`. The application will then be
244
+ returned to where the form was started, be it a dialplan or another form.
245
+
246
+
247
+ ## Credits
248
+
249
+ Adam Meehan (adam.meehan@gmail.com, [http://duckpunching.com/](http://duckpunching.com/))
250
+
251
+ Also thanks to Jay Phillips et al. for the brilliant work on Adhearsion ([http://adhearsion.com](http://adhearsion.com)).
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+ require 'spec/rake/spectask'
6
+
7
+ GEM = "voice_form"
8
+ GEM_VERSION = "0.3.0"
9
+ AUTHOR = "Adam Meehan"
10
+ EMAIL = "adam.meehan@gmail.com"
11
+ HOMEPAGE = "http://github.com/adzap/voice_form"
12
+ SUMMARY = "A plugin for Adhearsion to create forms in the style of the VoiceXML form element."
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = GEM
16
+ s.version = GEM_VERSION
17
+ s.platform = Gem::Platform::RUBY
18
+ s.has_rdoc = false
19
+ s.summary = SUMMARY
20
+ s.description = s.summary
21
+ s.author = AUTHOR
22
+ s.email = EMAIL
23
+ s.homepage = HOMEPAGE
24
+ s.extra_rdoc_files = ["History.txt"]
25
+ # Uncomment this to add a dependency
26
+ # s.add_dependency "foo"
27
+
28
+ s.require_path = 'lib'
29
+ s.autorequire = GEM
30
+ s.files = %w(MIT-LICENSE README.markdown Rakefile) + Dir.glob("{lib,spec,examples}/**/*")
31
+ end
32
+
33
+ task :default => :spec
34
+
35
+ desc "Run specs"
36
+ Spec::Rake::SpecTask.new do |t|
37
+ t.spec_files = FileList['spec/**/*_spec.rb']
38
+ t.spec_opts = %w(-fs --color)
39
+ end
40
+
41
+ Rake::GemPackageTask.new(spec) do |pkg|
42
+ pkg.gem_spec = spec
43
+ end
44
+
45
+ desc "install the gem locally"
46
+ task :install => [:package] do
47
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
48
+ end
49
+
50
+ desc "create a gemspec file"
51
+ task :make_spec do
52
+ File.open("#{GEM}.gemspec", "w") do |file|
53
+ file.puts spec.to_ruby
54
+ end
55
+ end
@@ -0,0 +1,62 @@
1
+ class MyComponent
2
+ include VoiceForm
3
+
4
+ MAX_AGE = 110
5
+
6
+ delegate :play, :speak, :to => :call
7
+
8
+ voice_form do
9
+ field(:age, :max_length => 3, :attempts => 3) do
10
+ prompt :speak => "Please enter your age", :timeout => 2, :repeats => 2
11
+ reprompt :speak => "Enter your age in years", :timeout => 2
12
+
13
+ confirm do
14
+ "Are you sure you are #{@age} years old? Press 1 to confirm, or 2 to retry."
15
+ end
16
+
17
+ validate { @age.to_i < MAX_AGE }
18
+
19
+ invalid do
20
+ speak "You cannot be that old. Try again."
21
+ end
22
+
23
+ success do
24
+ speak "You are #{@age} years old."
25
+ end
26
+
27
+ failure do
28
+ speak "You could not enter your age. Thats a bad sign."
29
+ end
30
+ end
31
+
32
+ do_block do
33
+ speak "Get ready for the next question."
34
+ end
35
+
36
+ field(:postcode, :length => 4, :attempts => 5) do
37
+ prompt :speak => "Please enter your 4 digit postcode", :timeout => 3
38
+
39
+ validate { @postcode[0..0] != '0' }
40
+
41
+ invalid do
42
+ if @postcode.size < 4
43
+ speak "Your postcode must 4 digits."
44
+ else
45
+ speak "Your postcode cannot start with a 0."
46
+ end
47
+ end
48
+
49
+ success do
50
+ speak "Your postcode is #{@postcode.scan(/\d/).join(', ')}."
51
+ end
52
+
53
+ failure do
54
+ if @age.empty?
55
+ speak "Lets start over shall we."
56
+ form.restart
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,47 @@
1
+ methods_for :dialplan do
2
+ def simon_game_voice_form
3
+ SimonGameVoiceForm.start_voice_form(self)
4
+ end
5
+ end
6
+
7
+ class SimonGameVoiceForm
8
+ include VoiceForm
9
+
10
+ voice_form do
11
+ setup do
12
+ @number = ''
13
+ end
14
+
15
+ field(:attempt, :attempts => 1) do
16
+ prompt :play => :current_number, :bargein => false, :timeout => 2
17
+
18
+ setup do
19
+ @number << random_number
20
+ end
21
+
22
+ validate do
23
+ @attempt == @number
24
+ end
25
+
26
+ success do
27
+ call.play 'good'
28
+ form.restart
29
+ end
30
+
31
+ failure do
32
+ call.play %W[#{@number.length-1} times wrong-try-again-smarty]
33
+ @number = ''
34
+ form.restart
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ def random_number
41
+ rand(10).to_s
42
+ end
43
+
44
+ def current_number
45
+ as_digits(@number)
46
+ end
47
+ end