cli_miami 1.0.3.pre → 1.0.5.pre

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