nera 0.0.1

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.
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