nera 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +37 -0
  3. data/README.rdoc +47 -0
  4. data/Rakefile +27 -0
  5. data/bin/nera +29 -0
  6. data/bin/nera_addsim +30 -0
  7. data/lib/nera/nera_cui.rb +417 -0
  8. data/lib/nera/nera_database.rb +281 -0
  9. data/lib/nera/nera_db_folders.rb +226 -0
  10. data/lib/nera/nera_dialog.rb +205 -0
  11. data/lib/nera/nera_job_layer_controller.rb +237 -0
  12. data/lib/nera/nera_job_records.rb +111 -0
  13. data/lib/nera/nera_job_script.rb +202 -0
  14. data/lib/nera/nera_parameter_layer_controller.rb +157 -0
  15. data/lib/nera/nera_parameter_records.rb +186 -0
  16. data/lib/nera/nera_run_layer_controller.rb +192 -0
  17. data/lib/nera/nera_run_records.rb +184 -0
  18. data/lib/nera/nera_simulator.rb +26 -0
  19. data/lib/nera/nera_simulator_layer_controller.rb +66 -0
  20. data/lib/nera/nera_simulator_records.rb +84 -0
  21. data/lib/nera.rb +25 -0
  22. data/lib/nera_addsim/make_simulator.rb +307 -0
  23. data/scripts/make_manifest.rb +21 -0
  24. data/test/runner.rb +3 -0
  25. data/test/test_helper.rb +52 -0
  26. data/test/test_nera_database.rb +221 -0
  27. data/test/test_nera_db_folders.rb +209 -0
  28. data/test/test_nera_dialog.rb +134 -0
  29. data/test/test_nera_job_layer_controller.rb +132 -0
  30. data/test/test_nera_job_records.rb +260 -0
  31. data/test/test_nera_parameter_layer_controller.rb +188 -0
  32. data/test/test_nera_parameter_records.rb +285 -0
  33. data/test/test_nera_run_layer_controller.rb +171 -0
  34. data/test/test_nera_run_records.rb +290 -0
  35. data/test/test_nera_simulator.rb +26 -0
  36. data/test/test_nera_simulator_layer_controller.rb +54 -0
  37. data/test/test_nera_simulator_records.rb +140 -0
  38. metadata +125 -0
