input_reader 0.0.1 → 0.1.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.
- checksums.yaml +7 -0
- data/.rspec +2 -0
- data/.travis.yml +12 -0
- data/Guardfile +23 -0
- data/README.md +7 -2
- data/Rakefile +11 -1
- data/input_reader.gemspec +8 -0
- data/lib/input_reader.rb +87 -86
- data/lib/input_reader/builder.rb +155 -0
- data/lib/input_reader/version.rb +1 -1
- data/spec/input_reader_spec.rb +134 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/coverage_loader.rb +26 -0
- metadata +118 -13
- data/lib/input_reader/input_reader.rb +0 -140
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d098ae0ddf4e3d2f9db106f0be0bf882bf94e2a4
|
4
|
+
data.tar.gz: b4de31045b953ed310ac4a536bd493de042296db
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2672ce79bb63b0c0f0205ee1fb5248ea5fd3d4b454caca26490dea17d2ead7befbd139a43da732dea4630a152e103e5e555d39a0f333ea80d88946868276e2f1
|
7
|
+
data.tar.gz: 1eb8bda20b4c47fb2a3e5bf63e2ddebd83c6352b96ad5397b1ead08bec3551de27adec6edb366586330054ac3bbbc3b89fe35a71b3a72f6c058756f91d59af19
|
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.2
|
4
|
+
- 2.3.0
|
5
|
+
script: "bundle exec rake spec"
|
6
|
+
notifications:
|
7
|
+
email:
|
8
|
+
- support@travellink.com.au
|
9
|
+
flowdock:
|
10
|
+
secure: PGMmGX4ppcqe1JlyJH4A51zAPX23okFNqu5KKcpYYTrZQ5KsCctzmGshiXX0PUOqFRPXiOc9+qhR0A6UmW7Hd5toocm9XjysODE6aho1FJMh/VNLK5sK8JSGlideoP8RYadgfJjZjDaLwcgj4da46kfLugA9VqdsfAM1LroWA+E=
|
11
|
+
sudo: false
|
12
|
+
cache: bundler
|
data/Guardfile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
guard 'rspec', :version => 2 do
|
4
|
+
watch(%r{^spec/.+_spec\.rb$})
|
5
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
6
|
+
watch('spec/spec_helper.rb') { "spec" }
|
7
|
+
|
8
|
+
# Rails example
|
9
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
10
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
11
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
12
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
13
|
+
watch('config/routes.rb') { "spec/routing" }
|
14
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
15
|
+
|
16
|
+
# Capybara request specs
|
17
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
|
18
|
+
|
19
|
+
# Turnip features and steps
|
20
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
21
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
22
|
+
end
|
23
|
+
|
data/README.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
-
|
1
|
+
InputReader
|
2
|
+
===========
|
2
3
|
|
3
|
-
|
4
|
+
[](https://travis-ci.org/sealink/input_reader)
|
5
|
+
[](https://gemnasium.com/sealink/input_reader)
|
6
|
+
[](https://codeclimate.com/github/sealink/input_reader)
|
7
|
+
|
8
|
+
Reads and parses input and helps build input menus, etc.
|
4
9
|
|
5
10
|
## Installation
|
6
11
|
|
data/Rakefile
CHANGED
@@ -1,2 +1,12 @@
|
|
1
|
-
#!/usr/bin/env rake
|
2
1
|
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
desc 'Default: run specs.'
|
4
|
+
task :default => :spec
|
5
|
+
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
|
8
|
+
desc "Run specs"
|
9
|
+
RSpec::Core::RakeTask.new do |t|
|
10
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
11
|
+
# Put spec opts in a file named .rspec in root
|
12
|
+
end
|
data/input_reader.gemspec
CHANGED
@@ -14,4 +14,12 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.name = "input_reader"
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = InputReader::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'rake'
|
19
|
+
gem.add_development_dependency 'rspec'
|
20
|
+
gem.add_development_dependency 'simplecov'
|
21
|
+
gem.add_development_dependency 'simplecov-rcov'
|
22
|
+
gem.add_development_dependency 'guard-rspec'
|
23
|
+
gem.add_development_dependency 'coveralls'
|
24
|
+
gem.add_development_dependency 'travis'
|
17
25
|
end
|
data/lib/input_reader.rb
CHANGED
@@ -1,50 +1,54 @@
|
|
1
1
|
require "input_reader/version"
|
2
2
|
|
3
3
|
module InputReader
|
4
|
-
require 'input_reader/
|
4
|
+
require 'input_reader/builder'
|
5
5
|
|
6
6
|
class << self
|
7
|
-
def
|
8
|
-
|
7
|
+
def boolean_true_values
|
8
|
+
%w{y yes t true 1}
|
9
9
|
end
|
10
10
|
|
11
|
+
def boolean_false_values
|
12
|
+
%w{n no f false 0}
|
13
|
+
end
|
11
14
|
|
12
|
-
def
|
13
|
-
|
15
|
+
def get_input(options = {})
|
16
|
+
InputReader::Builder.new(options).get_input
|
14
17
|
end
|
15
18
|
|
19
|
+
def get_string(options = {})
|
20
|
+
self.get_input_with_exception_handling(options)
|
21
|
+
end
|
16
22
|
|
17
|
-
def get_boolean(options =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
options[:validators] = [{:message => "Invalid input given. Valid values are #{all_values.join(', ')}",
|
23
|
-
:validator => lambda { |input| all_values.include?(input.to_s.downcase) } }]
|
24
|
-
options[:prompt] ||= "(Y/N)?"
|
23
|
+
def get_boolean(options = {})
|
24
|
+
all_values = boolean_true_values + boolean_false_values
|
25
|
+
options[:validators] = [{:message => "Invalid input given. Valid values are #{all_values.join(', ')}",
|
26
|
+
:validator => lambda { |input| all_values.include?(input.to_s.downcase) }}]
|
27
|
+
options[:prompt] ||= '(Y/N)?'
|
25
28
|
input = self.get_input(options)
|
26
|
-
|
29
|
+
if boolean_true_values.include?(input.to_s.downcase)
|
30
|
+
true
|
31
|
+
elsif boolean_false_values.include?(input.to_s.downcase)
|
32
|
+
false
|
33
|
+
else
|
34
|
+
nil
|
35
|
+
end
|
27
36
|
end
|
28
37
|
|
29
|
-
|
30
|
-
def get_int(options = nil)
|
38
|
+
def get_int(options = {})
|
31
39
|
self.get_and_parse_input(:to_i, options)
|
32
40
|
end
|
33
41
|
|
34
|
-
|
35
|
-
|
36
|
-
self.get_and_parse_input(:to_date, options)
|
42
|
+
def get_date(options = {})
|
43
|
+
self.get_and_parse_input(lambda { |d| Date.parse(d) }, options)
|
37
44
|
end
|
38
45
|
|
39
|
-
|
40
|
-
|
41
|
-
self.get_and_parse_input(:to_datetime, options)
|
46
|
+
def get_datetime(options = {})
|
47
|
+
self.get_and_parse_input(lambda { |dt| DateTime.parse(dt) }, options)
|
42
48
|
end
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
options ||= {}
|
47
|
-
array = []
|
50
|
+
def get_array(options = {})
|
51
|
+
array = []
|
48
52
|
input_options = options.merge(:allow_blank => true)
|
49
53
|
while input = self.get_input_with_exception_handling(input_options)
|
50
54
|
array << input
|
@@ -52,107 +56,104 @@ module InputReader
|
|
52
56
|
array
|
53
57
|
end
|
54
58
|
|
55
|
-
|
56
|
-
def get_array_of_ints(options = nil)
|
59
|
+
def get_array_of_ints(options = {})
|
57
60
|
self.get_and_parse_array(:to_i, options)
|
58
61
|
end
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
self.get_and_parse_array(:to_date, options)
|
63
|
+
def get_array_of_dates(options = {})
|
64
|
+
self.get_and_parse_array(lambda { |d| Date.parse(d) }, options)
|
63
65
|
end
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
self.get_and_parse_array(:to_datetime, options)
|
67
|
+
def get_array_of_datetimes(options = {})
|
68
|
+
self.get_and_parse_array(lambda { |dt| DateTime.parse(dt) }, options)
|
68
69
|
end
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
options ||= {}
|
73
|
-
options[:parsers] = Array.wrap(options[:parsers]) + Array.wrap(parsers)
|
71
|
+
def get_and_parse_array(parsers, options = {})
|
72
|
+
options[:parsers] = Array(options[:parsers]) + Array(parsers)
|
74
73
|
get_array(options)
|
75
74
|
end
|
76
75
|
|
77
|
-
|
78
|
-
|
79
|
-
options ||= {}
|
80
|
-
options[:parsers] = Array.wrap(options[:parsers]) + Array.wrap(parsers)
|
76
|
+
def get_and_parse_input(parsers, options = {})
|
77
|
+
options[:parsers] = Array(options[:parsers]) + Array(parsers)
|
81
78
|
self.get_input_with_exception_handling(options)
|
82
79
|
end
|
83
80
|
|
84
|
-
|
85
|
-
def get_input_with_exception_handling(options = nil)
|
86
|
-
options ||= {}
|
81
|
+
def get_input_with_exception_handling(options = {})
|
87
82
|
valid_input = false
|
88
83
|
while !valid_input
|
89
84
|
begin
|
90
85
|
input = self.get_input(options)
|
91
86
|
valid_input = true
|
87
|
+
rescue Interrupt
|
88
|
+
raise
|
92
89
|
rescue Exception => e
|
93
|
-
raise e if e.is_a?(Interrupt)
|
94
90
|
puts e.message
|
95
91
|
end
|
96
92
|
end
|
97
93
|
input
|
98
94
|
end
|
99
95
|
|
96
|
+
def prompt_choices(items, selection_attribute = nil)
|
97
|
+
items.each.with_index do |item, i|
|
98
|
+
option = if selection_attribute.is_a?(String) || selection_attribute.is_a?(Symbol)
|
99
|
+
item.send(selection_attribute)
|
100
|
+
elsif selection_attribute.is_a?(Proc)
|
101
|
+
selection_attribute.call(item)
|
102
|
+
else
|
103
|
+
item
|
104
|
+
end
|
105
|
+
puts "#{i + 1}. #{option}"
|
106
|
+
end
|
107
|
+
end
|
100
108
|
|
101
|
-
def select_item(items, options =
|
102
|
-
options ||= {}
|
109
|
+
def select_item(items, options = {})
|
103
110
|
prompt_choices(items, options[:selection_attribute])
|
104
111
|
input = get_int({
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
112
|
+
:valid_values => (1..items.size).to_a,
|
113
|
+
:allow_blank => options[:allow_blank],
|
114
|
+
:prompt => options[:prompt] || 'Choice: '
|
115
|
+
})
|
116
|
+
return nil if input.nil?
|
117
|
+
items[input - 1]
|
110
118
|
end
|
111
119
|
|
112
|
-
|
113
|
-
def select_items(items, options = nil)
|
114
|
-
options ||= {}
|
120
|
+
def select_items(items, options = {})
|
115
121
|
prompt_choices(items, options[:selection_attribute])
|
116
122
|
puts "#{items.size + 1}. All"
|
117
123
|
input = get_input({
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
124
|
+
:parsers => [lambda { |input|
|
125
|
+
choices = input.strip.gsub(/\s*,\s*/, ',').split(',').map(&:to_i)
|
126
|
+
if choices.include?(items.size + 1)
|
127
|
+
choices = (1..items.size).to_a
|
128
|
+
end
|
129
|
+
choices
|
130
|
+
}],
|
131
|
+
:validators => [lambda { |input| input.all? { |i| i > 0 && i <= items.size } }],
|
132
|
+
:allow_blank => options[:allow_blank],
|
133
|
+
:prompt => options[:prompt] || 'Choices (separate with comma): '
|
134
|
+
})
|
135
|
+
return [] if input.nil?
|
136
|
+
input.map { |c| items[c - 1] }
|
130
137
|
end
|
131
138
|
|
132
|
-
|
133
139
|
def confirmation_required(messages = '')
|
134
|
-
puts
|
135
|
-
puts
|
136
|
-
puts "[Messages]"
|
140
|
+
puts '-' * 110
|
141
|
+
puts '[Messages]'
|
137
142
|
puts messages
|
138
|
-
puts
|
139
|
-
|
140
|
-
print "Are you sure you want to do this? (Y/N): "
|
141
|
-
user_answer = STDIN.gets.chomp.upcase
|
143
|
+
puts '-' * 110
|
144
|
+
print 'Are you sure? (Y/N): '
|
142
145
|
|
143
|
-
|
144
|
-
|
145
|
-
else
|
146
|
-
if user_answer == 'Y'
|
146
|
+
case STDIN.gets.chomp.upcase
|
147
|
+
when 'Y'
|
147
148
|
puts
|
148
149
|
yield
|
149
|
-
|
150
|
-
puts
|
151
|
-
puts
|
152
|
-
|
150
|
+
puts '----------------------'
|
151
|
+
puts 'Operation completed!!'
|
152
|
+
puts '----------------------'
|
153
|
+
when 'N'
|
154
|
+
puts 'Operation aborted on user prompt'
|
153
155
|
else
|
154
|
-
puts
|
155
|
-
end
|
156
|
+
puts 'Please enter a valid response. Operation aborted.'
|
156
157
|
end
|
157
158
|
end
|
158
159
|
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module InputReader
|
2
|
+
class Builder
|
3
|
+
|
4
|
+
attr_reader :input
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@prompt = options[:prompt]
|
8
|
+
@allow_blank = options[:allow_blank]
|
9
|
+
@default_value = options[:default_value]
|
10
|
+
@valid_values = Array(options[:valid_values])
|
11
|
+
@validators = Array(options[:validators])
|
12
|
+
@unparsed_input_validators = Array(options[:unparsed_input_validators])
|
13
|
+
@parsed_input_validators = Array(options[:parsed_input_validators])
|
14
|
+
@parsers = Array(options[:parsers])
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def get_input
|
19
|
+
begin
|
20
|
+
$stdout.print "#{@prompt} "
|
21
|
+
flush
|
22
|
+
read_input
|
23
|
+
rescue Exception => e
|
24
|
+
puts e.message + e.class.inspect
|
25
|
+
exit
|
26
|
+
end while !valid_input?
|
27
|
+
@input
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def read
|
34
|
+
$stdin.gets.chomp
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def flush
|
39
|
+
$stdout.flush
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def read_input
|
44
|
+
@input = read
|
45
|
+
@input = @default_value if blank_input?
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
def validate_and_parse_input
|
50
|
+
return false unless validate_unparsed_input
|
51
|
+
return true if blank_input?
|
52
|
+
parse_input
|
53
|
+
validate_parsed_input
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def valid_input?
|
58
|
+
validate_and_parse_input
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def blank_input?
|
63
|
+
blank?(@input)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def blank?(input)
|
68
|
+
case input
|
69
|
+
when NilClass then true
|
70
|
+
when String, Array then input.length == 0
|
71
|
+
else false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def validate_unparsed_input
|
77
|
+
validators = [
|
78
|
+
{
|
79
|
+
:validator => lambda { |input| @allow_blank || !blank?(input) },
|
80
|
+
:message => 'No input given'
|
81
|
+
},
|
82
|
+
] + @unparsed_input_validators
|
83
|
+
process_validations(validators)
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def validate_parsed_input
|
88
|
+
validators = [
|
89
|
+
{
|
90
|
+
:validator => lambda { |input| blank?(@valid_values) || @valid_values.include?(input) },
|
91
|
+
:message => "Invalid input given. Valid values are #{@valid_values.join(', ')}"
|
92
|
+
}
|
93
|
+
] + @parsed_input_validators + @validators
|
94
|
+
process_validations(validators)
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def process_validations(validators)
|
99
|
+
validators.each do |validator|
|
100
|
+
valid = process_validation(validator)
|
101
|
+
|
102
|
+
if !valid
|
103
|
+
puts error_message_for(validator)
|
104
|
+
return false
|
105
|
+
end
|
106
|
+
end
|
107
|
+
true
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
def process_validation(validator)
|
112
|
+
validator_proc = validator_proc_for(validator)
|
113
|
+
case validator_proc
|
114
|
+
when Proc
|
115
|
+
validator_proc.call(@input)
|
116
|
+
when String, Symbol
|
117
|
+
@input.send(validator_proc)
|
118
|
+
else
|
119
|
+
true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
def validator_proc_for(validator)
|
125
|
+
if validator.is_a?(Proc)
|
126
|
+
validator
|
127
|
+
elsif validator.is_a?(Hash)
|
128
|
+
validator[:validator]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def error_message_for(validator)
|
134
|
+
if validator.is_a?(Hash) && validator[:message]
|
135
|
+
validator[:message]
|
136
|
+
else
|
137
|
+
'Invalid input'
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
def parse_input
|
143
|
+
@parsers.each do |parser|
|
144
|
+
case parser
|
145
|
+
when Proc
|
146
|
+
@input = parser.call(@input)
|
147
|
+
when String, Symbol
|
148
|
+
@input = @input.send(parser)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
@input
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
data/lib/input_reader/version.rb
CHANGED
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'input_reader'
|
4
|
+
|
5
|
+
describe InputReader do
|
6
|
+
it 'should read strings' do
|
7
|
+
expect_output ' '
|
8
|
+
input '1'
|
9
|
+
InputReader.get_input.should == '1'
|
10
|
+
|
11
|
+
expect_output 'number '
|
12
|
+
input '2'
|
13
|
+
InputReader.get_input(:prompt => 'number').should == '2'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should read integers' do
|
17
|
+
input '1'
|
18
|
+
InputReader.get_int.should == 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should read booleans' do
|
22
|
+
input 'y'
|
23
|
+
InputReader.get_boolean.should be true
|
24
|
+
|
25
|
+
input 'F'
|
26
|
+
InputReader.get_boolean.should be false
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should handle invalid boolean values' do
|
30
|
+
input 'x'
|
31
|
+
input 'y'
|
32
|
+
InputReader.get_boolean.should be true
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should handle dates' do
|
36
|
+
input '2012-01-13'
|
37
|
+
InputReader.get_date.should == Date.new(2012, 1, 13)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should handle date times' do
|
41
|
+
input '2012-01-13 18:45'
|
42
|
+
InputReader.get_datetime.should == DateTime.new(2012, 1, 13, 18, 45, 0)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should handle arrays' do
|
46
|
+
input ['1', '2', '3', '']
|
47
|
+
InputReader.get_array.should == ['1', '2', '3']
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should handle array of ints' do
|
51
|
+
input ['1', '2', '3', '']
|
52
|
+
InputReader.get_array_of_ints.should == [1, 2, 3]
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should handle array of dates' do
|
56
|
+
input ['2012-01-13', '2012-07-24', '']
|
57
|
+
InputReader.get_array_of_dates.should == [
|
58
|
+
Date.new(2012, 1, 13),
|
59
|
+
Date.new(2012, 7, 24)
|
60
|
+
]
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should handle array of date times' do
|
64
|
+
input ['2012-01-13 14:45', '2012-07-24 19:59', '']
|
65
|
+
InputReader.get_array_of_datetimes.should == [
|
66
|
+
DateTime.new(2012, 1, 13, 14, 45, 0),
|
67
|
+
DateTime.new(2012, 7, 24, 19, 59)
|
68
|
+
]
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should allow selecting between choices' do
|
72
|
+
input '2'
|
73
|
+
InputReader.select_item(['Alpha', 'Beta', 'Gamma']).should == 'Beta'
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should return nil if no choice is selected' do
|
77
|
+
input ''
|
78
|
+
InputReader.select_item(['Alpha', 'Beta', 'Gamma'],
|
79
|
+
:allow_blank => true).should == nil
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should handle selecting between multiple choices' do
|
83
|
+
objects = [
|
84
|
+
double(:name => 'Alpha'),
|
85
|
+
double(:name => 'Beta'),
|
86
|
+
double(:name => 'Gamma')
|
87
|
+
]
|
88
|
+
input '1,3'
|
89
|
+
InputReader.select_items(objects, :selection_attribute => :name).should ==
|
90
|
+
[objects[0], objects[2]]
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should return an empty array if no choices are selected' do
|
94
|
+
objects = [
|
95
|
+
double(:name => 'Alpha'),
|
96
|
+
double(:name => 'Beta'),
|
97
|
+
double(:name => 'Gamma')
|
98
|
+
]
|
99
|
+
input ''
|
100
|
+
InputReader.select_items(objects,
|
101
|
+
:selection_attribute => :name,
|
102
|
+
:allow_blank => true).should == []
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should allow confirming' do
|
106
|
+
input 'N'
|
107
|
+
block = lambda{ }
|
108
|
+
block.should_not_receive(:call)
|
109
|
+
InputReader.confirmation_required do
|
110
|
+
block.call
|
111
|
+
end
|
112
|
+
|
113
|
+
input 'Y'
|
114
|
+
block = lambda{ }
|
115
|
+
block.should_receive(:call)
|
116
|
+
InputReader.confirmation_required do
|
117
|
+
block.call
|
118
|
+
end
|
119
|
+
|
120
|
+
input 'X'
|
121
|
+
block = lambda{ }
|
122
|
+
block.should_not_receive(:call)
|
123
|
+
InputReader.confirmation_required do
|
124
|
+
block.call
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should validate unparsed input' do
|
129
|
+
input ['1', '2']
|
130
|
+
InputReader.get_int(
|
131
|
+
:parsed_input_validators => [{:validator => :even?}]
|
132
|
+
).should == 2
|
133
|
+
end
|
134
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
config.filter_run :focus
|
11
|
+
|
12
|
+
# Run specs in random order to surface order dependencies. If you find an
|
13
|
+
# order dependency and want to debug it, you can fix the order by providing
|
14
|
+
# the seed, which is printed after each run.
|
15
|
+
# --seed 1234
|
16
|
+
config.order = 'random'
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'support/coverage_loader'
|
20
|
+
|
21
|
+
def expect_output(output)
|
22
|
+
$stdout.should_receive(:print).with(output)
|
23
|
+
end
|
24
|
+
|
25
|
+
def input(input)
|
26
|
+
if input.is_a?(String)
|
27
|
+
$stdin.should_receive(:gets).once.and_return(input)
|
28
|
+
elsif input.is_a?(Array)
|
29
|
+
$stdin.should_receive(:gets).exactly(input.size).times.and_return(*input)
|
30
|
+
else
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'input_reader'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
MINIMUM_COVERAGE = 94
|
2
|
+
|
3
|
+
unless ENV['COVERAGE'] == 'off'
|
4
|
+
require 'simplecov'
|
5
|
+
require 'simplecov-rcov'
|
6
|
+
require 'coveralls'
|
7
|
+
Coveralls.wear!
|
8
|
+
|
9
|
+
SimpleCov.formatters = [
|
10
|
+
SimpleCov::Formatter::RcovFormatter,
|
11
|
+
Coveralls::SimpleCov::Formatter
|
12
|
+
]
|
13
|
+
SimpleCov.start do
|
14
|
+
add_filter '/vendor/'
|
15
|
+
add_filter '/spec/'
|
16
|
+
add_group 'lib', 'lib'
|
17
|
+
end
|
18
|
+
SimpleCov.at_exit do
|
19
|
+
SimpleCov.result.format!
|
20
|
+
percent = SimpleCov.result.covered_percent
|
21
|
+
unless percent >= MINIMUM_COVERAGE
|
22
|
+
puts "Coverage must be above #{MINIMUM_COVERAGE}%. It is #{"%.2f" % percent}%"
|
23
|
+
Kernel.exit(1)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
metadata
CHANGED
@@ -1,16 +1,113 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: input_reader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Alessandro Berardi, Michael Noack
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
11
|
+
date: 2016-06-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov-rcov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: travis
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
14
111
|
description: Command line helpers to read input of various types, confirm, etc.
|
15
112
|
email:
|
16
113
|
- support@travellink.com.au
|
@@ -18,37 +115,45 @@ executables: []
|
|
18
115
|
extensions: []
|
19
116
|
extra_rdoc_files: []
|
20
117
|
files:
|
21
|
-
- .gitignore
|
118
|
+
- ".gitignore"
|
119
|
+
- ".rspec"
|
120
|
+
- ".travis.yml"
|
22
121
|
- Gemfile
|
122
|
+
- Guardfile
|
23
123
|
- LICENSE
|
24
124
|
- README.md
|
25
125
|
- Rakefile
|
26
126
|
- input_reader.gemspec
|
27
127
|
- lib/input_reader.rb
|
28
|
-
- lib/input_reader/
|
128
|
+
- lib/input_reader/builder.rb
|
29
129
|
- lib/input_reader/version.rb
|
130
|
+
- spec/input_reader_spec.rb
|
131
|
+
- spec/spec_helper.rb
|
132
|
+
- spec/support/coverage_loader.rb
|
30
133
|
homepage: ''
|
31
134
|
licenses: []
|
135
|
+
metadata: {}
|
32
136
|
post_install_message:
|
33
137
|
rdoc_options: []
|
34
138
|
require_paths:
|
35
139
|
- lib
|
36
140
|
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
-
none: false
|
38
141
|
requirements:
|
39
|
-
- -
|
142
|
+
- - ">="
|
40
143
|
- !ruby/object:Gem::Version
|
41
144
|
version: '0'
|
42
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
-
none: false
|
44
146
|
requirements:
|
45
|
-
- -
|
147
|
+
- - ">="
|
46
148
|
- !ruby/object:Gem::Version
|
47
149
|
version: '0'
|
48
150
|
requirements: []
|
49
151
|
rubyforge_project:
|
50
|
-
rubygems_version:
|
152
|
+
rubygems_version: 2.4.8
|
51
153
|
signing_key:
|
52
|
-
specification_version:
|
154
|
+
specification_version: 4
|
53
155
|
summary: Command line helpers to read input, etc.
|
54
|
-
test_files:
|
156
|
+
test_files:
|
157
|
+
- spec/input_reader_spec.rb
|
158
|
+
- spec/spec_helper.rb
|
159
|
+
- spec/support/coverage_loader.rb
|
@@ -1,140 +0,0 @@
|
|
1
|
-
module InputReader
|
2
|
-
class Builder
|
3
|
-
|
4
|
-
attr_reader :input
|
5
|
-
|
6
|
-
def initialize(options = nil)
|
7
|
-
options ||= {}
|
8
|
-
@prompt = options[:prompt]
|
9
|
-
@allow_blank = options[:allow_blank]
|
10
|
-
@default_value = options[:default_value]
|
11
|
-
@valid_values = Array.wrap(options[:valid_values])
|
12
|
-
@validators = Array.wrap(options[:validators])
|
13
|
-
@pre_validators = Array.wrap(options[:pre_validators])
|
14
|
-
@post_validators = Array.wrap(options[:post_validators])
|
15
|
-
@parsers = Array.wrap(options[:parsers])
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
def get_input
|
20
|
-
begin
|
21
|
-
print "#{@prompt} "
|
22
|
-
flush
|
23
|
-
rescue Exception => e
|
24
|
-
puts e.message + e.class.inspect
|
25
|
-
exit
|
26
|
-
end while !valid_input?
|
27
|
-
@input
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
|
34
|
-
def read
|
35
|
-
$stdin.gets.chomp
|
36
|
-
end
|
37
|
-
|
38
|
-
|
39
|
-
def flush
|
40
|
-
$stdout.flush
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
def self.prompt_choices(items, selection_attribute = nil)
|
45
|
-
items.each.with_index do |item,i|
|
46
|
-
option = if selection_attribute.is_a?(String) || selection_attribute.is_a?(Symbol)
|
47
|
-
item.send(selection_attribute)
|
48
|
-
elsif selection_attribute.is_a?(Proc)
|
49
|
-
selection_attribute.call(item)
|
50
|
-
else
|
51
|
-
item
|
52
|
-
end
|
53
|
-
puts "#{i + 1}. #{option}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
|
58
|
-
def valid_input?
|
59
|
-
@input = read
|
60
|
-
@input = @default_value if @input.blank?
|
61
|
-
pre_validate(@input) && (@input.blank? || post_validate(@input = parse(@input)))
|
62
|
-
end
|
63
|
-
|
64
|
-
|
65
|
-
def pre_validate(input)
|
66
|
-
pre_validators = [
|
67
|
-
{
|
68
|
-
:validator => lambda { |input| @allow_blank || !input.blank? },
|
69
|
-
:message => "No input given"
|
70
|
-
},
|
71
|
-
] + @pre_validators
|
72
|
-
validate(input, pre_validators)
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
|
-
def post_validate(input)
|
77
|
-
post_validators = [
|
78
|
-
{
|
79
|
-
:validator => lambda { |input| @valid_values.blank? || @valid_values.include?(input) },
|
80
|
-
:message => "Invalid input given. Valid values are #{@valid_values.join(', ')}"
|
81
|
-
}
|
82
|
-
] + @post_validators + @validators
|
83
|
-
validate(input, post_validators)
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
|
-
def validate(input,validators)
|
88
|
-
|
89
|
-
validators.each do |validator|
|
90
|
-
|
91
|
-
validator_proc = if validator.is_a?(Proc)
|
92
|
-
validator
|
93
|
-
elsif validator.is_a?(Hash)
|
94
|
-
validator[:validator]
|
95
|
-
end
|
96
|
-
|
97
|
-
error_message = if validator.is_a?(Hash) && validator[:message]
|
98
|
-
validator[:message]
|
99
|
-
else
|
100
|
-
"Invalid input"
|
101
|
-
end
|
102
|
-
|
103
|
-
valid = if validator_proc.is_a?(Proc)
|
104
|
-
validator_proc.call(input)
|
105
|
-
elsif validator_proc.is_a?(String) || validator_proc.is_a?(Symbol)
|
106
|
-
input.send(validator_proc)
|
107
|
-
elsif validator_proc.blank?
|
108
|
-
true
|
109
|
-
else
|
110
|
-
false
|
111
|
-
end
|
112
|
-
|
113
|
-
if !valid
|
114
|
-
puts error_message
|
115
|
-
return false
|
116
|
-
end
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
true
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
|
125
|
-
def parse(input)
|
126
|
-
|
127
|
-
@parsers.each do |parser|
|
128
|
-
if parser.is_a?(Proc)
|
129
|
-
input = parser.call(input)
|
130
|
-
elsif parser.is_a?(String) || parser.is_a?(Symbol)
|
131
|
-
input = input.send(parser)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
input
|
136
|
-
|
137
|
-
end
|
138
|
-
|
139
|
-
end
|
140
|
-
end
|