cli_miami 1.0.3.pre → 1.0.5.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/i18n/en.yml +21 -13
- data/lib/cli_miami/ask.rb +94 -17
- data/lib/cli_miami/error.rb +17 -6
- data/lib/cli_miami/global.rb +5 -0
- data/lib/cli_miami/say.rb +1 -1
- data/lib/cli_miami/validation.rb +7 -0
- data/lib/cli_miami.rb +201 -8
- data/spec/fixtures/i18n.yml +3 -0
- data/spec/lib/cli_miami/ask_spec.rb +62 -0
- data/spec/lib/cli_miami/error_spec.rb +1 -1
- data/spec/lib/cli_miami/validation_spec.rb +6 -6
- data/spec/lib/cli_miami_spec.rb +26 -10
- metadata +3 -4
- data/lib/cli_miami/core.rb +0 -84
- data/lib/namespaced.rb +0 -89
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42418b61ff38c853438626c7bc70ad533ce728d7
|
4
|
+
data.tar.gz: 8cc0c02b300bd52c9d15969edd95f88468717645
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3df0997f3ba2a4e57859962b3024c3f488a04ffd59fccf3e8eca8d0a29cd5392aa70caac8240abcb7f6b503842e530449e9976a4ea38848b69c28c8902707ebc
|
7
|
+
data.tar.gz: 2aba81627198c963ab0c46d44d2135cb0e7b492fec548927643d5d25251326ce500b87e4368e48e3018c38af5d9c26a2dffc16b559f4601408df380c9b088222
|
data/README.md
CHANGED
@@ -15,11 +15,11 @@ gem install cli_miami
|
|
15
15
|
|
16
16
|
_Gemfile_
|
17
17
|
```ruby
|
18
|
-
#
|
18
|
+
# For the name-spaced methods CliMiami::A.sk & CliMiami::S.ay...
|
19
19
|
gem 'cli_miami'
|
20
20
|
|
21
|
-
#
|
22
|
-
gem 'cli_miami', :
|
21
|
+
# For the friendlier global methods A.sk & S.ay...
|
22
|
+
gem 'cli_miami', require: 'cli_miami/global'
|
23
23
|
```
|
24
24
|
|
25
25
|
#### S.ay
|
@@ -59,7 +59,7 @@ Both `S.ay` and `A.sk` suport the same options
|
|
59
59
|
color: => [symbol] # See ansi color codes below
|
60
60
|
bgcolor: => [symbol] # See ansi color codes below
|
61
61
|
style: => [symbol] # See ansi style codes below. Can accept multiple styles as an array
|
62
|
-
justify: => [center|
|
62
|
+
justify: => [center|left|right] # The type of justification to use
|
63
63
|
padding: => [integer] # The maximum string size to justify text in
|
64
64
|
indent: => [integer] # The number of characters to indent
|
65
65
|
newline: => [boolean] # True if you want a newline after the output
|
data/i18n/en.yml
CHANGED
@@ -3,32 +3,40 @@ en:
|
|
3
3
|
# CliMiami core library strings
|
4
4
|
core:
|
5
5
|
empty: '[EMPTY]'
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
enter_empty_value: Press Enter when finished...
|
7
|
+
enter_end_value: Enter the maximum value
|
8
|
+
enter_start_value: Enter the minimum value
|
9
|
+
enter_value_for: "Enter a value for %{key}"
|
10
|
+
|
11
|
+
# TYPE SPECIFIC MESSAGES
|
12
|
+
multiple_choice:
|
13
|
+
prompt: 'Choose one of the following options:'
|
14
|
+
selected_choices: Selected Options
|
10
15
|
|
11
16
|
errors:
|
12
17
|
# generic error type messages
|
13
|
-
convert: "
|
14
|
-
length: "
|
15
|
-
regexp: "
|
18
|
+
convert: "%{value} cannot be converted to a %{type} %{description}"
|
19
|
+
length: "%{value} has a length of %{value_length}, but must be between %{allowed_values} %{description}"
|
20
|
+
regexp: "%{value} does not match the required format of %{allowed_values} %{description}"
|
16
21
|
type: "Unknown type %{type}"
|
17
22
|
|
18
23
|
# specific value type messages
|
19
24
|
# these messages will take precedence over the general messages above
|
20
25
|
array:
|
21
|
-
length: "
|
26
|
+
length: "You entered %{value_length} items, but you need between %{allowed_values} %{description}"
|
22
27
|
boolean:
|
23
28
|
file:
|
24
|
-
validate: "
|
29
|
+
validate: "%{value} is not a valid path %{description}"
|
25
30
|
fixnum:
|
26
|
-
length: "
|
31
|
+
length: "%{value} is invalid. Enter a number between %{allowed_values} %{description}"
|
27
32
|
float:
|
28
|
-
length: "
|
33
|
+
length: "%{value} is invalid. Enter a number between %{allowed_values} %{description}"
|
29
34
|
hash:
|
30
|
-
length: "
|
31
|
-
keys: "
|
35
|
+
length: "You entered %{value_length} items, but you need between %{allowed_values} %{description}"
|
36
|
+
keys: "You are missing values for %{keys} %{description}"
|
37
|
+
multiple_choice:
|
38
|
+
length: "You have selected %{value_length} options, but you need between %{allowed_values} %{description}"
|
39
|
+
delete_required_key: "You cannot delete the required key %{key}"
|
32
40
|
range:
|
33
41
|
string:
|
34
42
|
symbol:
|
data/lib/cli_miami/ask.rb
CHANGED
@@ -21,11 +21,11 @@ private
|
|
21
21
|
def initialize question, options
|
22
22
|
options = CliMiami.get_options options
|
23
23
|
|
24
|
-
# add description to question
|
25
|
-
question << ' (' << options[:description] << ')'
|
26
|
-
|
27
24
|
# display question
|
28
|
-
CliMiami::S.ay question, options
|
25
|
+
CliMiami::S.ay question, options.merge(preset: :cli_miami_instruction)
|
26
|
+
|
27
|
+
# display description
|
28
|
+
CliMiami::S.ay options[:description], preset: :cli_miami_instruction_sub, indent: 4 if options[:description]
|
29
29
|
|
30
30
|
# request given type to user
|
31
31
|
@value = request_type options
|
@@ -45,7 +45,7 @@ private
|
|
45
45
|
def request_type options
|
46
46
|
send("request_#{options[:type]}", options)
|
47
47
|
rescue
|
48
|
-
CliMiami::S.ay I18n.t('cli_miami.errors.type', options)
|
48
|
+
CliMiami::S.ay I18n.t('cli_miami.errors.type', options.merge(preset: :cli_miami_fail))
|
49
49
|
end
|
50
50
|
|
51
51
|
# for most types, a simple validation check is all that is needed
|
@@ -86,7 +86,7 @@ private
|
|
86
86
|
alias_method :request_string, :request_until_valid
|
87
87
|
alias_method :request_symbol, :request_until_valid
|
88
88
|
|
89
|
-
# rubocop:disable Metrics/MethodLength
|
89
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
90
90
|
def request_array options
|
91
91
|
array = []
|
92
92
|
value_options = CliMiami.get_options(options[:value_options] || {})
|
@@ -96,21 +96,83 @@ private
|
|
96
96
|
while array.length < options[:max]
|
97
97
|
response = request_until_valid value_options, true
|
98
98
|
|
99
|
+
# user attempting to finish entering items
|
99
100
|
if response.empty?
|
100
101
|
break if CliMiami::Validation.new(array, options).valid?
|
101
102
|
redo
|
103
|
+
|
104
|
+
# remove item if user enters it twice
|
105
|
+
elsif array.include? response
|
106
|
+
array.delete response
|
107
|
+
|
108
|
+
# add response to the list
|
102
109
|
else
|
103
110
|
array << response
|
104
111
|
end
|
105
112
|
|
106
113
|
# update user
|
107
|
-
CliMiami::S.ay array.to_sentence, :
|
114
|
+
CliMiami::S.ay array.to_sentence, :cli_miami_update
|
108
115
|
end
|
109
116
|
|
110
117
|
array
|
111
118
|
end
|
119
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
120
|
+
|
121
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
122
|
+
def request_multiple_choice options
|
123
|
+
selected_choices = nil
|
124
|
+
selected_choice_indexes = []
|
125
|
+
|
126
|
+
while selected_choice_indexes.length < options[:max]
|
127
|
+
# quit if all the options have been selected
|
128
|
+
break if selected_choice_indexes.length == options[:choices].length
|
129
|
+
|
130
|
+
# display options
|
131
|
+
CliMiami::S.ay
|
132
|
+
CliMiami::S.ay I18n.t('cli_miami.core.multiple_choice.prompt'), :cli_miami_instruction
|
133
|
+
options[:choices].each_with_index do |li, i|
|
134
|
+
# show already selected choices with a different color
|
135
|
+
choice_text = "#{(i + 1).to_s.rjust(3)}: #{li}"
|
136
|
+
choice_text_color = selected_choice_indexes.include?(i) ? :black : :cyan
|
137
|
+
CliMiami::S.ay choice_text, style: :bright, color: choice_text_color
|
138
|
+
end
|
139
|
+
|
140
|
+
# request a response from the user (return fixnum)
|
141
|
+
response = request_until_valid({
|
142
|
+
type: :fixnum,
|
143
|
+
min: 1,
|
144
|
+
max: options[:choices].length
|
145
|
+
}, true)
|
146
|
+
|
147
|
+
if response == ''
|
148
|
+
break if CliMiami::Validation.new(selected_choice_indexes, options).valid?
|
149
|
+
redo
|
150
|
+
else
|
151
|
+
# convert human readable response to array index
|
152
|
+
response_index = response - 1
|
153
|
+
|
154
|
+
# add choice to array
|
155
|
+
if selected_choice_indexes.include? response_index
|
156
|
+
selected_choice_indexes.delete response_index
|
157
|
+
else
|
158
|
+
selected_choice_indexes << response_index
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# update user
|
163
|
+
CliMiami::S.ay
|
164
|
+
CliMiami::S.ay I18n.t('cli_miami.core.multiple_choice.selected_choices'), :cli_miami_instruction_sub
|
165
|
+
selected_choices = selected_choice_indexes.map{ |i| options[:choices][i] }
|
166
|
+
selected_choices.each do |sc|
|
167
|
+
CliMiami::S.ay "• #{sc}", preset: :cli_miami_update, indent: 3
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
selected_choices
|
172
|
+
end
|
173
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
112
174
|
|
113
|
-
# rubocop:disable Metrics/AbcSize, Metrics/BlockNesting, Metrics/PerceivedComplexity
|
175
|
+
# rubocop:disable Metrics/AbcSize, Metrics/BlockNesting, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
114
176
|
def request_hash options
|
115
177
|
hash = {}
|
116
178
|
options[:keys] ||= []
|
@@ -126,7 +188,7 @@ private
|
|
126
188
|
hash[key.to_sym] = request_until_valid value_options
|
127
189
|
|
128
190
|
# update user
|
129
|
-
CliMiami::S.ay hash.
|
191
|
+
CliMiami::S.ay hash.to_cli_miami_string, :cli_miami_update
|
130
192
|
end
|
131
193
|
|
132
194
|
# set boolean so we know all required keys are set
|
@@ -146,20 +208,35 @@ private
|
|
146
208
|
break if CliMiami::Validation.new(hash, options).valid?
|
147
209
|
redo
|
148
210
|
else
|
211
|
+
CliMiami::S.ay I18n.t('cli_miami.core.enter_value_for', key: user_key), :cli_miami_instruction
|
212
|
+
|
149
213
|
# request value
|
150
|
-
|
151
|
-
|
214
|
+
user_value = request_until_valid value_options, true
|
215
|
+
|
216
|
+
if user_value.empty?
|
217
|
+
if options[:keys].include? user_key
|
218
|
+
# prevent deleting required keys
|
219
|
+
CliMiami::S.ay I18n.t('cli_miami.errors.multiple_choice.delete_required_key', key: user_key), :cli_miami_fail
|
220
|
+
redo
|
221
|
+
else
|
222
|
+
# delete user-specified key
|
223
|
+
hash.delete user_key
|
224
|
+
end
|
225
|
+
else
|
226
|
+
# set user-defined key
|
227
|
+
hash[user_key] = user_value
|
228
|
+
end
|
152
229
|
end
|
153
230
|
end
|
154
231
|
|
155
|
-
CliMiami::S.ay hash.
|
232
|
+
CliMiami::S.ay hash.to_cli_miami_string, :cli_miami_update
|
156
233
|
end
|
157
234
|
|
158
235
|
hash
|
159
236
|
end
|
160
|
-
# rubocop:enable Metrics/AbcSize, Metrics/BlockNesting, Metrics/PerceivedComplexity
|
237
|
+
# rubocop:enable Metrics/AbcSize, Metrics/BlockNesting, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
161
238
|
|
162
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
239
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
163
240
|
def request_range options
|
164
241
|
start_value = nil
|
165
242
|
end_value = nil
|
@@ -167,13 +244,13 @@ private
|
|
167
244
|
|
168
245
|
# get start value
|
169
246
|
until (Float(start_value) rescue nil)
|
170
|
-
CliMiami::S.ay I18n.t('cli_miami.core.enter_start_value'), preset: :
|
247
|
+
CliMiami::S.ay I18n.t('cli_miami.core.enter_start_value'), preset: :cli_miami_instruction, newline: false
|
171
248
|
start_value = request_until_valid range_value_options
|
172
249
|
end
|
173
250
|
|
174
251
|
# get end value
|
175
252
|
until (Float(end_value) rescue nil)
|
176
|
-
CliMiami::S.ay I18n.t('cli_miami.core.enter_end_value'), preset: :
|
253
|
+
CliMiami::S.ay I18n.t('cli_miami.core.enter_end_value'), preset: :cli_miami_instruction, newline: false
|
177
254
|
end_value = request_until_valid range_value_options
|
178
255
|
end
|
179
256
|
|
@@ -190,5 +267,5 @@ private
|
|
190
267
|
request_range options
|
191
268
|
end
|
192
269
|
end
|
193
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
270
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
194
271
|
end
|
data/lib/cli_miami/error.rb
CHANGED
@@ -69,7 +69,7 @@ private
|
|
69
69
|
all_boolean_values = CliMiami::BOOLEAN_TRUE_VALUES + CliMiami::BOOLEAN_FALSE_VALUES
|
70
70
|
all_boolean_values.to_sentence
|
71
71
|
|
72
|
-
when :fixnum, :float, :range, :string, :symbol
|
72
|
+
when :fixnum, :float, :multiple_choice, :range, :string, :symbol
|
73
73
|
"#{options[:min]}-#{options[:max]}"
|
74
74
|
|
75
75
|
when :regexp
|
@@ -83,26 +83,37 @@ private
|
|
83
83
|
|
84
84
|
# build an i18n dot notation string to use for lookup in the language yaml
|
85
85
|
#
|
86
|
-
# rubocop:disable
|
86
|
+
# rubocop:disable Metrics/MethodLength
|
87
87
|
def i18n_lookup_keys options, *error_keys
|
88
88
|
# check for type specific error first, then look for generic errors
|
89
|
-
i18n_lookup(
|
89
|
+
i18n_lookup(
|
90
|
+
options,
|
90
91
|
'cli_miami',
|
91
92
|
'errors',
|
92
93
|
options[:type].to_s,
|
93
94
|
*error_keys
|
94
|
-
) || i18n_lookup(
|
95
|
+
) || i18n_lookup(
|
96
|
+
options,
|
95
97
|
'cli_miami',
|
96
98
|
'errors',
|
97
99
|
*error_keys
|
98
100
|
)
|
99
101
|
end
|
100
|
-
# rubocop:enable
|
102
|
+
# rubocop:enable Metrics/MethodLength
|
101
103
|
|
102
104
|
# use i18n dot notation keys to lookup string
|
103
105
|
#
|
104
106
|
def i18n_lookup options, *keys
|
105
107
|
i18n_string = keys.flatten.compact.each(&:to_s).join('.')
|
106
|
-
|
108
|
+
|
109
|
+
# check if value exists, and translate it
|
110
|
+
if I18n.exists? i18n_string
|
111
|
+
# format description for i18n messages
|
112
|
+
if options[:description]
|
113
|
+
options[:description] = ' (' << options[:description] << ')'
|
114
|
+
end
|
115
|
+
|
116
|
+
I18n.t i18n_string, options
|
117
|
+
end
|
107
118
|
end
|
108
119
|
end
|
data/lib/cli_miami/say.rb
CHANGED
data/lib/cli_miami/validation.rb
CHANGED
@@ -71,6 +71,9 @@ private
|
|
71
71
|
Float string
|
72
72
|
end
|
73
73
|
|
74
|
+
# multiple choice strings are also integers
|
75
|
+
alias_method :convert_string_to_multiple_choice, :convert_string_to_fixnum
|
76
|
+
|
74
77
|
def convert_string_to_range string, _options
|
75
78
|
range_array = string.split('..').map { |i| Float(i) }.sort
|
76
79
|
|
@@ -172,6 +175,10 @@ private
|
|
172
175
|
end
|
173
176
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
174
177
|
|
178
|
+
def validate_multiple_choice selections, options
|
179
|
+
validate_length selections, selections.length, options
|
180
|
+
end
|
181
|
+
|
175
182
|
def validate_range range, options
|
176
183
|
range_diff = (range.max - range.min) + 1
|
177
184
|
validate_length range, range_diff, options
|
data/lib/cli_miami.rb
CHANGED
@@ -1,12 +1,205 @@
|
|
1
1
|
#
|
2
|
-
#
|
2
|
+
# load dependencies
|
3
3
|
#
|
4
|
-
|
5
|
-
|
4
|
+
require 'active_support/core_ext/hash/conversions'
|
5
|
+
require 'active_support/core_ext/hash/keys'
|
6
|
+
require 'active_support/core_ext/string/inflections'
|
7
|
+
require 'i18n'
|
8
|
+
require 'readline'
|
9
|
+
|
10
|
+
# open Hash class
|
11
|
+
#
|
12
|
+
class Hash
|
13
|
+
def to_cli_miami_string
|
14
|
+
map do |k, v|
|
15
|
+
"#{k}: #{v}"
|
16
|
+
end.to_sentence
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# open String class
|
21
|
+
#
|
22
|
+
class String
|
23
|
+
def method_missing method, *args
|
24
|
+
method_string = method.to_s.dup
|
25
|
+
|
26
|
+
if method_string.slice! 'cli_miami_'
|
27
|
+
preset = CliMiami.presets[method_string.to_sym]
|
28
|
+
options = preset.merge args[0] || {}
|
29
|
+
CliMiami::S.ay self, options
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# i18n
|
37
|
+
#
|
38
|
+
# load yml locales included in the cli miami gem
|
39
|
+
# [PATH TO GEMS]/cli_miami/i18n/en.yml
|
40
|
+
I18n.load_path += Dir["#{File.dirname(__FILE__)}/../i18n/*.yml"]
|
41
|
+
# load locale in current directory named `i18n.yml`
|
42
|
+
# ./i18n.yml
|
43
|
+
I18n.load_path += Dir['./i18n.yml']
|
44
|
+
# load locales in the folder `i18n` in the current directory
|
45
|
+
# ./i18n/en.yml
|
46
|
+
I18n.load_path += Dir['./i18n/*.yml']
|
47
|
+
|
48
|
+
# readline
|
49
|
+
#
|
50
|
+
Readline.completion_append_character = '/'
|
51
|
+
|
52
|
+
# creates Boolean type that true/false inherit
|
53
|
+
# this allows true/false to respond to a single Boolean class to check for
|
6
54
|
#
|
7
|
-
#
|
8
|
-
|
55
|
+
# rubocop:disable Style/Documentation
|
56
|
+
module Boolean; end
|
57
|
+
class TrueClass; include Boolean; end
|
58
|
+
class FalseClass; include Boolean; end
|
59
|
+
# rubocop:enable Style/Documentation
|
60
|
+
|
61
|
+
# create Cli Miami namespace and load library
|
62
|
+
#
|
63
|
+
module CliMiami
|
64
|
+
require 'cli_miami/ask'
|
65
|
+
require 'cli_miami/error'
|
66
|
+
require 'cli_miami/say'
|
67
|
+
require 'cli_miami/validation'
|
68
|
+
|
69
|
+
BOOLEAN_TRUE_VALUES = %w(true t yes y).freeze
|
70
|
+
BOOLEAN_FALSE_VALUES = %w(false f no n).freeze
|
71
|
+
|
72
|
+
# keys: type values allowed through the API
|
73
|
+
# values: type used to validate internally in the Validation class
|
74
|
+
TYPE_MAP = {
|
75
|
+
array: :array,
|
76
|
+
boolean: :boolean,
|
77
|
+
dir: :file,
|
78
|
+
directory: :file,
|
79
|
+
file: :file,
|
80
|
+
fixnum: :fixnum,
|
81
|
+
float: :float,
|
82
|
+
folder: :file,
|
83
|
+
multiple_choice: :multiple_choice,
|
84
|
+
hash: :hash,
|
85
|
+
integer: :fixnum,
|
86
|
+
list: :array,
|
87
|
+
number: :fixnum,
|
88
|
+
object: :hash,
|
89
|
+
path: :file,
|
90
|
+
range: :range,
|
91
|
+
string: :string,
|
92
|
+
symbol: :symbol
|
93
|
+
}.freeze
|
94
|
+
|
95
|
+
# default presets
|
96
|
+
# rubocop:disable Style/ClassVars
|
97
|
+
@@presets = {
|
98
|
+
cli_miami_fail: {
|
99
|
+
indent: 2,
|
100
|
+
color: :red,
|
101
|
+
style: [:bold, :underline]
|
102
|
+
},
|
103
|
+
cli_miami_instruction: {
|
104
|
+
color: :green,
|
105
|
+
style: :bold
|
106
|
+
},
|
107
|
+
cli_miami_instruction_sub: {
|
108
|
+
indent: 2,
|
109
|
+
color: :green
|
110
|
+
},
|
111
|
+
cli_miami_success: {
|
112
|
+
color: :green,
|
113
|
+
style: [:bold, :underline]
|
114
|
+
},
|
115
|
+
cli_miami_update: {
|
116
|
+
indent: 2,
|
117
|
+
color: :cyan
|
118
|
+
}
|
119
|
+
}
|
120
|
+
# rubocop:enable Style/ClassVars
|
121
|
+
|
122
|
+
# Create a new custom preset
|
123
|
+
#
|
124
|
+
def self.set_preset type, options
|
125
|
+
raise ArgumentError, 'Preset must be a hash of options' unless options.is_a? Hash
|
126
|
+
|
127
|
+
# extend preset if it exists
|
128
|
+
extend_preset = options.delete(:preset)
|
129
|
+
if extend_preset && @@presets[extend_preset.to_sym]
|
130
|
+
extend_preset_options = @@presets[extend_preset.to_sym]
|
131
|
+
options = extend_preset_options.merge! options
|
132
|
+
end
|
133
|
+
|
134
|
+
# set options to global var
|
135
|
+
@@presets[type] = options
|
136
|
+
end
|
137
|
+
|
138
|
+
# getter for the presets
|
139
|
+
#
|
140
|
+
def self.presets
|
141
|
+
@@presets
|
142
|
+
end
|
143
|
+
|
144
|
+
# build an options hash from preset options and/or additional options
|
145
|
+
#
|
146
|
+
# build shared validation hash object
|
147
|
+
#
|
148
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
149
|
+
def self.get_options user_options = {}
|
150
|
+
# lookup preset if passed in as a symbol, or as a :preset key in an options hash
|
151
|
+
if user_options.is_a? Hash
|
152
|
+
user_options.deep_symbolize_keys!
|
153
|
+
options = @@presets[user_options.delete(:preset)] || {}
|
154
|
+
options.deep_merge! user_options
|
155
|
+
else
|
156
|
+
options = @@presets[user_options.to_sym] || {}
|
157
|
+
end
|
158
|
+
|
159
|
+
# make sure all keys are symbols
|
160
|
+
options.deep_symbolize_keys!
|
161
|
+
|
162
|
+
# set defaults
|
163
|
+
options.reverse_merge!(
|
164
|
+
description: nil,
|
165
|
+
max: Float::INFINITY,
|
166
|
+
min: options.delete(:required) ? 1 : 0,
|
167
|
+
newline: true,
|
168
|
+
justify: :left,
|
169
|
+
padding: 0,
|
170
|
+
preset: nil,
|
171
|
+
regexp: //,
|
172
|
+
style: [],
|
173
|
+
type: :string
|
174
|
+
)
|
175
|
+
|
176
|
+
# prepare style array
|
177
|
+
options[:style] = [options[:style]].flatten
|
178
|
+
|
179
|
+
# lookup type in type map
|
180
|
+
options[:type] = TYPE_MAP[options[:type].to_sym] || :string
|
181
|
+
|
182
|
+
# convert range to min/max values
|
183
|
+
range = options.delete(:range)
|
184
|
+
if range
|
185
|
+
options[:min] = range.min
|
186
|
+
options[:max] = range.max
|
187
|
+
end
|
188
|
+
|
189
|
+
# convert length to min/max values
|
190
|
+
length = options.delete(:length)
|
191
|
+
if length
|
192
|
+
options[:min] = options[:max] = length
|
193
|
+
end
|
194
|
+
|
195
|
+
# if value options are set, apply the same treatment to them
|
196
|
+
value_options = options[:value_options]
|
197
|
+
if value_options
|
198
|
+
options[:value_options] = get_options value_options
|
199
|
+
options[:value_options][:description] = options[:description]
|
200
|
+
end
|
9
201
|
|
10
|
-
|
11
|
-
|
12
|
-
|
202
|
+
options
|
203
|
+
end
|
204
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
205
|
+
end
|
data/spec/fixtures/i18n.yml
CHANGED
@@ -35,6 +35,12 @@ describe CliMiami::A do
|
|
35
35
|
expect(ask.value).to eq %w(foo bar)
|
36
36
|
end
|
37
37
|
|
38
|
+
it 'should allow user to remove entered values' do
|
39
|
+
allow($stdin).to receive(:gets).and_return 'foo', 'bar', 'foo', ''
|
40
|
+
ask = CliMiami::A.sk @q, type: :array
|
41
|
+
expect(ask.value).to eq %w(bar)
|
42
|
+
end
|
43
|
+
|
38
44
|
it 'should prevent user from entering less than the minimum' do
|
39
45
|
allow($stdin).to receive(:gets).and_return 'foo', '', 'bar', ''
|
40
46
|
ask = CliMiami::A.sk @q, type: :array, min: 2
|
@@ -48,7 +54,57 @@ describe CliMiami::A do
|
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
57
|
+
describe 'MULTIPLE_CHOICE' do
|
58
|
+
it 'should allow a user to remove selections' do
|
59
|
+
allow($stdin).to receive(:gets).and_return '1', '2', '1', ''
|
60
|
+
ask = CliMiami::A.sk @q, type: :multiple_choice, choices: ['option 1', 'option 2', 'option 3']
|
61
|
+
expect(ask.value).to eq ['option 2']
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should not allow a user to select less than the min amount of choices' do
|
65
|
+
allow($stdin).to receive(:gets).and_return '2', '', '1', ''
|
66
|
+
ask = CliMiami::A.sk @q, type: :multiple_choice, min: 2, choices: ['option 1', 'option 2', 'option 3']
|
67
|
+
expect(ask.value).to eq ['option 2', 'option 1']
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should not allow a user to select more than the max amount of choices' do
|
71
|
+
allow($stdin).to receive(:gets).and_return '2', '1'
|
72
|
+
ask = CliMiami::A.sk @q, type: :multiple_choice, max: 2, choices: ['option 1', 'option 2', 'option 3']
|
73
|
+
expect(ask.value).to eq ['option 2', 'option 1']
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should not allow a user to select an invalid choice' do
|
77
|
+
allow($stdin).to receive(:gets).and_return '4', '-4', '2'
|
78
|
+
ask = CliMiami::A.sk @q, type: :multiple_choice, max: 1, choices: ['option 1', 'option 2', 'option 3']
|
79
|
+
expect(ask.value).to eq ['option 2']
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should allow a user to select an amount between the min and max' do
|
83
|
+
allow($stdin).to receive(:gets).and_return '3', '1', ''
|
84
|
+
ask = CliMiami::A.sk @q, type: :multiple_choice, min: 1, max: 3, choices: ['option 1', 'option 2', 'option 3']
|
85
|
+
expect(ask.value).to eq ['option 3', 'option 1']
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should allow a user to select all the options' do
|
89
|
+
allow($stdin).to receive(:gets).and_return '3', '1', '2'
|
90
|
+
ask = CliMiami::A.sk @q, type: :multiple_choice, choices: ['option 1', 'option 2', 'option 3']
|
91
|
+
expect(ask.value).to eq ['option 3', 'option 1', 'option 2']
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
51
95
|
describe 'HASH' do
|
96
|
+
it 'should allow user to overwrite keys' do
|
97
|
+
allow($stdin).to receive(:gets).and_return 'foo1', '1', 'bar1', '2', 'foo1', '3', ''
|
98
|
+
ask = CliMiami::A.sk @q, type: :hash
|
99
|
+
expect(ask.value).to eq(foo1: '3', bar1: '2')
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should allow user to remove keys' do
|
103
|
+
allow($stdin).to receive(:gets).and_return 'foo1', '1', 'bar1', '2', 'foo1', '', ''
|
104
|
+
ask = CliMiami::A.sk @q, type: :hash
|
105
|
+
expect(ask.value).to eq(bar1: '2')
|
106
|
+
end
|
107
|
+
|
52
108
|
it 'should allow user to enter keys & values until they enter an empty key string' do
|
53
109
|
allow($stdin).to receive(:gets).and_return 'foo1', '1', 'bar1', '2', ''
|
54
110
|
ask = CliMiami::A.sk @q, type: :hash
|
@@ -73,6 +129,12 @@ describe CliMiami::A do
|
|
73
129
|
ask = CliMiami::A.sk @q, type: :hash, keys: [:foo4]
|
74
130
|
expect(ask.value).to eq(foo4: '1', bar4: '2')
|
75
131
|
end
|
132
|
+
|
133
|
+
it 'should not allow user to delete required keys' do
|
134
|
+
allow($stdin).to receive(:gets).and_return 'foo1', 'foo', '', 'bar', 'bar1', ''
|
135
|
+
ask = CliMiami::A.sk @q, type: :hash, keys: [:foo]
|
136
|
+
expect(ask.value).to eq(foo: 'foo1', bar: 'bar1')
|
137
|
+
end
|
76
138
|
end
|
77
139
|
end
|
78
140
|
|
@@ -27,18 +27,18 @@ describe CliMiami::Validation do
|
|
27
27
|
it_behaves_like 'an invalid type', :file, File.join(Dir.pwd, 'foo')
|
28
28
|
end
|
29
29
|
|
30
|
-
describe 'FLOAT' do
|
31
|
-
it_behaves_like 'a valid type', :float, '2.0', 2.0
|
32
|
-
it_behaves_like 'a valid type', :float, '3', 3.0
|
33
|
-
it_behaves_like 'an invalid type', :float, 'foo'
|
34
|
-
end
|
35
|
-
|
36
30
|
describe 'FIXNUM' do
|
37
31
|
it_behaves_like 'a valid type', :fixnum, '3', 3
|
38
32
|
it_behaves_like 'a valid type', :fixnum, '3.8', 3
|
39
33
|
it_behaves_like 'an invalid type', :fixnum, 'foo'
|
40
34
|
end
|
41
35
|
|
36
|
+
describe 'FLOAT' do
|
37
|
+
it_behaves_like 'a valid type', :float, '2.0', 2.0
|
38
|
+
it_behaves_like 'a valid type', :float, '3', 3.0
|
39
|
+
it_behaves_like 'an invalid type', :float, 'foo'
|
40
|
+
end
|
41
|
+
|
42
42
|
describe 'RANGE' do
|
43
43
|
it_behaves_like 'a valid type', :range, '2..4', (2..4)
|
44
44
|
it_behaves_like 'an invalid type', :range, 'foo'
|
data/spec/lib/cli_miami_spec.rb
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
describe CliMiami do
|
2
|
-
|
2
|
+
describe String do
|
3
|
+
before do
|
4
|
+
CliMiami.set_preset :string_spec,
|
5
|
+
color: :green,
|
6
|
+
bgcolor: :blue
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should create methods for calling presets' do
|
10
|
+
expect($stdout).to receive(:puts).with "\e[44m\e[32mSTRING!\e[0m\e[0m"
|
11
|
+
'STRING!'.cli_miami_string_spec
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should extend presets' do
|
15
|
+
expect($stdout).to receive(:puts).with "\e[43m\e[32mSTRING!\e[0m\e[0m"
|
16
|
+
'STRING!'.cli_miami_string_spec bgcolor: :yellow
|
17
|
+
end
|
18
|
+
end
|
3
19
|
|
4
20
|
describe '.set_preset' do
|
5
21
|
it 'should raise an error if invalid options' do
|
@@ -16,15 +32,15 @@ describe CliMiami do
|
|
16
32
|
it 'should extend an existing preset' do
|
17
33
|
subject.set_preset :foo,
|
18
34
|
color: :red,
|
19
|
-
|
35
|
+
bgcolor: :blue
|
20
36
|
|
21
37
|
subject.set_preset :bar,
|
22
38
|
preset: :foo,
|
23
|
-
|
39
|
+
bgcolor: :green
|
24
40
|
|
25
41
|
expect(subject.class_variable_get(:@@presets)[:bar]).to eq(
|
26
42
|
color: :red,
|
27
|
-
|
43
|
+
bgcolor: :green
|
28
44
|
)
|
29
45
|
end
|
30
46
|
end
|
@@ -33,7 +49,7 @@ describe CliMiami do
|
|
33
49
|
before do
|
34
50
|
subject.set_preset :foo,
|
35
51
|
color: 'red',
|
36
|
-
|
52
|
+
bgcolor: 'blue'
|
37
53
|
end
|
38
54
|
|
39
55
|
it 'should handle `required`' do
|
@@ -49,7 +65,7 @@ describe CliMiami do
|
|
49
65
|
it 'should return the preset options' do
|
50
66
|
expect(subject.get_options(:foo)).to include(
|
51
67
|
color: 'red',
|
52
|
-
|
68
|
+
bgcolor: 'blue'
|
53
69
|
)
|
54
70
|
end
|
55
71
|
end
|
@@ -66,22 +82,22 @@ describe CliMiami do
|
|
66
82
|
it 'should return the preset options' do
|
67
83
|
expect(subject.get_options(preset: :foo)).to include(
|
68
84
|
color: 'red',
|
69
|
-
|
85
|
+
bgcolor: 'blue'
|
70
86
|
)
|
71
87
|
end
|
72
88
|
|
73
89
|
it 'should extend preset options with additional passed options' do
|
74
90
|
expect(subject.get_options(preset: :foo, style: 'bold')).to include(
|
75
91
|
color: 'red',
|
76
|
-
|
92
|
+
bgcolor: 'blue',
|
77
93
|
style: ['bold']
|
78
94
|
)
|
79
95
|
end
|
80
96
|
|
81
97
|
it 'should override preset options with additional passed options' do
|
82
|
-
expect(subject.get_options(preset: :foo,
|
98
|
+
expect(subject.get_options(preset: :foo, bgcolor: 'green')).to include(
|
83
99
|
color: 'red',
|
84
|
-
|
100
|
+
bgcolor: 'green'
|
85
101
|
)
|
86
102
|
end
|
87
103
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cli_miami
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Brewster
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -180,11 +180,10 @@ files:
|
|
180
180
|
- i18n/en.yml
|
181
181
|
- lib/cli_miami.rb
|
182
182
|
- lib/cli_miami/ask.rb
|
183
|
-
- lib/cli_miami/core.rb
|
184
183
|
- lib/cli_miami/error.rb
|
184
|
+
- lib/cli_miami/global.rb
|
185
185
|
- lib/cli_miami/say.rb
|
186
186
|
- lib/cli_miami/validation.rb
|
187
|
-
- lib/namespaced.rb
|
188
187
|
- spec/fixtures/i18n.yml
|
189
188
|
- spec/lib/cli_miami/ask_spec.rb
|
190
189
|
- spec/lib/cli_miami/error_spec.rb
|
data/lib/cli_miami/core.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# 3rd party libraries & configuration
|
3
|
-
#
|
4
|
-
require 'active_support/core_ext/hash/conversions'
|
5
|
-
require 'active_support/core_ext/hash/keys'
|
6
|
-
require 'active_support/core_ext/string/inflections'
|
7
|
-
require 'i18n'
|
8
|
-
require 'readline'
|
9
|
-
|
10
|
-
# core object overrides
|
11
|
-
#
|
12
|
-
class Hash
|
13
|
-
def to_s
|
14
|
-
map do |k, v|
|
15
|
-
"#{k}: #{v}"
|
16
|
-
end.to_sentence
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# i18n
|
21
|
-
#
|
22
|
-
# load yml locales included in the cli miami gem
|
23
|
-
# [PATH TO GEMS]/cli_miami/i18n/en.yml
|
24
|
-
I18n.load_path += Dir["#{File.dirname(__FILE__)}/../i18n/*.yml"]
|
25
|
-
# load locale in current directory named `i18n.yml`
|
26
|
-
# ./i18n.yml
|
27
|
-
I18n.load_path += Dir['./i18n.yml']
|
28
|
-
# load locales in the folder `i18n` in the current directory
|
29
|
-
# ./i18n/en.yml
|
30
|
-
I18n.load_path += Dir['./i18n/*.yml']
|
31
|
-
|
32
|
-
# readline
|
33
|
-
#
|
34
|
-
Readline.completion_append_character = '/'
|
35
|
-
|
36
|
-
# creates Boolean type that true/false inherit
|
37
|
-
# this allows true/false to respond to a single Boolean class to check for
|
38
|
-
#
|
39
|
-
# rubocop:disable Style/Documentation
|
40
|
-
module Boolean; end
|
41
|
-
class TrueClass; include Boolean; end
|
42
|
-
class FalseClass; include Boolean; end
|
43
|
-
# rubocop:enable Style/Documentation
|
44
|
-
|
45
|
-
# global configuration and setup
|
46
|
-
#
|
47
|
-
module CliMiami::Core
|
48
|
-
BOOLEAN_TRUE_VALUES = %w(true t yes y).freeze
|
49
|
-
BOOLEAN_FALSE_VALUES = %w(false f no n).freeze
|
50
|
-
|
51
|
-
# keys: type values allowed through the API
|
52
|
-
# values: type used to validate internally in the Validation class
|
53
|
-
TYPE_MAP = {
|
54
|
-
array: :array,
|
55
|
-
boolean: :boolean,
|
56
|
-
dir: :file,
|
57
|
-
directory: :file,
|
58
|
-
file: :file,
|
59
|
-
fixnum: :fixnum,
|
60
|
-
float: :float,
|
61
|
-
folder: :file,
|
62
|
-
hash: :hash,
|
63
|
-
integer: :fixnum,
|
64
|
-
list: :array,
|
65
|
-
number: :fixnum,
|
66
|
-
object: :hash,
|
67
|
-
path: :file,
|
68
|
-
range: :range,
|
69
|
-
string: :string,
|
70
|
-
symbol: :symbol
|
71
|
-
}.freeze
|
72
|
-
|
73
|
-
# default presets
|
74
|
-
# rubocop:disable Style/ClassVars
|
75
|
-
@@presets = {
|
76
|
-
cli_miami_fail: {
|
77
|
-
color: :red
|
78
|
-
},
|
79
|
-
cli_miami_success: {
|
80
|
-
color: :green
|
81
|
-
}
|
82
|
-
}
|
83
|
-
# rubocop:enable Style/ClassVars
|
84
|
-
end
|
data/lib/namespaced.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
# root namespace class
|
2
|
-
#
|
3
|
-
class CliMiami
|
4
|
-
# require & include core first
|
5
|
-
require 'cli_miami/core'
|
6
|
-
include CliMiami::Core
|
7
|
-
|
8
|
-
# CliMiami library
|
9
|
-
require 'cli_miami/error'
|
10
|
-
require 'cli_miami/validation'
|
11
|
-
require 'cli_miami/ask'
|
12
|
-
require 'cli_miami/say'
|
13
|
-
|
14
|
-
# Create a new custom preset
|
15
|
-
def self.set_preset type, options
|
16
|
-
raise ArgumentError, 'Preset must be a hash of options' unless options.is_a? Hash
|
17
|
-
|
18
|
-
# extend preset if it exists
|
19
|
-
extend_preset = options.delete(:preset)
|
20
|
-
if extend_preset && @@presets[extend_preset.to_sym]
|
21
|
-
extend_preset_options = @@presets[extend_preset.to_sym]
|
22
|
-
options = extend_preset_options.merge! options
|
23
|
-
end
|
24
|
-
|
25
|
-
# set options to global var
|
26
|
-
@@presets[type] = options
|
27
|
-
end
|
28
|
-
|
29
|
-
# build an options hash from preset options and/or additional options
|
30
|
-
#
|
31
|
-
# build shared validation hash object
|
32
|
-
#
|
33
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
34
|
-
def self.get_options user_options = {}
|
35
|
-
# lookup preset if passed in as a symbol, or as a :preset key in an options hash
|
36
|
-
if user_options.is_a? Hash
|
37
|
-
user_options.deep_symbolize_keys!
|
38
|
-
options = @@presets[user_options.delete(:preset)] || {}
|
39
|
-
options.deep_merge! user_options
|
40
|
-
else
|
41
|
-
options = @@presets[user_options.to_sym] || {}
|
42
|
-
end
|
43
|
-
|
44
|
-
# make sure all keys are symbols
|
45
|
-
options.deep_symbolize_keys!
|
46
|
-
|
47
|
-
# set defaults
|
48
|
-
options.reverse_merge!(
|
49
|
-
description: I18n.t('cli_miami.core.no_description'),
|
50
|
-
max: Float::INFINITY,
|
51
|
-
min: options.delete(:required) ? 1 : 0,
|
52
|
-
newline: true,
|
53
|
-
justify: :left,
|
54
|
-
padding: 0,
|
55
|
-
regexp: //,
|
56
|
-
style: [],
|
57
|
-
type: :string
|
58
|
-
)
|
59
|
-
|
60
|
-
# prepare style array
|
61
|
-
options[:style] = [options[:style]].flatten
|
62
|
-
|
63
|
-
# lookup type in type map
|
64
|
-
options[:type] = TYPE_MAP[options[:type].to_sym] || :string
|
65
|
-
|
66
|
-
# convert range to min/max values
|
67
|
-
range = options.delete(:range)
|
68
|
-
if range
|
69
|
-
options[:min] = range.min
|
70
|
-
options[:max] = range.max
|
71
|
-
end
|
72
|
-
|
73
|
-
# convert length to min/max values
|
74
|
-
length = options.delete(:length)
|
75
|
-
if length
|
76
|
-
options[:min] = options[:max] = length
|
77
|
-
end
|
78
|
-
|
79
|
-
# if value options are set, apply the same treatment to them
|
80
|
-
value_options = options[:value_options]
|
81
|
-
if value_options
|
82
|
-
options[:value_options] = get_options value_options
|
83
|
-
options[:value_options][:description] = options[:description]
|
84
|
-
end
|
85
|
-
|
86
|
-
options
|
87
|
-
end
|
88
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
89
|
-
end
|