@@ -0,0 +1,281 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+ #require 'filelock'
4
+ require 'pstore'
5
+ require 'pp'
6
+
7
+ #* list of public methods
8
+ #- initialize( path_to_filename )
9
+ #
10
+ #- self.create_table( path_to_basename )
11
+ #--- method to create new table
12
+ #--- ex. NERA::Database.create_table( "./db")
13
+ #
14
+ #- add( new_rec )
15
+ #--- add a new record to the table.
16
+ #--- new_rec must be specified by a Hash.
17
+ #--- ex. db.add( { :key1 => 'test', :key2 => DateTime.now, ... } )
18
+ #--- You don't need to specify the ':id' key. If it is specified, it is omitted.
19
+ #--- During the transaction, files are locked with exclusive locks.
20
+ #--- return true if the addition succeeded.
21
+ #--- raise RuntimeError if the specified hash is not valid.
22
+ #
23
+ #- find_by_id( id )
24
+ #--- returns records in Hash.
25
+ #--- id can be Integer, Array of Integers, or Range of Integers.
26
+ #--- During reading file, the files are locked with shared locks.
27
+ #--- If there are several records, it returns Array of Hash.
28
+ #
29
+ #- find_all { |rec| (condition) }
30
+ #--- returns the array of the matched records.
31
+ #--- return nil if no record matched.
32
+ #
33
+ #- update( rec )
34
+ #--- update a record
35
+ #--- ex. db.update( {:id=>3, :key1=>'updated', :key2=>DateTime.now} )
36
+ #--- returns nil if the record of the specified 'id' does not exist.
37
+ #
38
+ #- destroy( id )
39
+ #--- destroy records
40
+ #--- id may be an Integer, Array of Integers, Range
41
+ #--- if the specified record is not found in the table, return nil.
42
+ #
43
+ #- transaction { ..(transaction).. }
44
+ #--- all the database files are exclusively locked during block is processed.
45
+ #--- database files are unlocked at the end of the block. terminates.
46
+ #--- sample -----------
47
+ #
48
+ # db.transaction do
49
+ # arr = db.find_all do |record|
50
+ # record[:id] > 5
51
+ # end
52
+ #
53
+ # arr.each do |rec| rec[:state] = finished end
54
+ # db.update( arr)
55
+ # end
56
+ #
57
+ #--- end of sample ---
58
+ #
59
+ #
60
+ #
61
+ module NERA
62
+ class Database
63
+ # name of the file
64
+ @filename
65
+
66
+ # true if files are exclusively locked
67
+ @in_transaction
68
+
69
+ # PStore object
70
+ @pstore
71
+
72
+ # search a data file in this order
73
+ # classes of values
74
+ VALUE_CLASSES = [ String, Symbol, Integer, Float, Date, DateTime, TrueClass, FalseClass, NilClass ]
75
+
76
+ public
77
+ # ----------------------------------
78
+ def initialize( path_to_file )
79
+ @filename = path_to_file
80
+ unless File.exist?(@filename)
81
+ raise "No such database : #{@filename}"
82
+ end
83
+ end
84
+
85
+ # ------------------------------------
86
+ def transaction
87
+ if @in_transaction # nothing happens when the process is already in transaction
88
+ yield
89
+ return nil
90
+ end
91
+ @pstore = PStore.new(@filename)
92
+ @pstore.transaction( false) do
93
+ begin
94
+ @in_transaction = true
95
+ yield
96
+ ensure
97
+ @in_transaction = false
98
+ nil
99
+ end
100
+ end
101
+ end
102
+
103
+ private
104
+ # ------------------------------------
105
+ def readonly_transaction
106
+ if @in_transaction
107
+ yield
108
+ return nil
109
+ end
110
+ @pstore = PStore.new(@filename)
111
+ @pstore.transaction(true) do
112
+ yield
113
+ end
114
+ end
115
+
116
+ public
117
+ # ------------------------------------
118
+ def self.create_table( filename)
119
+ if File.exists?( filename)
120
+ $stderr.puts "Database #{filename} already exists."
121
+ return nil
122
+ end
123
+ db = PStore.new( filename)
124
+ db.transaction(false) do
125
+ db[:max_id] = 0
126
+ end
127
+ return true
128
+ end
129
+
130
+ public
131
+ # ------------------------------------
132
+ def add( new_rec )
133
+ unless valid_as_a_record?( new_rec)
134
+ raise ArgumentError, "This record is not valid."
135
+ end
136
+ new_id = nil
137
+ transaction do
138
+ new_id = @pstore[:max_id] + 1
139
+ new_rec[:id] = new_id
140
+ @pstore[:max_id] = new_id
141
+ @pstore[new_id] = new_rec
142
+ end
143
+ return new_id
144
+ end
145
+
146
+ private
147
+ # ------------------------------------------
148
+ def valid_as_a_record?( rec)
149
+ # check validity
150
+ raise "rec must be a Hash" unless rec.is_a?(Hash)
151
+ rec.each do |key,val|
152
+ unless VALUE_CLASSES.find do |type| val.is_a?(type) end
153
+ $stderr.puts "Types of this record is not valid : #{key}, #{val}, #{val.class.to_s}"
154
+ return false
155
+ end
156
+ end
157
+ return true
158
+ end
159
+
160
+ public
161
+ # --- find methods --------------------------
162
+ def find_by_id( ids)
163
+ if ids.is_a?(Integer)
164
+ readonly_transaction do
165
+ return @pstore[ids]
166
+ end
167
+ elsif ids.is_a?(Array)
168
+ found = []
169
+ readonly_transaction do
170
+ # find all ----
171
+ ids.each do |id|
172
+ found << @pstore[id]
173
+ end
174
+ return found
175
+ end
176
+ elsif ids.is_a?(Range)
177
+ found = []
178
+ readonly_transaction do
179
+ matched_ids = @pstore.roots.find_all do |id|
180
+ ids.include?(id)
181
+ end
182
+ return nil unless matched_ids
183
+ matched_ids.sort! do |id1,id2|
184
+ id1 <=> id2
185
+ end
186
+ matched_ids.each do |id|
187
+ found << @pstore[id]
188
+ end
189
+ found = nil if found.size == 0
190
+ return found
191
+ end
192
+ else
193
+ raise ArgumentError, "Argument of the find method must be Integer, Array or Range. : #{ids.class}"
194
+ end
195
+ end
196
+
197
+ public
198
+ # --- find all ---
199
+ # usage: find_all { ..(condition).. }
200
+ def find_all
201
+ matched = []
202
+ readonly_transaction do
203
+ all_ids = @pstore.roots
204
+ all_ids.delete( :max_id)
205
+ all_ids.each do |id|
206
+ if yield @pstore[id].dup
207
+ matched << @pstore[id]
208
+ end
209
+ end
210
+ end
211
+ matched.sort! do |rec1,rec2|
212
+ rec1[:id] <=> rec2[:id]
213
+ end
214
+ matched = nil if matched.size == 0
215
+ return matched
216
+ end
217
+
218
+ public
219
+ # --- update method ---
220
+ #- update( rec )
221
+ #--- update a record
222
+ #--- ex. db.update( {:id=>3, :key1=>'updated', :key2=>DateTime.now} )
223
+ #--- returns nil if the record of the specified 'id' does not exist.
224
+ def update( rec)
225
+ unless valid_as_a_record?( rec)
226
+ $stderr.puts "This record is not valid."
227
+ return nil
228
+ end
229
+ id_to_update = rec[:id]
230
+ flag = nil
231
+ transaction do
232
+ unless @pstore.root?( id_to_update)
233
+ return nil
234
+ end
235
+ @pstore[id_to_update] = rec
236
+ flag = true
237
+ end
238
+ return flag
239
+ end
240
+
241
+ public
242
+ #- destroy( id )
243
+ #--- destroy records
244
+ #--- id may be an Integer, Array of Integers, Range
245
+ #--- if the specified record is not found in the table, return nil.
246
+ def destroy( ids)
247
+ if ids.is_a?( Array)
248
+ status = []
249
+ transaction do
250
+ ids.each do |id|
251
+ status << destroy( id)
252
+ end
253
+ end
254
+ return status
255
+ elsif ids.is_a?( Range)
256
+ flag = nil
257
+ transaction do
258
+ id_array = @pstore.roots.find_all do |id|
259
+ ids.include?(id)
260
+ end
261
+ flag = destroy( id_array)
262
+ end
263
+ flag = flag.find do |f| f end
264
+ return flag
265
+ elsif ids.is_a?( Integer)
266
+ stat = nil
267
+ transaction do
268
+ stat = @pstore.delete(ids)
269
+ end
270
+ return stat
271
+ else
272
+ raise ArgumentError, "argument must be specified by Integer, Array of Integer, or Range. : #{ids.class}"
273
+ end
274
+ return true
275
+ end
276
+
277
+ end
278
+ # ------ end of Database class --------
279
+
280
+ end
281
+ # ------ end of NERA module --------
@@ -0,0 +1,226 @@
1
+ require 'fileutils'
2
+ require 'nera_simulator_records'
3
+ require 'nera_job_records'
4
+
5
+
6
+ module NERA
7
+
8
+ # This class contains the information of the folder structure
9
+ class DbFolders
10
+
11
+ #--------------------------------------
12
+ #-class methods -----------------------
13
+ #--------------------------------------
14
+
15
+ def initialize( db_folder)
16
+ if db_folder =~ /\/$/
17
+ @db_folder = db_folder
18
+ else
19
+ @db_folder = db_folder + '/'
20
+ end
21
+ unless valid_db?( @db_folder)
22
+ raise RuntimeError, "Database #{@db_folder} is not a valid database folder."
23
+ end
24
+
25
+ # load simulator classes
26
+ Dir.glob( "#{@db_folder}/Simulator_classes/*.rb").each do |lib|
27
+ require lib
28
+ end
29
+ end
30
+
31
+ def valid_db?( fold)
32
+ return false unless FileTest.directory?( fold)
33
+ return false unless FileTest.directory?( fold + "Jobs/")
34
+ return false unless FileTest.directory?( fold + "Jobs/Include/")
35
+ return false unless FileTest.directory?( fold + "Tables/")
36
+ return false unless FileTest.directory?( fold + "Simulator_classes/")
37
+ return false unless FileTest.exist?( fold + "Tables/simulators.pstore")
38
+ return false unless FileTest.exist?( fold + "Tables/jobs.pstore")
39
+ return true
40
+ end
41
+ private :valid_db?
42
+
43
+ def self.create_db( db_folder)
44
+ if FileTest.directory?( db_folder)
45
+ raise RuntimeError, "Folder #{db_folder} already exists."
46
+ end
47
+
48
+ if db_folder =~ /\/$/
49
+ db = db_folder
50
+ else
51
+ db = db_folder + '/'
52
+ end
53
+ FileUtils.mkdir( db, :verbose => true)
54
+ FileUtils.mkdir( db + "Jobs/", :verbose => true)
55
+ FileUtils.mkdir( db + "Jobs/Include/", :verbose => true)
56
+ FileUtils.mkdir( db + "Tables/", :verbose => true)
57
+ FileUtils.mkdir( db + "Simulator_classes/", :verbose => true)
58
+ f = NERA::SimulatorRecords.create_table( db + "Tables/simulators.pstore")
59
+ raise "must not happen" unless f
60
+ f = NERA::JobRecords.create_table( db + "Tables/jobs.pstore")
61
+ raise "must not happen" unless f
62
+ File.open( db + 'simulators.yml', 'w') do |io|
63
+ YAML.dump( [], io )
64
+ io.flush
65
+ end
66
+ File.open( db + 'Jobs/jobs.yml', 'w') do |io|
67
+ YAML.dump( [], io )
68
+ io.flush
69
+ end
70
+
71
+ return true
72
+ end
73
+
74
+ #--------------------------------------
75
+ #-public instance methods--------------
76
+ #--------------------------------------
77
+ public
78
+ def path_to_simulator_layer
79
+ return @db_folder
80
+ end
81
+
82
+ def path_to_simulators_table
83
+ return "#{@db_folder}Tables/simulators.pstore"
84
+ end
85
+
86
+ def path_to_simulators_yaml
87
+ return "#{@db_folder}simulators.yml"
88
+ end
89
+
90
+ def create_new_simulator_folder( sim_class)
91
+ unless sim_class.superclass == NERA::Simulator
92
+ raise ArgumentError, "must be a subclass of NERA::Simulator"
93
+ end
94
+ name = sim_class.to_s
95
+ name += '/'
96
+
97
+ if FileTest.directory?( @db_folder + name) or FileTest.directory?( @db_folder + "Tables/" + name)
98
+ return nil
99
+ end
100
+ FileUtils.mkdir( @db_folder + name, :verbose => true)
101
+ FileUtils.mkdir( @db_folder + "Tables/" + name, :verbose => true)
102
+
103
+ File.open( path_to_parameters_yaml( sim_class) , 'w') do |io|
104
+ YAML.dump( [], io )
105
+ io.flush
106
+ end
107
+
108
+ return true
109
+ end
110
+
111
+ def path_to_parameter_layer( sim_class)
112
+ unless sim_class.superclass == NERA::Simulator
113
+ raise ArgumentError, "must be a subclass of NERA::Simulator"
114
+ end
115
+ name = sim_class.to_s
116
+ name += '/'
117
+
118
+ path = @db_folder + name
119
+ if FileTest.directory?(path)
120
+ return path
121
+ else
122
+ return nil
123
+ end
124
+ end
125
+
126
+ def path_to_parameters_table( sim_class)
127
+ unless sim_class.superclass == NERA::Simulator
128
+ raise ArgumentError, "must be a subclass of NERA::Simulator"
129
+ end
130
+ name = sim_class.to_s
131
+ name += '/'
132
+
133
+ path = @db_folder + 'Tables/' + name + 'parameters.pstore'
134
+ unless FileTest.directory?( @db_folder + 'Tables/' + name)
135
+ return nil
136
+ end
137
+ return path
138
+ end
139
+
140
+ def path_to_parameters_yaml( sim_class)
141
+ return path_to_parameter_layer( sim_class) + 'parameters.yml'
142
+ end
143
+
144
+ def create_new_parameter_folder( sim_class, param_id)
145
+ p = path_to_parameter_layer( sim_class)
146
+ return nil unless p
147
+
148
+ if param_id.to_i > 99999 or param_id.to_i <= 0
149
+ raise RuntimeError, "The specified parameter id #{param_id} is out of range."
150
+ end
151
+ fold_name = sprintf("%05d/", param_id.to_i)
152
+ if FileTest.directory?( p + fold_name)
153
+ return nil
154
+ end
155
+ FileUtils.mkdir( p + fold_name)
156
+ p = path_to_parameters_table( sim_class)
157
+ p.sub!(/parameters.pstore$/, fold_name)
158
+ FileUtils.mkdir( p)
159
+
160
+ File.open( path_to_runs_yaml( sim_class, param_id) , 'w') do |io|
161
+ YAML.dump( [], io )
162
+ io.flush
163
+ end
164
+
165
+ return true
166
+ end
167
+
168
+ def path_to_run_layer( sim_class, param_id)
169
+ p = path_to_parameter_layer( sim_class)
170
+ return nil unless p
171
+ if param_id.to_i > 99999 or param_id.to_i <= 0
172
+ return nil
173
+ end
174
+ fold_name = sprintf("%05d/", param_id.to_i)
175
+ path = p + fold_name
176
+ return path
177
+ end
178
+
179
+ def path_to_runs_table( sim_class, param_id)
180
+ ppt = path_to_parameters_table( sim_class)
181
+ return nil unless ppt
182
+ if param_id.to_i > 99999 or param_id.to_i <= 0
183
+ return nil
184
+ end
185
+ fold_name = sprintf("%05d/", param_id.to_i)
186
+ path = ppt.sub(/parameters.pstore$/, fold_name)
187
+ raise "must not happen" unless path
188
+ return path + 'runs.pstore'
189
+ end
190
+
191
+ def path_to_runs_yaml( sim_class, param_id)
192
+ return path_to_run_layer( sim_class, param_id) + 'runs.yml'
193
+ end
194
+
195
+ def path_to_job_layer
196
+ return @db_folder + "Jobs/"
197
+ end
198
+
199
+ def path_to_jobs_table
200
+ return @db_folder + "Tables/jobs.pstore"
201
+ end
202
+
203
+ def path_to_jobs_yaml
204
+ return path_to_job_layer + "jobs.yml"
205
+ end
206
+
207
+ def path_to_job_script(job_id)
208
+ filename = sprintf("j%06d.sh", job_id)
209
+ return path_to_job_layer + filename
210
+ end
211
+
212
+ def path_to_include_layer
213
+ return @db_folder + "Jobs/Include/"
214
+ end
215
+
216
+ def search_include_files
217
+ return Dir.glob( path_to_include_layer + "j[0-9][0-9][0-9][0-9][0-9][0-9]*.tar.bz2")
218
+ end
219
+
220
+ def path_to_finished_jobs_file
221
+ return @db_folder + "Tables/finished_jobs.yml"
222
+ end
223
+
224
+ end
225
+ end
226
+
@@ -0,0 +1,205 @@
1
+ # -*- coding: utf-8 -*-
2
+ require "readline"
3
+
4
+ # module for CUI interactive interface
5
+ #
6
+ module Dialog
7
+
8
+ PROMPT = "> "
9
+
10
+ # ----------------
11
+ # Select one from the list specified by the array.
12
+ def select_one( arr, message = "Input the number", default = 0)
13
+ unless arr.is_a?(Array) and arr.size >= 1 and default.to_i >= 0 and default.to_i < arr.size
14
+ raise ArgumentError, "Arguments are not valid."
15
+ end
16
+
17
+ while true
18
+ $stdout.puts( "#{message} : [#{default.to_s}]")
19
+ arr.size.times do |i|
20
+ puts( "[#{i}]:\t#{arr[i]}")
21
+ end
22
+ buf = Readline.readline(PROMPT).chomp.strip
23
+ buf = default.to_s if buf==''
24
+
25
+ unless buf.match(/^[0-9]+$/)
26
+ $stdout.puts "The input #{buf} is not valid. Try again"
27
+ redo
28
+ end
29
+ num = Integer(buf)
30
+ if num >= 0 and num < arr.size
31
+ valid_input = true
32
+ return num
33
+ end
34
+ $stdout.puts "Your input is not valid. Try again."
35
+ end
36
+ end
37
+
38
+ # ---------------
39
+ def select_one_or_return_string( arr, message = "Input the number", default = 0)
40
+ unless arr.is_a?(Array) and arr.size >= 1
41
+ raise ArgumentError, "Arguments are not valid."
42
+ end
43
+
44
+ while true
45
+ $stdout.puts( "#{message} : [#{default.to_s}]")
46
+ arr.size.times do |i|
47
+ puts( "[#{i}]:\t#{arr[i]}")
48
+ end
49
+ buf = Readline.readline(PROMPT).chomp.strip
50
+ buf = default.to_s if buf==''
51
+
52
+ if buf.match(/^[0-9]+$/)
53
+ num = Integer(buf)
54
+ if num >= 0 and num < arr.size
55
+ valid_input = true
56
+ return num
57
+ end
58
+ else
59
+ return buf
60
+ end
61
+ $stdout.puts "Your input is not valid. Try again."
62
+ end
63
+ end
64
+
65
+ # ---------------
66
+ def select_many( arr, message = "Select multiple numbers (ex. 1,3,5-8)", default = 0)
67
+ unless arr.is_a?(Array) and arr.size >= 1
68
+ raise ArgumentError, "Arguments are not valid."
69
+ end
70
+
71
+ result = []
72
+ valid_input = nil
73
+ until valid_input
74
+ $stdout.puts( "#{message} : [#{default}]")
75
+ arr.size.times do |i|
76
+ puts( "[#{i}]:\t#{arr[i]}")
77
+ end
78
+
79
+ buf = Readline.readline(PROMPT).chomp
80
+ buf = default.to_s if buf==''
81
+
82
+ valid_input = true
83
+ buf = buf.split(',').map do |e| e.strip end
84
+ buf.each do |e|
85
+ if e.match(/^[0-9]+$/)
86
+ num = Integer(e)
87
+ if num < 0 or num >= arr.size
88
+ valid_input = nil
89
+ break
90
+ else
91
+ result << num
92
+ end
93
+ elsif e.match(/^[0-9]+-[0-9]+$/)
94
+ f = e.split('-')
95
+ from = Integer(f[0])
96
+ to = Integer(f[1])
97
+ if from >= to
98
+ valid_input = nil
99
+ break
100
+ end
101
+ for i in from..to
102
+ result << i if i > 0 and i <= arr.size
103
+ end
104
+ else
105
+ valid_input = nil
106
+ break
107
+ end
108
+ end
109
+ $stdout.puts "The input #{buf} is not valid. Try again." unless valid_input
110
+ end
111
+ return result
112
+ end
113
+
114
+ # ---------------
115
+ def message( str)
116
+ $stdout.puts str
117
+ end
118
+
119
+ # ---------------
120
+ def set_multiple_values( arr, message = "Input the values")
121
+ unless arr.is_a?(Array)
122
+ raise ArgumentError, "Arguments are not valid."
123
+ end
124
+
125
+ result = Hash.new
126
+ arr.each do |a|
127
+ unless a.is_a?(Array) and a.size == 3
128
+ raise ArgumentError, "Arguments are not valid."
129
+ end
130
+ key = a[0]
131
+ type = a[1]
132
+ val = a[2]
133
+ unless ( type == Integer or type == Float or type == String ) and val.is_a?(type)
134
+ raise ArgumentError, "Arguments are not valid."
135
+ end
136
+ result[key] = val
137
+ end
138
+ unless arr.size == result.size
139
+ raise ArgumentError, "There is a nonunique key."
140
+ end
141
+
142
+ ok = nil
143
+ until ok
144
+ # show the list
145
+ arr.each_with_index do |elem,i|
146
+ $stdout.puts "[#{i}] :\t#{elem[0]}, #{elem[1]}, #{result[elem[0]]}"
147
+ end
148
+ $stdout.puts "[#{arr.size}] : \tOK"
149
+ # input
150
+ buf = Readline.readline(PROMPT).chomp.strip
151
+ unless buf.match(/^[0-9]+$/)
152
+ redo
153
+ end
154
+ input = buf.to_i
155
+ if input >= 0 and input < arr.size
156
+ $stdout.puts "Input #{arr[input][0]} (#{arr[input][1]})"
157
+ buf = Readline.readline(PROMPT).chomp.strip
158
+ if arr[input][1] == Integer
159
+ begin
160
+ result[ arr[input][0] ] = Integer(buf)
161
+ rescue
162
+ $stdout.puts "The specified value is not convertible to Integer"
163
+ end
164
+ elsif arr[input][1] == Float
165
+ begin
166
+ result[ arr[input][0] ] = Float(buf)
167
+ rescue
168
+ $stdout.puts "The specified value is not convertible to Float"
169
+ end
170
+ elsif arr[input][1] == String
171
+ result[ arr[input][0] ] = buf.to_s
172
+ end
173
+ elsif input == arr.size
174
+ ok = true
175
+ end
176
+ end
177
+ return result
178
+ end
179
+
180
+ # ---------------
181
+ def get_integer( message = "Input a integer", default = 0)
182
+ unless default.is_a?(Integer)
183
+ raise ArgumentError, "Arguments are not valid."
184
+ end
185
+
186
+ while true
187
+ $stdout.puts( "#{message} : [#{default}]")
188
+
189
+ buf = Readline.readline(PROMPT).chomp.strip
190
+ buf = default.to_s if buf==''
191
+ unless buf.match(/^[0-9]+$/)
192
+ $stdout.puts "The input #{buf} is not valid. Try again"
193
+ redo
194
+ end
195
+ num = Integer(buf)
196
+ if yield num
197
+ return num
198
+ else
199
+ $stdout.puts "The input #{buf} does not match the condition. Try again"
200
+ end
201
+ end
202
+ end
203
+
204
+ module_function :select_one, :select_one_or_return_string, :select_many, :message, :set_multiple_values, :get_integer
205
+ end