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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 719e8649e44771d53e0bca18acd408340159a58b
4
- data.tar.gz: 7e59f4a97ad922d23a819bc73b5f28e9db79f711
3
+ metadata.gz: 42418b61ff38c853438626c7bc70ad533ce728d7
4
+ data.tar.gz: 8cc0c02b300bd52c9d15969edd95f88468717645
5
5
  SHA512:
6
- metadata.gz: 9ef54e39d4cac9503534f0b66cc378f8992c6427868087f8901ea7c72e8491a3a852a66a5bbb5918499f47831dbe3c54e147794a68f3890cace9b5587c04b7e5
7
- data.tar.gz: 80e5523952bf696fe7bae610f839d187d23643575013da129778203cb58c57fa793a6bee856545e5f625282461f0fb5e4f2e70a35fa0f6da45baee2b4e21ef68
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
- # This exposes the shortened API for `A.sk` and `S.ay`
18
+ # For the name-spaced methods CliMiami::A.sk & CliMiami::S.ay...
19
19
  gem 'cli_miami'
20
20
 
21
- # if you need the API to be namespaced (`CliMiami::A.sk`, `CliMiami::S.ay`)
22
- gem 'cli_miami', :require => :namespaced
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|ljust|rjust] # The type of justification to use
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
- enter_end_value: 'Enter end value'
7
- enter_start_value: 'Enter starting value'
8
- enter_value_for: 'Enter a value for %{key}'
9
- no_description: No description provided
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: "`%{value}` cannot be converted to `%{type}`. Allowed values are: `%{allowed_values}` (%{description})"
14
- length: "`%{value}` has a length of %{value_length}, but must be between %{min} and %{max} . Allowed values are: `%{allowed_values}` (%{description})"
15
- regexp: "`%{value}` does not match the required format. Allowed values are: `%{allowed_values}` (%{description})"
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: "Your array (%{value}) has %{value_length} items, but needs between %{allowed_values} (%{description})"
26
+ length: "You entered %{value_length} items, but you need between %{allowed_values} %{description}"
22
27
  boolean:
23
28
  file:
24
- validate: "`%{value}` is not a valid path. Allowed values are: `%{allowed_values}` (%{description})"
29
+ validate: "%{value} is not a valid path %{description}"
25
30
  fixnum:
26
- length: "`%{value}` must be between %{min} and %{max} . Allowed values are: `%{allowed_values}` (%{description})"
31
+ length: "%{value} is invalid. Enter a number between %{allowed_values} %{description}"
27
32
  float:
28
- length: "`%{value}` must be between %{min} and %{max} . Allowed values are: `%{allowed_values}` (%{description})"
33
+ length: "%{value} is invalid. Enter a number between %{allowed_values} %{description}"
29
34
  hash:
30
- length: "Your object (%{value}) has %{value_length} items, but needs between %{allowed_values} (%{description})"
31
- keys: "The object has `%{value}` set, but is missing `%{keys}`. Allowed values are: `%{allowed_values}` (%{description})"
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, :cli_miami_success
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.to_s, :cli_miami_success
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
- CliMiami::S.ay I18n.t('cli_miami.core.enter_value_for', key: user_key), :cli_miami_success
151
- hash[user_key] = request_until_valid value_options, true
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.to_s, :cli_miami_success
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: :cli_miami_success, newline: false
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: :cli_miami_success, newline: false
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
@@ -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 Style/ClosingParenthesisIndentation
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(options,
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(options,
95
+ ) || i18n_lookup(
96
+ options,
95
97
  'cli_miami',
96
98
  'errors',
97
99
  *error_keys
98
100
  )
99
101
  end
100
- # rubocop:enable Style/ClosingParenthesisIndentation
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
- I18n.t i18n_string, options if I18n.exists? i18n_string
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
@@ -0,0 +1,5 @@
1
+ require 'cli_miami'
2
+
3
+ # create global aliases to support calling `A.sk` and `S.ay` directly
4
+ ::A = CliMiami::A
5
+ ::S = CliMiami::S
data/lib/cli_miami/say.rb CHANGED
@@ -37,7 +37,7 @@ class CliMiami::S
37
37
  private
38
38
 
39
39
  def initialize text, options
40
- @text = text
40
+ @text = text.to_s
41
41
  @options = options
42
42
 
43
43
  # formatter methods
@@ -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
- # DO NOT CHANGE THIS FILE FOR CORE CLI_MIAMI INIT FUNCTIONS!
2
+ # load dependencies
3
3
  #
4
- # This file is used only for augmenting the core classes with global aliases.
5
- # This lets users use CLI Miami without having to pollute their global namespace
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
- # the core initilization runs from `namespaced.rb`
8
- require 'namespaced'
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
- # create alias classes to support `A.sk` and `S.ay` syntax
11
- A = CliMiami::A
12
- S = CliMiami::S
202
+ options
203
+ end
204
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
205
+ end
@@ -26,6 +26,9 @@ en:
26
26
  hash:
27
27
  spec: "hash: %{value} - %{allowed_values}"
28
28
 
29
+ multiple_choice:
30
+ spec: "multiple_choice: %{value} - %{allowed_values}"
31
+
29
32
  range:
30
33
  spec: "range: %{value} - %{allowed_values}"
31
34
 
@@ -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
 
@@ -4,7 +4,7 @@ describe CliMiami::Error do
4
4
  CliMiami::TYPE_MAP.values.uniq.each do |type|
5
5
  case type
6
6
 
7
- when :array
7
+ when :array, :multiple_choice
8
8
  value_type = [1, nil, :foo, '', (1..2)]
9
9
  value_string = '1, [EMPTY], foo, [EMPTY], and 1-2'
10
10
  allowed_values = '1-38'
@@ -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'
@@ -1,5 +1,21 @@
1
1
  describe CliMiami do
2
- subject { CliMiami }
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
- bg_color: :blue
35
+ bgcolor: :blue
20
36
 
21
37
  subject.set_preset :bar,
22
38
  preset: :foo,
23
- bg_color: :green
39
+ bgcolor: :green
24
40
 
25
41
  expect(subject.class_variable_get(:@@presets)[:bar]).to eq(
26
42
  color: :red,
27
- bg_color: :green
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
- bg_color: 'blue'
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
- bg_color: 'blue'
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
- bg_color: 'blue'
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
- bg_color: 'blue',
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, bg_color: 'green')).to include(
98
+ expect(subject.get_options(preset: :foo, bgcolor: 'green')).to include(
83
99
  color: 'red',
84
- bg_color: 'green'
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.3.pre
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-02-26 00:00:00.000000000 Z
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
@@ -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