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.
- data/History.txt +24 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +251 -0
- data/Rakefile +55 -0
- data/examples/my_component.rb +62 -0
- data/examples/simon_game_voice_form.rb +47 -0
- data/lib/voice_form.rb +3 -0
- data/lib/voice_form/form.rb +101 -0
- data/lib/voice_form/form_field.rb +209 -0
- data/lib/voice_form/form_methods.rb +69 -0
- data/spec/form_field_spec.rb +303 -0
- data/spec/form_spec.rb +232 -0
- data/spec/spec_helper.rb +28 -0
- metadata +66 -0
data/lib/voice_form.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
module VoiceForm
|
2
|
+
|
3
|
+
class Form
|
4
|
+
include VoiceForm::FormMethods
|
5
|
+
|
6
|
+
attr_accessor :form_stack
|
7
|
+
attr_reader :current_field
|
8
|
+
|
9
|
+
def initialize(options={}, &block)
|
10
|
+
@options = options
|
11
|
+
@form_stack = []
|
12
|
+
@stack_index = 0
|
13
|
+
|
14
|
+
instance_eval(&block)
|
15
|
+
raise 'A form requires at least one field defined' if fields.empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
def run(component)
|
19
|
+
@component = component
|
20
|
+
|
21
|
+
add_field_accessors
|
22
|
+
run_setup
|
23
|
+
run_form_stack
|
24
|
+
end
|
25
|
+
|
26
|
+
def setup(&block)
|
27
|
+
@setup = block
|
28
|
+
end
|
29
|
+
|
30
|
+
def do_block(&block)
|
31
|
+
form_stack << block
|
32
|
+
end
|
33
|
+
|
34
|
+
def goto(name)
|
35
|
+
index = field_index(name)
|
36
|
+
raise "goto failed: No form field found with name '#{name}'." unless index
|
37
|
+
@stack_index = index
|
38
|
+
end
|
39
|
+
|
40
|
+
def restart
|
41
|
+
@stack_index = 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def exit
|
45
|
+
@exit = true
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def run_setup
|
51
|
+
@component.instance_eval(&@setup) if @setup
|
52
|
+
end
|
53
|
+
|
54
|
+
def run_form_stack
|
55
|
+
while @stack_index < form_stack.size && !@exit do
|
56
|
+
slot = form_stack[@stack_index]
|
57
|
+
@stack_index += 1
|
58
|
+
|
59
|
+
if form_field?(slot)
|
60
|
+
@current_field = slot.name
|
61
|
+
slot.run(@component)
|
62
|
+
else
|
63
|
+
@current_field = nil
|
64
|
+
@component.instance_eval(&slot)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
@stack_index = 0
|
68
|
+
@current_field = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def add_field_accessors
|
72
|
+
return if @accessors_added
|
73
|
+
|
74
|
+
fields.keys.each do |field_name|
|
75
|
+
@component.class.class_eval do
|
76
|
+
attr_accessor field_name
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
@accessors_added = true
|
81
|
+
end
|
82
|
+
|
83
|
+
def form_field?(slot)
|
84
|
+
slot.is_a?(VoiceForm::FormField)
|
85
|
+
end
|
86
|
+
|
87
|
+
def fields
|
88
|
+
@fields ||= form_stack.inject({}) do |flds,s|
|
89
|
+
flds[s.name] = s if form_field?(s)
|
90
|
+
flds
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def field_index(field)
|
95
|
+
form_stack.each_with_index {|slot, i|
|
96
|
+
return i if form_field?(slot) && slot.name == field.to_sym
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
module VoiceForm
|
2
|
+
|
3
|
+
class FormField
|
4
|
+
cattr_accessor :default_prompt_options
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
self.default_prompt_options = { :bargein => true, :timeout => 5 }
|
8
|
+
|
9
|
+
def initialize(name, options, component, &block)
|
10
|
+
@name, @options, @component = name, options, component
|
11
|
+
@options.reverse_merge!(:attempts => 5, :call => 'call')
|
12
|
+
@callbacks = {}
|
13
|
+
@prompt_queue = []
|
14
|
+
|
15
|
+
instance_eval(&block)
|
16
|
+
raise 'A field requires a prompt to be defined' if @prompt_queue.empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
def prompt(options)
|
20
|
+
add_prompt(options.reverse_merge(self.class.default_prompt_options))
|
21
|
+
end
|
22
|
+
|
23
|
+
def reprompt(options)
|
24
|
+
raise 'A reprompt can only be used after a prompt' if @prompt_queue.empty?
|
25
|
+
add_prompt(options.reverse_merge(self.class.default_prompt_options))
|
26
|
+
end
|
27
|
+
|
28
|
+
def setup(&block)
|
29
|
+
@callbacks[:setup] = block
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate(&block)
|
33
|
+
@callbacks[:validate] = block
|
34
|
+
end
|
35
|
+
|
36
|
+
def invalid(&block)
|
37
|
+
@callbacks[:invalid] = block
|
38
|
+
end
|
39
|
+
|
40
|
+
def timeout(&block)
|
41
|
+
@callbacks[:timeout] = block
|
42
|
+
end
|
43
|
+
|
44
|
+
def success(&block)
|
45
|
+
@callbacks[:success] = block
|
46
|
+
end
|
47
|
+
|
48
|
+
def failure(&block)
|
49
|
+
@callbacks[:failure] = block
|
50
|
+
end
|
51
|
+
|
52
|
+
def confirm(options={}, &block)
|
53
|
+
options.reverse_merge!(
|
54
|
+
self.class.default_prompt_options.merge(
|
55
|
+
:attempts => 3,
|
56
|
+
:accept => 1,
|
57
|
+
:reject => 2
|
58
|
+
)
|
59
|
+
)
|
60
|
+
@confirmation_options = options.merge(:message => block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def run(component=nil)
|
64
|
+
@component = component if component
|
65
|
+
|
66
|
+
run_callback(:setup)
|
67
|
+
|
68
|
+
result = 1.upto(@options[:attempts]) do |attempt|
|
69
|
+
@value = get_input(prompt_for_attempt(attempt))
|
70
|
+
|
71
|
+
unless valid_length?
|
72
|
+
run_callback(:timeout)
|
73
|
+
next
|
74
|
+
end
|
75
|
+
|
76
|
+
if input_valid?
|
77
|
+
if value_confirmed?
|
78
|
+
break 0
|
79
|
+
else
|
80
|
+
next
|
81
|
+
end
|
82
|
+
else
|
83
|
+
run_callback(:invalid)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
if result == 0
|
87
|
+
run_callback(:success)
|
88
|
+
else
|
89
|
+
run_callback(:failure)
|
90
|
+
end
|
91
|
+
set_component_value @value
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def get_input(prompt)
|
97
|
+
method = prompt[:method]
|
98
|
+
message = prompt[:message]
|
99
|
+
|
100
|
+
if prompt[:bargein]
|
101
|
+
prompt[method] = message
|
102
|
+
else
|
103
|
+
call.send(method, message)
|
104
|
+
end
|
105
|
+
|
106
|
+
args = [ prompt.slice(method, :timeout, :accept_key) ]
|
107
|
+
args.unshift(prompt[:length]) if prompt[:length]
|
108
|
+
call.input(*args)
|
109
|
+
end
|
110
|
+
|
111
|
+
def input_valid?
|
112
|
+
run_callback(:validate)
|
113
|
+
end
|
114
|
+
|
115
|
+
def valid_length?
|
116
|
+
!@value.empty? &&
|
117
|
+
@value.size >= minimum_length &&
|
118
|
+
@value.size <= maximum_length
|
119
|
+
end
|
120
|
+
|
121
|
+
def value_confirmed?
|
122
|
+
return true unless @confirmation_options
|
123
|
+
|
124
|
+
prompt = evaluate_prompt(@confirmation_options)
|
125
|
+
prompt[:method] = prompt[:message].is_a?(Array) ? :play : :speak
|
126
|
+
prompt[:length] = [ prompt[:accept].to_s.size, prompt[:reject].to_s.size ].max
|
127
|
+
|
128
|
+
1.upto(prompt[:attempts]) do |attempt|
|
129
|
+
case get_input(prompt)
|
130
|
+
when prompt[:accept].to_s
|
131
|
+
return true
|
132
|
+
when prompt[:reject].to_s
|
133
|
+
return false
|
134
|
+
else
|
135
|
+
next
|
136
|
+
end
|
137
|
+
end
|
138
|
+
false
|
139
|
+
end
|
140
|
+
|
141
|
+
def run_callback(callback)
|
142
|
+
if block = @callbacks[callback]
|
143
|
+
set_component_value @value
|
144
|
+
result = @component.instance_eval(&block)
|
145
|
+
@value = get_component_value
|
146
|
+
result
|
147
|
+
else
|
148
|
+
true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def set_component_value(value)
|
153
|
+
@component.send("#{@name}=", @value)
|
154
|
+
end
|
155
|
+
|
156
|
+
def get_component_value
|
157
|
+
@component.send(@name)
|
158
|
+
end
|
159
|
+
|
160
|
+
def minimum_length
|
161
|
+
@options[:min_length] || @options[:length] || 1
|
162
|
+
end
|
163
|
+
|
164
|
+
def maximum_length
|
165
|
+
@options[:max_length] || @options[:length] || @value.size
|
166
|
+
end
|
167
|
+
|
168
|
+
def call
|
169
|
+
@call ||= @component.send(@options[:call])
|
170
|
+
end
|
171
|
+
|
172
|
+
def add_prompt(options)
|
173
|
+
method = options.has_key?(:play) ? :play : :speak
|
174
|
+
options[:message] = options.delete(method)
|
175
|
+
options[:method] = method
|
176
|
+
options[:length] = @options[:length] || @options[:max_length]
|
177
|
+
|
178
|
+
repeats = options[:repeats] || 1
|
179
|
+
@prompt_queue += ([options] * repeats)
|
180
|
+
end
|
181
|
+
|
182
|
+
def prompt_for_attempt(attempt)
|
183
|
+
prompt = if attempt == 1 || @prompt_queue.size == 1 then
|
184
|
+
@prompt_queue.first
|
185
|
+
else
|
186
|
+
@prompt_queue[attempt-1] || @prompt_queue.last
|
187
|
+
end
|
188
|
+
evaluate_prompt(prompt)
|
189
|
+
end
|
190
|
+
|
191
|
+
def evaluate_prompt(prompt)
|
192
|
+
options = prompt.dup
|
193
|
+
message = options[:message]
|
194
|
+
|
195
|
+
message = case message
|
196
|
+
when String, Array
|
197
|
+
message
|
198
|
+
when Symbol
|
199
|
+
@component.send(message)
|
200
|
+
when Proc
|
201
|
+
@component.instance_eval(&message)
|
202
|
+
end
|
203
|
+
|
204
|
+
options[:message] = message
|
205
|
+
options
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module VoiceForm
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
base.class_eval do
|
6
|
+
include FormMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def voice_form(options={}, &block)
|
13
|
+
raise "Voice form requires block" unless block_given?
|
14
|
+
|
15
|
+
self.class_eval do
|
16
|
+
include InstanceMethods
|
17
|
+
|
18
|
+
cattr_accessor :voice_form_options
|
19
|
+
attr_accessor :form, :call
|
20
|
+
end
|
21
|
+
|
22
|
+
self.voice_form_options = [options, block]
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_voice_form(call)
|
26
|
+
raise "No voice form defined" unless voice_form_options
|
27
|
+
self.new.start_voice_form(call)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
module InstanceMethods
|
33
|
+
|
34
|
+
def start_voice_form(call)
|
35
|
+
raise "No voice form defined" unless self.class.voice_form_options
|
36
|
+
options, block = *self.class.voice_form_options
|
37
|
+
@call = call
|
38
|
+
self.form = VoiceForm::Form.new(options, &block)
|
39
|
+
self.form.run(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def as_digits(string)
|
43
|
+
string.scan(/\d/).map {|v| v.to_i }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module FormMethods
|
48
|
+
|
49
|
+
# Can be used in a form or stand-alone in a component method
|
50
|
+
def field(field_name, options={}, &block)
|
51
|
+
raise "A field requires a block" unless block_given?
|
52
|
+
|
53
|
+
form_field = VoiceForm::FormField.new(field_name, options, self, &block)
|
54
|
+
|
55
|
+
if self.is_a?(VoiceForm::Form)
|
56
|
+
self.form_stack << form_field
|
57
|
+
else
|
58
|
+
unless self.respond_to?(field_name)
|
59
|
+
self.class.class_eval do
|
60
|
+
attr_accessor field_name
|
61
|
+
end
|
62
|
+
end
|
63
|
+
form_field.run
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe VoiceForm::FormField do
|
4
|
+
include VoiceForm::FormMethods
|
5
|
+
|
6
|
+
attr_accessor :call, :my_field
|
7
|
+
|
8
|
+
before do
|
9
|
+
@call = mock('Call', :play => nil, :speak => nil)
|
10
|
+
@call.stub!(:input).with(any_args).and_return('')
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should define accessor for field in component" do
|
14
|
+
field(:my_field) do
|
15
|
+
prompt :play => 'test'
|
16
|
+
end
|
17
|
+
self.methods.include?(:my_field)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should raise error if no prompts defined" do
|
21
|
+
lambda {
|
22
|
+
form_field(:my_field) {}
|
23
|
+
}.should raise_error
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should raise error if reprompt defined before prompt" do
|
27
|
+
lambda {
|
28
|
+
form_field(:my_field) do
|
29
|
+
reprompt :play => 'again'
|
30
|
+
end
|
31
|
+
}.should raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return same prompt for for all attempts if single prompt" do
|
35
|
+
item = form_field(:my_field) do
|
36
|
+
prompt :play => "first"
|
37
|
+
end
|
38
|
+
item.send(:prompt_for_attempt, 1)[:message].should == 'first'
|
39
|
+
item.send(:prompt_for_attempt, 2)[:message].should == 'first'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return reprompt for subsequent prompts" do
|
43
|
+
item = form_field(:my_field) do
|
44
|
+
prompt :play => "first"
|
45
|
+
reprompt :play => 'next'
|
46
|
+
end
|
47
|
+
item.send(:prompt_for_attempt, 1)[:message].should == 'first'
|
48
|
+
item.send(:prompt_for_attempt, 2)[:message].should == 'next'
|
49
|
+
item.send(:prompt_for_attempt, 3)[:message].should == 'next'
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should return prompt for given number of repeats before subsequent prompts" do
|
53
|
+
item = form_field(:my_field) do
|
54
|
+
prompt :play => "first", :repeats => 2
|
55
|
+
reprompt :play => 'next'
|
56
|
+
end
|
57
|
+
item.send(:prompt_for_attempt, 1)[:message].should == 'first'
|
58
|
+
item.send(:prompt_for_attempt, 2)[:message].should == 'first'
|
59
|
+
item.send(:prompt_for_attempt, 3)[:message].should == 'next'
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should set input value in component" do
|
63
|
+
item = form_field(:my_field, :length => 3) do
|
64
|
+
prompt :play => "first"
|
65
|
+
end
|
66
|
+
call.stub!(:input).and_return('123')
|
67
|
+
item.run
|
68
|
+
|
69
|
+
my_field.should == '123'
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should run setup callback once" do
|
73
|
+
call_me = i_should_be_called
|
74
|
+
item = form_field(:my_field, :attempts => 3) do
|
75
|
+
prompt :play => "first"
|
76
|
+
setup { call_me.call }
|
77
|
+
end
|
78
|
+
call.should_receive(:input).and_return('')
|
79
|
+
|
80
|
+
item.run
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should run timeout callback if no input" do
|
84
|
+
call_me = i_should_be_called
|
85
|
+
item = form_field(:my_field, :attempts => 1) do
|
86
|
+
prompt :play => "first"
|
87
|
+
timeout { call_me.call }
|
88
|
+
end
|
89
|
+
call.should_receive(:input).and_return('')
|
90
|
+
|
91
|
+
item.run
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should run timeout callback if input not valid length" do
|
95
|
+
call_me = i_should_be_called
|
96
|
+
item = form_field(:my_field, :attempts => 1, :length => 3) do
|
97
|
+
prompt :play => "first"
|
98
|
+
timeout { call_me.call }
|
99
|
+
end
|
100
|
+
call.should_receive(:input).and_return('12')
|
101
|
+
|
102
|
+
item.run
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should make all attempts to get valid input" do
|
106
|
+
item = form_field(:my_field) do
|
107
|
+
prompt :play => "first"
|
108
|
+
end
|
109
|
+
call.should_receive(:input).exactly(3).times
|
110
|
+
|
111
|
+
item.run
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should make one attempt if input is valid" do
|
115
|
+
item = form_field(:my_field) do
|
116
|
+
prompt :play => "first"
|
117
|
+
end
|
118
|
+
item.stub!(:input_valid?).and_return(true)
|
119
|
+
call.should_receive(:input).once
|
120
|
+
|
121
|
+
item.run
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should check if input_valid?" do
|
125
|
+
item = form_field(:my_field, :length => 3) do
|
126
|
+
prompt :play => "first"
|
127
|
+
end
|
128
|
+
call.should_receive(:input).and_return('123')
|
129
|
+
item.should_receive(:input_valid?)
|
130
|
+
|
131
|
+
item.run
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should run validation callback if defined" do
|
135
|
+
call_me = i_should_be_called
|
136
|
+
item = form_field(:my_field, :length => 3, :attempts => 1) do
|
137
|
+
prompt :play => "first"
|
138
|
+
validate { call_me.call }
|
139
|
+
end
|
140
|
+
call.stub!(:input).and_return('123')
|
141
|
+
|
142
|
+
item.run
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should run confirm callback if defined" do
|
146
|
+
call_me = i_should_be_called
|
147
|
+
item = form_field(:my_field, :length => 3, :attempts => 1) do
|
148
|
+
prompt :play => "first"
|
149
|
+
confirm { call_me.call; [] }
|
150
|
+
end
|
151
|
+
call.stub!(:input).and_return('123')
|
152
|
+
|
153
|
+
item.run
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "confirm callback" do
|
157
|
+
|
158
|
+
it "should not run if not valid input" do
|
159
|
+
dont_call_me = i_should_not_be_called
|
160
|
+
item = form_field(:my_field, :length => 3, :attempts => 1) do
|
161
|
+
prompt :play => "first"
|
162
|
+
validate { false }
|
163
|
+
confirm { dont_call_me.call }
|
164
|
+
end
|
165
|
+
call.stub!(:input).and_return('123')
|
166
|
+
|
167
|
+
item.run
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should be run the number of attempts if no valid response" do
|
171
|
+
call.should_receive(:input).with(3, anything).and_return('123')
|
172
|
+
item = form_field(:my_field, :length => 3, :attempts => 1) do
|
173
|
+
prompt :play => "first"
|
174
|
+
|
175
|
+
confirm(:attempts => 3) { [] }
|
176
|
+
end
|
177
|
+
call.should_receive(:input).with(1, anything).exactly(3).times.and_return('')
|
178
|
+
|
179
|
+
item.run
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should run success callback if accept value entered" do
|
183
|
+
call_me = i_should_be_called
|
184
|
+
call.should_receive(:input).with(3, anything).and_return('123')
|
185
|
+
item = form_field(:my_field, :length => 3, :attempts => 1) do
|
186
|
+
prompt :play => "first"
|
187
|
+
|
188
|
+
success { call_me.call }
|
189
|
+
confirm(:accept => '1', :reject => '2') { [] }
|
190
|
+
end
|
191
|
+
call.should_receive(:input).with(1, anything).and_return('1')
|
192
|
+
|
193
|
+
item.run
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should run failure callback if reject value entered" do
|
197
|
+
call_me = i_should_be_called
|
198
|
+
call.should_receive(:input).with(3, anything).and_return('123')
|
199
|
+
item = form_field(:my_field, :length => 3, :attempts => 1) do
|
200
|
+
prompt :play => "first"
|
201
|
+
|
202
|
+
failure { call_me.call }
|
203
|
+
confirm(:accept => '1', :reject => '2') { [] }
|
204
|
+
end
|
205
|
+
call.should_receive(:input).with(1, anything).and_return('2')
|
206
|
+
|
207
|
+
item.run
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should play confirmation input prompt if confirm block return value is array" do
|
211
|
+
call_me = i_should_be_called
|
212
|
+
call.should_receive(:input).with(3, anything).and_return('123')
|
213
|
+
|
214
|
+
item = form_field(:my_field, :length => 3, :attempts => 1) do
|
215
|
+
prompt :play => "first"
|
216
|
+
|
217
|
+
success { call_me.call }
|
218
|
+
confirm(:timeout => 3) { [] }
|
219
|
+
end
|
220
|
+
call.should_receive(:input).with(1, {:play => [], :timeout => 3}).and_return('1')
|
221
|
+
|
222
|
+
item.run
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should speak confirmation input prompt if confirm block return value is string" do
|
226
|
+
call_me = i_should_be_called
|
227
|
+
call.should_receive(:input).with(3, anything).and_return('123')
|
228
|
+
|
229
|
+
item = form_field(:my_field, :length => 3, :attempts => 1) do
|
230
|
+
prompt :play => "first"
|
231
|
+
|
232
|
+
success { call_me.call }
|
233
|
+
confirm(:timeout => 3) { 'speak me' }
|
234
|
+
end
|
235
|
+
call.should_receive(:input).with(1, {:speak => 'speak me', :timeout => 3}).and_return('1')
|
236
|
+
|
237
|
+
item.run
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should run failure callback if no input" do
|
242
|
+
call_me = i_should_be_called
|
243
|
+
item = form_field(:my_field, :length => 3) do
|
244
|
+
prompt :play => "first"
|
245
|
+
failure { call_me.call }
|
246
|
+
end
|
247
|
+
call.should_receive(:input).and_return('')
|
248
|
+
|
249
|
+
item.run
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should run success callback if input valid length" do
|
253
|
+
call_me = i_should_be_called
|
254
|
+
item = form_field(:my_field, :length => 3) do
|
255
|
+
prompt :play => "first"
|
256
|
+
success { call_me.call }
|
257
|
+
end
|
258
|
+
call.should_receive(:input).and_return('123')
|
259
|
+
|
260
|
+
item.run
|
261
|
+
end
|
262
|
+
|
263
|
+
it "should run success callback if input valid length and valid input" do
|
264
|
+
validate_me = i_should_be_called
|
265
|
+
call_me = i_should_be_called
|
266
|
+
item = form_field(:my_field, :length => 3) do
|
267
|
+
prompt :play => "first"
|
268
|
+
validate do
|
269
|
+
validate_me.call
|
270
|
+
my_field.to_i > 100
|
271
|
+
end
|
272
|
+
success { call_me.call }
|
273
|
+
end
|
274
|
+
call.should_receive(:input).and_return('123')
|
275
|
+
|
276
|
+
item.run
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should execute input with message when bargein is true" do
|
280
|
+
item = form_field(:my_field, :length => 3) do
|
281
|
+
prompt :play => "first", :bargein => true
|
282
|
+
end
|
283
|
+
call.should_not_receive(:play)
|
284
|
+
call.should_receive(:input).with(3, :play => "first", :timeout => 5)
|
285
|
+
|
286
|
+
item.run
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should execute playback method with message and input without message when bargein is false" do
|
290
|
+
item = form_field(:my_field, :length => 3) do
|
291
|
+
prompt :play => "first", :bargein => false
|
292
|
+
end
|
293
|
+
call.should_receive(:play).with('first')
|
294
|
+
call.should_receive(:input).with(3, :timeout => 5)
|
295
|
+
|
296
|
+
item.run
|
297
|
+
end
|
298
|
+
|
299
|
+
def form_field(field, options={}, &block)
|
300
|
+
self.class.class_eval { attr_accessor field }
|
301
|
+
VoiceForm::FormField.new(field, {:attempts => 3}.merge(options), self, &block)
|
302
|
+
end
|
303
|
+
end
|