larynx 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +4 -0
- data/Rakefile +0 -1
- data/lib/larynx/application.rb +2 -0
- data/lib/larynx/call_handler.rb +2 -1
- data/lib/larynx/callbacks_with_async.rb +1 -1
- data/lib/larynx/fields.rb +32 -12
- data/lib/larynx/form.rb +4 -4
- data/lib/larynx/prompt.rb +5 -2
- data/lib/larynx/server.rb +95 -0
- data/lib/larynx/session.rb +4 -0
- data/lib/larynx/version.rb +1 -1
- data/spec/larynx/call_handler_spec.rb +12 -0
- data/spec/larynx/fields_spec.rb +49 -1
- data/spec/larynx/form_spec.rb +49 -0
- data/spec/larynx/prompt_spec.rb +42 -15
- metadata +7 -5
data/README.rdoc
CHANGED
@@ -212,6 +212,10 @@ together and define another form for unrelated fields.
|
|
212
212
|
Lets look at a simple Form class with empty callbacks in place.
|
213
213
|
|
214
214
|
class MyApp < Larynx::Form
|
215
|
+
setup do
|
216
|
+
# Run when the form is first run or restarted.
|
217
|
+
end
|
218
|
+
|
215
219
|
field(:my_field, :attempts => 3, :length => 1) do
|
216
220
|
prompt :speak => 'Please enter a value.'
|
217
221
|
|
data/Rakefile
CHANGED
@@ -23,7 +23,6 @@ spec = Gem::Specification.new do |s|
|
|
23
23
|
s.homepage = "http://github.com/adzap/larynx"
|
24
24
|
|
25
25
|
s.require_path = 'lib'
|
26
|
-
s.autorequire = GEM_NAME
|
27
26
|
s.files = %w(MIT-LICENSE README.rdoc Rakefile) + Dir.glob("{lib,spec,examples}/**/*")
|
28
27
|
s.add_dependency "eventmachine", "~> 0.12.10"
|
29
28
|
s.add_dependency "activesupport", "~> 2.3.5"
|
data/lib/larynx/application.rb
CHANGED
data/lib/larynx/call_handler.rb
CHANGED
@@ -107,7 +107,7 @@ module Larynx
|
|
107
107
|
finalize_command
|
108
108
|
@state = :ready
|
109
109
|
Larynx.fire_callback(:answer, self)
|
110
|
-
send_next_command
|
110
|
+
send_next_command if @state == :ready
|
111
111
|
when @response.executing?
|
112
112
|
log "Executing: #{current_command.name}"
|
113
113
|
current_command.setup
|
@@ -168,6 +168,7 @@ module Larynx
|
|
168
168
|
break! if @state == :executing
|
169
169
|
cancel_all_timers
|
170
170
|
clear_observers!
|
171
|
+
@session = nil
|
171
172
|
end
|
172
173
|
|
173
174
|
def log(msg)
|
data/lib/larynx/fields.rb
CHANGED
@@ -26,30 +26,47 @@ module Larynx
|
|
26
26
|
|
27
27
|
def initialize(*args, &block)
|
28
28
|
@fields = self.class.field_definitions.map {|field| Field.new(field[:name], field[:options], &field[:block]) }
|
29
|
-
@
|
29
|
+
@field_index = -1
|
30
30
|
super
|
31
31
|
end
|
32
32
|
|
33
33
|
def next_field(field_name=nil)
|
34
|
-
|
35
|
-
|
34
|
+
if field_name
|
35
|
+
@field_index = field_index(field_name)
|
36
|
+
else
|
37
|
+
@field_index += 1
|
38
|
+
end
|
39
|
+
if field = current_field
|
36
40
|
field.run(self)
|
37
|
-
@current_field += 1
|
38
41
|
field
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
45
|
+
def current_field
|
46
|
+
@fields[@field_index]
|
47
|
+
end
|
48
|
+
|
42
49
|
def field_index(name)
|
43
50
|
field = @fields.find {|f| f.name == name }
|
44
51
|
@fields.index(field)
|
45
52
|
end
|
46
53
|
|
54
|
+
def attempt
|
55
|
+
current_field.attempt
|
56
|
+
end
|
57
|
+
|
58
|
+
def last_attempt?
|
59
|
+
current_field.last_attempt?
|
60
|
+
end
|
61
|
+
|
47
62
|
end
|
48
63
|
|
49
64
|
class Field
|
50
65
|
include CallbacksWithAsync
|
51
66
|
|
52
|
-
|
67
|
+
VALID_PROMPT_OPTIONS = [:play, :speak, :phrase, :bargein, :repeats, :interdigit_timeout, :timeout]
|
68
|
+
|
69
|
+
attr_reader :name, :app, :attempt
|
53
70
|
define_callback :setup, :validate, :invalid, :success, :failure, :scope => :app
|
54
71
|
|
55
72
|
def initialize(name, options, &block)
|
@@ -71,7 +88,7 @@ module Larynx
|
|
71
88
|
end
|
72
89
|
|
73
90
|
def add_prompt(options)
|
74
|
-
options.assert_valid_keys(
|
91
|
+
options.assert_valid_keys(*VALID_PROMPT_OPTIONS)
|
75
92
|
repeats = options.delete(:repeats) || 1
|
76
93
|
options.merge!(@options.slice(:length, :min_length, :max_length, :interdigit_timeout, :timeout))
|
77
94
|
@prompt_queue += ([options] * repeats)
|
@@ -99,10 +116,9 @@ module Larynx
|
|
99
116
|
end
|
100
117
|
|
101
118
|
# hook called when callback is complete
|
102
|
-
def callback_complete(callback, result)
|
119
|
+
def callback_complete(callback, result=true)
|
103
120
|
case callback
|
104
121
|
when :validate
|
105
|
-
result = result.nil? ? true : result
|
106
122
|
evaluate_validity(result)
|
107
123
|
when :invalid
|
108
124
|
invalid_input
|
@@ -120,11 +136,11 @@ module Larynx
|
|
120
136
|
end
|
121
137
|
|
122
138
|
def invalid_input
|
123
|
-
if
|
139
|
+
if last_attempt?
|
140
|
+
fire_callback(:failure)
|
141
|
+
else
|
124
142
|
increment_attempts
|
125
143
|
execute_prompt
|
126
|
-
else
|
127
|
-
fire_callback(:failure)
|
128
144
|
end
|
129
145
|
end
|
130
146
|
|
@@ -138,7 +154,7 @@ module Larynx
|
|
138
154
|
end
|
139
155
|
|
140
156
|
def command_from_options(options)
|
141
|
-
(
|
157
|
+
(Prompt::COMMAND_OPTIONS & options.keys).first
|
142
158
|
end
|
143
159
|
|
144
160
|
def run(app)
|
@@ -158,6 +174,10 @@ module Larynx
|
|
158
174
|
send_next_command
|
159
175
|
end
|
160
176
|
|
177
|
+
def last_attempt?
|
178
|
+
@attempt == @options[:attempts]
|
179
|
+
end
|
180
|
+
|
161
181
|
end
|
162
182
|
|
163
183
|
end
|
data/lib/larynx/form.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
module Larynx
|
2
2
|
class Form < Application
|
3
3
|
include Fields
|
4
|
-
|
4
|
+
class_inheritable_accessor :setup_block
|
5
5
|
|
6
6
|
def self.setup(&block)
|
7
|
-
|
7
|
+
self.setup_block = block
|
8
8
|
end
|
9
9
|
|
10
10
|
def run
|
11
|
-
instance_eval
|
11
|
+
instance_eval &self.class.setup_block if self.class.setup_block
|
12
12
|
next_field
|
13
13
|
end
|
14
14
|
|
15
15
|
def restart_form
|
16
|
-
@
|
16
|
+
@field_index = -1
|
17
17
|
run
|
18
18
|
end
|
19
19
|
end
|
data/lib/larynx/prompt.rb
CHANGED
@@ -13,6 +13,8 @@ module Larynx
|
|
13
13
|
class Prompt
|
14
14
|
attr_reader :call
|
15
15
|
|
16
|
+
COMMAND_OPTIONS = [:play, :speak, :phrase]
|
17
|
+
|
16
18
|
def initialize(call, options, &block)
|
17
19
|
@call, @options, @block = call, options, block
|
18
20
|
@options.reverse_merge!(:bargein => true, :timeout => 10, :interdigit_timeout => 3, :termchar => '#')
|
@@ -21,8 +23,9 @@ module Larynx
|
|
21
23
|
|
22
24
|
def command
|
23
25
|
@command ||= AppCommand.new(command_name, message, :bargein => @options[:bargein]).
|
24
|
-
before { call.clear_input }.
|
26
|
+
before { call.clear_input unless @options[:bargein] }.
|
25
27
|
after {
|
28
|
+
call.clear_input unless @options[:bargein]
|
26
29
|
if prompt_finished?
|
27
30
|
finalise
|
28
31
|
else
|
@@ -62,7 +65,7 @@ module Larynx
|
|
62
65
|
end
|
63
66
|
|
64
67
|
def command_name
|
65
|
-
(
|
68
|
+
(COMMAND_OPTIONS & @options.keys).first.to_s
|
66
69
|
end
|
67
70
|
|
68
71
|
def message
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'daemons/daemonize'
|
3
|
+
|
4
|
+
module Larynx
|
5
|
+
module Server
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def boot
|
9
|
+
parse_options(ARGV)
|
10
|
+
setup_app
|
11
|
+
daemonize if @options[:daemonize]
|
12
|
+
setup_logger
|
13
|
+
trap_signals
|
14
|
+
start_server
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_options(args=ARGV)
|
18
|
+
@options = {
|
19
|
+
:ip => "0.0.0.0",
|
20
|
+
:port => 8084,
|
21
|
+
:pid_file => "./larynx.pid",
|
22
|
+
:log_file => "./larynx.log"
|
23
|
+
}
|
24
|
+
opts = OptionParser.new
|
25
|
+
opts.banner = "Usage: larynx [options] app_file"
|
26
|
+
opts.separator ''
|
27
|
+
opts.separator "Larynx is a framework to develop FreeSWITCH IVR applications in Ruby."
|
28
|
+
opts.on('-i', '--ip IP', 'Listen for connections on this IP') {|ip| @options[:ip] = ip }
|
29
|
+
opts.on('-p', '--port PORT', 'Listen on this port', Integer) {|port| @options[:port] = port }
|
30
|
+
opts.on('-d', '--daemonize', 'Run as daemon') { @options[:daemonize] = true }
|
31
|
+
opts.on('-l', '--log-file FILE', 'Defaults to /app/root/larynx.log') {|log| @options[:log_file] = log }
|
32
|
+
opts.on( '--pid-file FILE', 'Defaults to /app/root/larynx.pid') {|pid| @options[:pid_file] = pid }
|
33
|
+
opts.on('-h', '--help', 'This is it') { $stderr.puts opts; exit 0 }
|
34
|
+
opts.on('-v', '--version') { $stderr.puts "Larynx version #{Larynx::VERSION}"; exit 0 }
|
35
|
+
opts.parse!(args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def setup_logger
|
39
|
+
logger = Larynx::Logger.new(@options[:log_file])
|
40
|
+
logger.level = Logger::INFO
|
41
|
+
Object.const_set "LARYNX_LOGGER", logger
|
42
|
+
end
|
43
|
+
|
44
|
+
def graceful_exit
|
45
|
+
msg = "Shutting down Larynx"
|
46
|
+
$stderr.puts msg unless @options[:daemon]
|
47
|
+
LARYNX_LOGGER.info msg
|
48
|
+
|
49
|
+
EM.stop_server @em_signature
|
50
|
+
@em_signature = nil
|
51
|
+
remove_pid_file if @options[:daemonize]
|
52
|
+
exit 130
|
53
|
+
end
|
54
|
+
|
55
|
+
def daemonize
|
56
|
+
Daemonize.daemonize
|
57
|
+
Dir.chdir LARYNX_ROOT
|
58
|
+
File.open(@options[:pid_file], 'w+') {|f| f.write("#{Process.pid}\n") }
|
59
|
+
end
|
60
|
+
|
61
|
+
def remove_pid_file
|
62
|
+
File.delete @options[:pid_file]
|
63
|
+
end
|
64
|
+
|
65
|
+
def trap_signals
|
66
|
+
trap('TERM') { graceful_exit }
|
67
|
+
trap('INT') { graceful_exit }
|
68
|
+
end
|
69
|
+
|
70
|
+
def setup_app
|
71
|
+
if ARGV[0].nil?
|
72
|
+
$stderr.puts "You must specify an application file"
|
73
|
+
exit -1
|
74
|
+
end
|
75
|
+
Object.const_set "LARYNX_ROOT", File.expand_path(File.dirname(ARGV[0]))
|
76
|
+
require File.expand_path(ARGV[0])
|
77
|
+
end
|
78
|
+
|
79
|
+
def start_server
|
80
|
+
msg = "Larynx starting up on #{@options[:ip]}:#{@options[:port]}"
|
81
|
+
$stderr.puts msg unless @options[:daemon]
|
82
|
+
LARYNX_LOGGER.info msg
|
83
|
+
|
84
|
+
EM::run {
|
85
|
+
@em_signature = EM::start_server @options[:ip], @options[:port], Larynx::CallHandler
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def running?
|
90
|
+
!@em_signature.nil?
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/larynx/session.rb
CHANGED
data/lib/larynx/version.rb
CHANGED
@@ -198,6 +198,18 @@ describe Larynx::CallHandler do
|
|
198
198
|
end
|
199
199
|
end
|
200
200
|
|
201
|
+
it "should send next command if state is ready" do
|
202
|
+
call.should_receive(:send_next_command)
|
203
|
+
answer_call
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should not send next command if answer callback changed state" do
|
207
|
+
call.should_not_receive(:send_next_command)
|
208
|
+
with_global_callback(:answer, lambda { call.state = :sending }) do
|
209
|
+
answer_call
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
201
213
|
def answer_call
|
202
214
|
call.send_response :answered
|
203
215
|
end
|
data/spec/larynx/fields_spec.rb
CHANGED
@@ -25,7 +25,7 @@ describe Larynx::Fields do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
context 'next_field' do
|
28
|
+
context '#next_field' do
|
29
29
|
before do
|
30
30
|
@app = define_app do
|
31
31
|
field(:field1) { prompt :speak => 'hello' }
|
@@ -45,6 +45,21 @@ describe Larynx::Fields do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
context "#current_field" do
|
49
|
+
it 'should return field of current position' do
|
50
|
+
@app = define_app do
|
51
|
+
field(:field1) { prompt :speak => 'hello' }
|
52
|
+
field(:field2) { prompt :speak => 'hello' }
|
53
|
+
end.new(call)
|
54
|
+
app.run
|
55
|
+
|
56
|
+
app.next_field
|
57
|
+
app.current_field.should == app.fields[0]
|
58
|
+
app.next_field
|
59
|
+
app.current_field.should == app.fields[1]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
48
63
|
context 'field object' do
|
49
64
|
it 'should raise exception if field has no prompt' do
|
50
65
|
lambda { field(:guess) {} }.should raise_exception(Larynx::NoPromptDefined)
|
@@ -105,6 +120,27 @@ describe Larynx::Fields do
|
|
105
120
|
fld.current_prompt.message.should == 'second'
|
106
121
|
end
|
107
122
|
|
123
|
+
context "#last_attempt?" do
|
124
|
+
it 'should return false when current attempt not equal to max attempts' do
|
125
|
+
fld = field(:guess, :attempts => 2) do
|
126
|
+
prompt :speak => 'first'
|
127
|
+
end
|
128
|
+
fld.run(app)
|
129
|
+
fld.attempt.should == 1
|
130
|
+
fld.last_attempt?.should be_false
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should return true when current attempt equals max attempts' do
|
134
|
+
fld = field(:guess, :attempts => 2) do
|
135
|
+
prompt :speak => 'first'
|
136
|
+
end
|
137
|
+
fld.run(app)
|
138
|
+
fld.increment_attempts
|
139
|
+
fld.attempt.should == 2
|
140
|
+
fld.last_attempt?.should be_true
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
108
144
|
context 'input evaluation' do
|
109
145
|
it 'should run validate callback if input minimum length' do
|
110
146
|
call_me = should_be_called
|
@@ -139,6 +175,18 @@ describe Larynx::Fields do
|
|
139
175
|
fld.current_prompt.finalise
|
140
176
|
end
|
141
177
|
|
178
|
+
it 'should run invalid callback if validate callback returns nil' do
|
179
|
+
call_me = should_be_called
|
180
|
+
fld = field(:guess, :min_length => 1) do
|
181
|
+
prompt :speak => 'first'
|
182
|
+
validate { nil }
|
183
|
+
invalid &call_me
|
184
|
+
end
|
185
|
+
fld.run app
|
186
|
+
call.input << '1'
|
187
|
+
fld.current_prompt.finalise
|
188
|
+
end
|
189
|
+
|
142
190
|
it 'should run success callback if length valid and no validate callback' do
|
143
191
|
call_me = should_be_called
|
144
192
|
fld = field(:guess, :min_length => 1) do
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
class TestForm < Larynx::Form; end
|
4
|
+
|
5
|
+
describe Larynx::Form do
|
6
|
+
attr_reader :call
|
7
|
+
|
8
|
+
before do
|
9
|
+
@call = TestCallHandler.new(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
context "#run" do
|
13
|
+
it 'should call setup block' do
|
14
|
+
this_should_be_called = should_be_called
|
15
|
+
define_form do
|
16
|
+
setup &this_should_be_called
|
17
|
+
end.run(call)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "#restart_form" do
|
22
|
+
it 'should call form setup block' do
|
23
|
+
this_should_be_called = should_be_called
|
24
|
+
form = define_form do
|
25
|
+
setup &this_should_be_called
|
26
|
+
end.new(call)
|
27
|
+
form.restart_form
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should run the first field again' do
|
31
|
+
form = define_form do
|
32
|
+
field(:test1) { prompt :speak => '' }
|
33
|
+
field(:test2) { prompt :speak => '' }
|
34
|
+
end.new(call)
|
35
|
+
|
36
|
+
form.fields[0].should_receive(:run).twice
|
37
|
+
form.run
|
38
|
+
form.next_field
|
39
|
+
form.restart_form
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def define_form(&block)
|
44
|
+
reset_class(TestForm) do
|
45
|
+
instance_eval &block if block_given?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/spec/larynx/prompt_spec.rb
CHANGED
@@ -39,6 +39,27 @@ describe Larynx::Prompt do
|
|
39
39
|
@prompt.input.should == '1'
|
40
40
|
end
|
41
41
|
end
|
42
|
+
context "command" do
|
43
|
+
it "should return command object for command name" do
|
44
|
+
cmd = new_prompt.command
|
45
|
+
cmd.should be_kind_of(Larynx::AppCommand)
|
46
|
+
cmd.command.should == 'speak'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "before callback" do
|
51
|
+
it "should clear input on execution if no bargein allowed" do
|
52
|
+
call.input << '1'
|
53
|
+
before_callback new_prompt(:speak => 'hello', :bargein => false)
|
54
|
+
call.input.should be_empty
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should clear input on execution if bargein allowed" do
|
58
|
+
call.input << '1'
|
59
|
+
before_callback new_prompt(:speak => 'hello', :bargein => true)
|
60
|
+
call.input.should_not be_empty
|
61
|
+
end
|
62
|
+
end
|
42
63
|
|
43
64
|
context "prompt_finished?" do
|
44
65
|
it "should return true if input length reached" do
|
@@ -66,23 +87,28 @@ describe Larynx::Prompt do
|
|
66
87
|
end
|
67
88
|
end
|
68
89
|
|
69
|
-
context "command" do
|
70
|
-
it "should return command object for command name" do
|
71
|
-
cmd = new_prompt.command
|
72
|
-
cmd.should be_kind_of(Larynx::AppCommand)
|
73
|
-
cmd.command.should == 'speak'
|
74
|
-
end
|
75
|
-
end
|
76
90
|
|
77
|
-
context "
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
91
|
+
context "after callback" do
|
92
|
+
context "bargein" do
|
93
|
+
it 'should clear input before prompt status evaluated if false' do
|
94
|
+
prompt = new_prompt(:speak => 'hello', :length => 1, :bargein => false)
|
95
|
+
call.input << '1'
|
96
|
+
em do
|
97
|
+
after_callback prompt
|
98
|
+
prompt.prompt_finished?.should be_false
|
99
|
+
done
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should not clear input before prompt status evaluated if true' do
|
104
|
+
prompt = new_prompt(:speak => 'hello', :length => 1, :bargein => true) { done }
|
105
|
+
call.input << '1'
|
106
|
+
em do
|
107
|
+
after_callback prompt
|
108
|
+
end
|
109
|
+
end
|
82
110
|
end
|
83
|
-
end
|
84
111
|
|
85
|
-
context "after callback" do
|
86
112
|
context "input completed" do
|
87
113
|
it "should not add timers if reached length" do
|
88
114
|
prompt = new_prompt
|
@@ -146,9 +172,10 @@ describe Larynx::Prompt do
|
|
146
172
|
end
|
147
173
|
|
148
174
|
it "should clear input" do
|
175
|
+
call.input << '1'
|
149
176
|
prompt = new_prompt
|
150
|
-
call.should_receive(:clear_input)
|
151
177
|
prompt.finalise
|
178
|
+
call.input.should be_empty
|
152
179
|
end
|
153
180
|
end
|
154
181
|
|
metadata
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: larynx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 5
|
10
|
+
version: 0.1.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Adam Meehan
|
14
|
-
autorequire:
|
14
|
+
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-09-03 00:00:00 +10:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -107,6 +107,7 @@ files:
|
|
107
107
|
- lib/larynx/prompt.rb
|
108
108
|
- lib/larynx/response.rb
|
109
109
|
- lib/larynx/restartable_timer.rb
|
110
|
+
- lib/larynx/server.rb
|
110
111
|
- lib/larynx/session.rb
|
111
112
|
- lib/larynx/version.rb
|
112
113
|
- lib/larynx.rb
|
@@ -122,6 +123,7 @@ files:
|
|
122
123
|
- spec/larynx/command_spec.rb
|
123
124
|
- spec/larynx/eventmachince_spec.rb
|
124
125
|
- spec/larynx/fields_spec.rb
|
126
|
+
- spec/larynx/form_spec.rb
|
125
127
|
- spec/larynx/prompt_spec.rb
|
126
128
|
- spec/larynx_spec.rb
|
127
129
|
- spec/spec_helper.rb
|