cliutils 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 63361eba2c187fdc7bd5dbff677bf976fbd97559
4
- data.tar.gz: 7e191c796aee68c3fa9a80641bbea6385bd05520
3
+ metadata.gz: 6f244aff7b8639cf70d06eee8bef9f69d1ccaced
4
+ data.tar.gz: 16845634a0d353037b7c60f200bc76788abc73b7
5
5
  SHA512:
6
- metadata.gz: c07545c990b26e7fb5635e60e919ef886dbdb0bf291cae0425cad9d7aba057da4084076e0e1ac645502ff50cf74a19100140f035c337451f3df21b76e7eb18fe
7
- data.tar.gz: c8bf5139e9b1c610e4ff05961ca794c164ed59d2955b04e6ad212e146b6c2c73f9841c2287e9d6c5174276295868e048105cb43aa8dcfef765fe336407542cc1
6
+ metadata.gz: 78b54b0002d6c07b1189983782b46462c2db1225b573d5924af0d53059f2a01925421c0cc2160b4410021c04d6985f739d2f695c02b6f744d5a1044c2f693807
7
+ data.tar.gz: 769aaa4b4026175ef9c4bcce2aa373639472759f5fcad62df3a90d4169316e6cdccf3f3ddbcde9b41b8d1b923942365716535aefb1edc521c0d19984fe49590f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cliutils (1.1.1)
4
+ cliutils (1.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/HISTORY.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 1.1.0 (2014-04-03)
2
+
3
+ * Changed Requirements => Prerequisites in Prefs
4
+ * Added Validators to Prefs
5
+ * Added Behaviors to Prefs
6
+
1
7
  # 1.1.0 (2014-04-01)
2
8
 
3
9
  * Added notice of valid options in Prefs
data/README.md CHANGED
@@ -169,6 +169,8 @@ messenger.info("You answered: #{ p }")
169
169
  ```
170
170
  ![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prompting.png "Prompting")
171
171
 
172
+ When you pass a default to `messenging.prompt`, hitting `Enter` (i.e., leaving the prompt blank) will return the value of the default.
173
+
172
174
  ### Logging
173
175
 
174
176
  Often, it's desirable to log messages as they appear to your user. `messenging` makes this a breeze by allowing you to attach and detach Logger instances at will.
@@ -180,7 +182,7 @@ messenger.info('This should only appear in STDOUT.')
180
182
 
181
183
  # messenger.attach takes a Hash of string/symbol keys
182
184
  # and Logger values (so you can refer to them later on).
183
- messenger.attach(MY\_FILE\_LOGGER: Logger.new('file.txt'))
185
+ messenger.attach(MY_FILE_LOGGER: Logger.new('file.txt'))
184
186
 
185
187
  messenger.warn('This warning should appear in STDOUT and file.txt')
186
188
  messenger.error('This error should appear in STDOUT and file.txt')
@@ -188,7 +190,7 @@ messenger.debug('This debug message should only appear in file.txt')
188
190
 
189
191
  # messenger.detach takes the string/symbol key
190
192
  # defined earlier.
191
- messenger.detach(:MY\_FILE\_LOGGER)
193
+ messenger.detach(:MY_FILE_LOGGER)
192
194
 
193
195
  messenger.section('This section message should appear only in STDOUT')
194
196
  ```
@@ -209,7 +211,7 @@ Since you can attach Logger objects, each can have it's own format and severity
209
211
 
210
212
  ## Configuration
211
213
 
212
- CLIUtils offers two "things" -- a `Configurator` class and a `Configuration` module that provides access to a shared instance of `Configurator` -- that make managing a user's configuration parameters easy. Mix it in!
214
+ CLIUtils offers a `Configurator` class and a `Configuration` module (which provides access to a shared instance of `Configurator`) that make managing a user's configuration parameters easy. Mix it in!
213
215
 
214
216
  ```Ruby
215
217
  include CLIUtils::Configuration
@@ -263,49 +265,31 @@ user_data:
263
265
 
264
266
  Many times, CLI apps need to ask their users some questions, collect the feedback, validate it, and store it. CLIUtils makes this a breeze via the `Prefs` class.
265
267
 
268
+ ### Basic Schema
269
+
266
270
  `Prefs` can load preferences information from either a YAML file (via a filepath) or from an array of preferences. In either case, the schema is the same; each prompt includes the following:
267
271
 
268
- * prompt (**required**): the string to prompt your user with
269
- * default (*optional*): an optional default to offer
270
- * key (**required**): the key that refers to this preference
271
- * section (**required**): the Configuration section that this preference applies to
272
- * options (*optional*): an optional array of values; the user's choice must be in this array
273
- * requirements (*optional*): an optional list of key/value pairs that must exist for this preference to be displayed
272
+ * `prompt` (**required**): the string to prompt your user with
273
+ * `default` (*optional*): an optional default to offer
274
+ * `config_key` (**required**): the Configurator key that this preference will use
275
+ * `config_section` (**required**): the Configurator section that this preference applies to
274
276
 
275
277
  Here's an example YAML preferences file.
276
278
 
277
279
  ```YAML
278
280
  prompts:
279
- - prompt: What is the hostname of your DD-WRT router?
280
- default: 192.168.1.1
281
- key: hostname
282
- section: ssh_info
283
- - prompt: What is the SSH username of your DD-WRT router?
284
- default: root
285
- key: username
286
- section: ssh_info
287
- - prompt: What SSH port does your DD-WRT router use?
288
- default: 22
289
- key: port
290
- section: ssh_info
291
- - prompt: Do you use password or key authentication?
292
- default: password
293
- key: auth_method
294
- section: ssh_info
295
- options: ['password', 'key']
296
- - prompt: Where is your key located?
297
- default: ~/.ssh
298
- key: key_location
299
- section: ssh_info
300
- requirements:
301
- - key: auth_method
302
- value: key
303
- - prompt: What is your password?
304
- key: password
305
- section: ssh_info
306
- requirements:
307
- - key: auth_method
308
- value: password
281
+ - prompt: What is your name?
282
+ default: Bob Cobb
283
+ config_key: name
284
+ config_section: personal_info
285
+ - prompt: What is your age?
286
+ default: 45
287
+ config_key: age
288
+ config_section: personal_info
289
+ - prompt: Batman or Superman?
290
+ default: Batman
291
+ config_key: superhero
292
+ config_section: personal_info
309
293
  ```
310
294
 
311
295
  Assuming the above, `Prefs` is instantiated like so:
@@ -321,13 +305,137 @@ prefs.ask
321
305
  ```
322
306
  ![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prefs-ask.png "Prefs.ask")
323
307
 
308
+ ### Prerequisites
309
+
310
+ Sometimes, you need to answer certain prompts before others become relevant. `Prefs` allows this via a `prereqs` key, which can contain multiple already-answered key/value pairs to check for. For instance, imagine we want to drill into a user's superhero preference a bit further:
311
+
312
+ ```YAML
313
+ prompts:
314
+ - prompt: Batman or Superman?
315
+ default: Batman
316
+ config_key: superhero
317
+ config_section: personal_info
318
+ - prompt: Do you feel smart for preferring Batman?
319
+ default: Y
320
+ config_key: batman_answer
321
+ config_section: personal_info
322
+ prereqs:
323
+ - config_key: superhero
324
+ config_value: Batman
325
+ - prompt: Why do you prefer Superman?!
326
+ default: No clue
327
+ config_key: superman_answer
328
+ config_section: personal_info
329
+ prereqs:
330
+ - config_key: superhero
331
+ config_value: Superman
332
+ ```
333
+
334
+ `prereqs` checks for already-answered preferences (based on a Configurator key and value); assuming everything checks out, the subsequent preferences are collected:
335
+
336
+ ```Ruby
337
+ prefs.ask
338
+ ```
339
+ ![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prefs-ask-prereqs.png "Prerequisities")
340
+
341
+ Be careful tht you don't define any circular prerequisities (e.g., A requires B and B requires A). In that case, neither preference will be collected.
342
+
343
+ ### Options
344
+
345
+ What if you want to limit a preference to a certain set of options? Easy! Imagine we want to expand the previous example and force the user to choose either "Batman" or "Superman":
346
+
347
+ ```YAML
348
+ prompts:
349
+ - prompt: Batman or Superman?
350
+ default: Batman
351
+ config_key: superhero
352
+ config_section: personal_info
353
+ options: ['Batman', 'Superman']
354
+ - prompt: Do you feel smart for preferring Batman?
355
+ default: Y
356
+ config_key: batman_answer
357
+ config_section: personal_info
358
+ prereqs:
359
+ - config_key: superhero
360
+ config_value: Batman
361
+ - prompt: Why do you prefer Superman?!
362
+ default: No clue
363
+ config_key: superman_answer
364
+ config_section: personal_info
365
+ prereqs:
366
+ - config_key: superhero
367
+ config_value: Superman
368
+ ```
369
+
370
+ Once in place:
371
+
372
+ ```Ruby
373
+ prefs.ask
374
+ ```
375
+ ![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prefs-ask-options.png "Options")
376
+
377
+ ### Validators
378
+
379
+ "But," you say, "I want to ensure that my user gives answers that conform to certain specifications!" Not a problem, dear user, `Prefs` has you covered:
380
+
381
+ ```YAML
382
+ prompts:
383
+ - prompt: What is your name?
384
+ config_key: name
385
+ config_section: personal_info
386
+ validators:
387
+ - alphabetic
388
+ - prompt: How old are you?
389
+ config_key: age
390
+ config_section: personal_info
391
+ validators:
392
+ - numeric
393
+ ```
394
+
395
+ ```Ruby
396
+ prefs.ask
397
+ ```
398
+ ![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prefs-ask-validators.png "Validators")
399
+
400
+ `Prefs` currently supports these validators:
401
+
402
+ * `alphabetic`: must be made up of letters and spaces
403
+ * `alphanumeric`: must be made up of letters, numbers, and spaces
404
+ * `date`: must be a parsable date (e.g., 2014-04-03)
405
+ * `non_nil`: must be a non-nil value
406
+ * `numeric`: must be made up of numbers
407
+ * `url`: must be a fully-qualified URL
408
+
409
+ ### Behaviors
410
+
411
+ Finally, a common desire might be to modify the user's answer in some way:
412
+
413
+ ```YAML
414
+ prompts:
415
+ - prompt: Where is your SSH public key located?
416
+ config_key: pub_key
417
+ config_section: personal_info
418
+ behaviors:
419
+ - local_filepath
420
+ ```
421
+
422
+ This will expand the user's answer as a filepath (e.g., `~/.ssh` will get saved as `/Users/bachya/.ssh/`).
423
+
424
+ `Prefs` currently supports these behaviors:
425
+
426
+ * `local_filepath`: runs File.expand_path on the answer
427
+
428
+ ### Adding Pref Responses to a Configurator
429
+
324
430
  Once the user has answered all the preference prompts, you can fold those answers back into a Configurator using the `ingest` method:
325
431
 
326
432
  ```Ruby
327
- configuration.ingest(prefs)
433
+ configuration.ingest_prefs(prefs)
328
434
  configuration.save
329
435
  ```
330
436
 
437
+ ### Using Configurator Values as Defaults
438
+
331
439
  Note that you can also initialize a `Prefs` object with a Configurator:
332
440
 
333
441
  ```Ruby
@@ -336,10 +444,6 @@ prefs = CLIUtils::Prefs.new('path/to/yaml/file', my_configurator)
336
444
 
337
445
  In this case, `Prefs` will look to see if any values already exist for a specific prompt; if so, that value will be used as the default, rather than the default specified in the YAML.
338
446
 
339
- ### Why a Prefs Class?
340
-
341
- I've written apps that need to request user input at various times for multiple different things; as such, I thought it'd be easier to have those scenarios chunked up. You can always wrap `Prefs` into a module singleton if you wish.
342
-
343
447
  # Known Issues
344
448
 
345
449
  * LoggerDelegator doesn't currently know what to do with `messenger.prompt`, so you'll have to manually log a `debug` message if you want that information logged.
@@ -348,6 +452,8 @@ I've written apps that need to request user input at various times for multiple
348
452
 
349
453
  To report bugs with or suggest features/changes for CLIUtils, please use the [Issues Page](http://github.com/bachya/cli-utils/issues).
350
454
 
455
+ * Trello Board: [https://trello.com/b/qXs7Yeir/cliutils](https://trello.com/b/qXs7Yeir/cliutils "CLIUtils on Trello")
456
+
351
457
  # Contributing
352
458
 
353
459
  Contributions are welcome and encouraged. To contribute:
@@ -0,0 +1,14 @@
1
+ module CLIUtils
2
+ # PrefBehavior Module
3
+ # Behaviors that should be applied to a Pref's
4
+ # final value
5
+ module PrefBehavior
6
+ # Expands the passed text (assumes it
7
+ # is a filepath).
8
+ # @param [String] text The text to evaluate
9
+ # @return [String]
10
+ def self.local_filepath(text)
11
+ File.expand_path(text)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,64 @@
1
+ require 'uri'
2
+
3
+ module CLIUtils
4
+ # PrefValidation Module
5
+ # Validation rules that can be applied to a Pref.
6
+ module PrefValidation
7
+ Validator = Struct.new(:code, :message)
8
+
9
+ # Validates that a value is only letters.
10
+ # @param [String] text The text to inspect
11
+ # @return [Boolean]
12
+ def self.alphabetic(text)
13
+ m = "Response is not alphabetic: #{ text }"
14
+ c = text.to_s =~ /\A[A-Za-z\s]+\z/
15
+ Validator.new(c, m)
16
+ end
17
+
18
+ # Validates that a value is only letters and numbers.
19
+ # @param [String] text The text to inspect
20
+ # @return [Boolean]
21
+ def self.alphanumeric(text)
22
+ m = "Response is not alphanumeric: #{ text }"
23
+ c = text.to_s =~ /\A[A-Za-z0-9\s]+\z/
24
+ Validator.new(c, m)
25
+ end
26
+
27
+ # Validates that a value is a date.
28
+ # @param [String] text The text to inspect
29
+ # @return [Boolean]
30
+ def self.date(text)
31
+ m = "Response is not a date: #{ text }"
32
+ c = !(Date.parse(text) rescue nil).nil?
33
+ Validator.new(c, m)
34
+ end
35
+
36
+ # Validates that a value is passed and is not
37
+ # empty.
38
+ # @param [String] text The text to inspect
39
+ # @return [Boolean]
40
+ def self.non_nil(text)
41
+ m = "Nil text not allowed"
42
+ c = !text.nil? && !text.empty?
43
+ Validator.new(c, m)
44
+ end
45
+
46
+ # Validates that a value is some sort of number.
47
+ # @param [String] text The text to inspect
48
+ # @return [Boolean]
49
+ def self.numeric(text)
50
+ m = "Response is not a number: #{ text }"
51
+ c = text.to_s =~ /\A[-+]?\d*\.?\d+\z/
52
+ Validator.new(c, m)
53
+ end
54
+
55
+ # Validates that passed value is a URL.
56
+ # @param [String] text The text to inspect
57
+ # @return [Boolean]
58
+ def self.url(text)
59
+ m = "Response is not a url: #{ text }"
60
+ c = text.to_s =~ URI::DEFAULT_PARSER.regexp[:ABS_URI]
61
+ Validator.new(c, m)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,142 @@
1
+ require 'cliutils/messenging'
2
+ require 'cliutils/prefs/pref-behavior'
3
+ require 'cliutils/prefs/pref-validation'
4
+
5
+ module CLIUtils
6
+ # Pref Class
7
+ # An individual preference
8
+ class Pref
9
+ include Messenging
10
+
11
+ # Stores the answer to this Pref.
12
+ # @return [String, Symbol]
13
+ attr_accessor :answer
14
+
15
+ # Stores the behaviors that this Pref conforms to.
16
+ # @return [Array]
17
+ attr_accessor :behaviors
18
+
19
+ # Stores a key to reference this pref in a Configurator.
20
+ # @return [String, Symbol]
21
+ attr_accessor :config_key
22
+
23
+ # Stores a Configurator section to stick this Pref under.
24
+ # @return [String, Symbol]
25
+ attr_accessor :config_section
26
+
27
+ # Stores the default text.
28
+ # @return [String]
29
+ attr_accessor :default
30
+
31
+ # Stores the last error message.
32
+ # @return [String]
33
+ attr_accessor :last_error_message
34
+
35
+ # Stores the valid options the user can pick from.
36
+ # @return [Array]
37
+ attr_accessor :options
38
+
39
+ # Stores the prereqs information.
40
+ # @return [Array]
41
+ attr_accessor :prereqs
42
+
43
+ # Stores the prompt text.
44
+ # @return [String]
45
+ attr_accessor :prompt
46
+
47
+ # Stores key/value combinations required to show this Pref.
48
+ # @return [Hash]
49
+ attr_accessor :validators
50
+
51
+ # Initializes a new Pref via passed-in parameters.
52
+ # @param [Hash] params Parameters to initialize
53
+ # @return [void]
54
+ def initialize(params = {})
55
+ params.each { |key, value| send("#{ key }=", value) }
56
+ end
57
+
58
+ # Custom equality operator for this class.
59
+ # @param [Pref] other
60
+ # @return [Boolean]
61
+ def ==(other)
62
+ return self.answer == other.answer &&
63
+ self.behaviors == other.behaviors &&
64
+ self.config_key == other.config_key &&
65
+ self.config_section == other.config_section &&
66
+ self.default == other.default &&
67
+ self.last_error_message == other.last_error_message &&
68
+ self.options == other.options &&
69
+ self.prereqs == other.prereqs &&
70
+ self.prompt == other.prompt &&
71
+ self.validators == other.validators
72
+ end
73
+
74
+ # Runs the passed text through this Pref's behaviors.
75
+ # @param [String] text The text to evaluate
76
+ # @return [String]
77
+ def evaluate_behaviors(text)
78
+ if @behaviors
79
+ modified_text = text
80
+ @behaviors.each do |b|
81
+ if PrefBehavior.respond_to?(b)
82
+ modified_text = PrefBehavior.send(b, modified_text)
83
+ else
84
+ messenger.warn("Skipping undefined Pref behavior: #{ b }")
85
+ end
86
+ end
87
+
88
+ modified_text
89
+ else
90
+ text
91
+ end
92
+ end
93
+
94
+ # Validates a text against this pref's options and
95
+ # validators.
96
+ # @param [String] text The text to validate
97
+ # @return [Boolean]
98
+ def validate(text)
99
+ _confirm_options(text) &&
100
+ _confirm_validators(text)
101
+ end
102
+
103
+ private
104
+
105
+ # Validates a text against the options for this Pref
106
+ # @param [String] text The text to validate
107
+ # @return [Boolean]
108
+ def _confirm_options(text)
109
+ ret = true
110
+ if @options
111
+ unless @options.include?(text)
112
+ @last_error_message = "Invalid option chosen (\"#{ text }\"); valid options are: #{ options }"
113
+ ret = false
114
+ end
115
+ end
116
+
117
+ ret
118
+ end
119
+
120
+ # Validates a text against the validators for this Pref
121
+ # @param [String] text The text to validate
122
+ # @return [Boolean]
123
+ def _confirm_validators(text)
124
+ ret = true
125
+ if @validators
126
+ @validators.each do |v|
127
+ if PrefValidation.respond_to?(v)
128
+ validator = PrefValidation.send(v, text)
129
+ unless validator.code
130
+ @last_error_message = validator.message
131
+ ret = false
132
+ end
133
+ else
134
+ messenger.warn("Skipping undefined Pref validator: #{ v }")
135
+ end
136
+ end
137
+ end
138
+
139
+ ret
140
+ end
141
+ end
142
+ end
@@ -1,14 +1,11 @@
1
+ require 'cliutils/prefs/pref'
1
2
  require 'cliutils/pretty-io'
2
3
 
3
4
  module CLIUtils
4
5
  # Engine to derive preferences from a YAML file, deliver
5
6
  # those to a user via a prompt, and collect the results.
6
7
  class Prefs
7
- include PrettyIO
8
- # Stores answers to prompt questions.
9
- # @return [Array]
10
- attr_reader :answers
11
-
8
+ include Messenging
12
9
  # Stores the filepath (if it exists) to the prefs file.
13
10
  # @return [String]
14
11
  attr_reader :config_path
@@ -18,8 +15,8 @@ module CLIUtils
18
15
  attr_reader :configurator
19
16
 
20
17
  # Stores answers to prompt questions.
21
- # @return [Hash]
22
- attr_reader :prompts
18
+ # @return [Array]
19
+ attr_reader :prefs
23
20
 
24
21
  # Reads prompt data from and stores it.
25
22
  # @param [<String, Hash, Array>] data Filepath to YAML, Hash, or Array
@@ -28,28 +25,26 @@ module CLIUtils
28
25
  def initialize(data, configurator = nil)
29
26
  @answers = []
30
27
  @configurator = configurator
31
- @prompts = {}
28
+ @prefs = []
32
29
 
33
30
  case data
34
31
  when String
35
32
  if File.exists?(data)
36
33
  @config_path = data
37
-
38
- prompts = YAML::load_file(data)
39
- @prompts.deep_merge!(prompts).deep_symbolize_keys!
34
+ data = YAML::load_file(data).deep_symbolize_keys
35
+ @prefs = _generate_prefs(data)
40
36
  else
41
- fail "Invalid configuration file: #{ data }"
37
+ fail "Invalid configuration file: #{ data }"
42
38
  end
43
39
  when Hash
44
40
  @config_path = nil
45
-
46
41
  data = {:prompts => data} unless data.keys[0] == :prompts
47
- @prompts.deep_merge!(data).deep_symbolize_keys!
42
+ data.deep_symbolize_keys!
43
+ @prefs = _generate_prefs(data)
48
44
  when Array
49
45
  @config_path = nil
50
-
51
- prompts = {:prompts => data}
52
- @prompts.deep_merge!(prompts).deep_symbolize_keys!
46
+ data = {:prompts => data}.deep_symbolize_keys
47
+ @prefs = _generate_prefs(data)
53
48
  else
54
49
  fail 'Invalid configuration data'
55
50
  end
@@ -57,16 +52,16 @@ module CLIUtils
57
52
 
58
53
  # Runs through all of the prompt questions and collects
59
54
  # answers from the user. Note that all questions w/o
60
- # requirements are examined first; once those are
61
- # complete, questions w/ requirements are examined.
55
+ # prerequisites are examined first; once those are
56
+ # complete, questions w/ prerequisites are examined.
62
57
  # @return [void]
63
58
  def ask
64
- @prompts[:prompts].reject { |p| p[:requirements] }.each do |p|
59
+ @prefs.reject { |p| p.prereqs }.each do |p|
65
60
  _deliver_prompt(p)
66
61
  end
67
-
68
- @prompts[:prompts].find_all { |p| p[:requirements] }.each do |p|
69
- _deliver_prompt(p) if _requirements_fulfilled?(p)
62
+
63
+ @prefs.find_all { |p| p.prereqs }.each do |p|
64
+ _deliver_prompt(p) if _prereqs_fulfilled?(p)
70
65
  end
71
66
  end
72
67
 
@@ -77,42 +72,46 @@ module CLIUtils
77
72
  # @param [Hash] p The prompt
78
73
  # @return [void]
79
74
  def _deliver_prompt(p)
80
- default = p[:default]
75
+ default = p.default
81
76
 
82
77
  unless @configurator.nil?
83
- unless @configurator.data[p[:section].to_sym].nil?
84
- config_val = @configurator.data[p[:section].to_sym][p[:key].to_sym]
78
+ unless @configurator.data[p.section.to_sym].nil?
79
+ config_val = @configurator.data[p.config_section.to_sym][p.config_key.to_sym]
85
80
  default = config_val unless config_val.nil?
86
81
  end
87
82
  end
88
-
89
- if p[:options].nil?
90
- pref = prompt(p[:prompt], default)
91
- else
92
- valid_option_chosen = false
93
- until valid_option_chosen
94
- pref = prompt(p[:prompt], default)
95
- if p[:options].include?(pref)
96
- valid_option_chosen = true
97
- else
98
- error("Invalid option chosen (\"#{ pref }\"); valid options are: #{ p[:options] }")
99
- end
83
+
84
+ valid_option_chosen = false
85
+ until valid_option_chosen
86
+ response = prompt(p.prompt, default)
87
+
88
+ if p.validate(response)
89
+ valid_option_chosen = true
90
+ p.answer = p.evaluate_behaviors(response)
91
+ else
92
+ messenger.error(p.last_error_message)
100
93
  end
101
94
  end
102
- p[:answer] = pref
103
- @answers << p
95
+ end
96
+
97
+ # Generates an Array of Prefs based on passed
98
+ # in data.
99
+ # @param [Hash] pref_data Loaded pref data
100
+ # @return [Array]
101
+ def _generate_prefs(pref_data_hash)
102
+ pref_data_hash[:prompts].map { |p| CLIUtils::Pref.new(p) }
104
103
  end
105
104
 
106
105
  # Utility method for determining whether a prompt's
107
- # requirements have already been fulfilled.
106
+ # prerequisites have already been fulfilled.
108
107
  # @param [Hash] p The prompt
109
108
  # @return [void]
110
- def _requirements_fulfilled?(p)
109
+ def _prereqs_fulfilled?(p)
111
110
  ret = true
112
- p[:requirements].each do |req|
113
- a = @answers.detect do |answer|
114
- answer[:key] == req[:key] &&
115
- answer[:answer] == req[:value]
111
+ p.prereqs.each do |req|
112
+ a = @prefs.detect do |answer|
113
+ answer.config_key == req[:config_key] &&
114
+ answer.answer == req[:config_value]
116
115
  end
117
116
  ret = false if a.nil?
118
117
  end
@@ -1,4 +1,4 @@
1
1
  module CLIUtils
2
2
  # The current version of the gem
3
- VERSION = '1.1.1'
3
+ VERSION = '1.2.0'
4
4
  end
data/lib/cliutils.rb CHANGED
@@ -8,6 +8,7 @@ require 'cliutils/configuration'
8
8
  require 'cliutils/logger-delegator'
9
9
  require 'cliutils/messenging'
10
10
  require 'cliutils/prefs'
11
+ require 'cliutils/prefs/pref'
11
12
  require 'cliutils/version'
12
13
 
13
14
  # The CLIUtils module, which wraps everything
Binary file
data/test/prefs_test.rb CHANGED
@@ -1,13 +1,13 @@
1
- require 'fileutils'
1
+ require 'cliutils/ext/hash+extensions'
2
+ require 'cliutils/prefs'
3
+ require 'cliutils/prefs/pref'
2
4
  require 'test/unit'
3
5
  require 'yaml'
4
6
 
5
- require File.join(File.dirname(__FILE__), '..', 'lib/cliutils/ext/Hash+Extensions')
6
- require File.join(File.dirname(__FILE__), '..', 'lib/cliutils/prefs')
7
-
8
7
  class TestPrefs < Test::Unit::TestCase
9
8
  def setup
10
- @prefs_arr = [{:prompt=>"What is the hostname of your DD-WRT router?", :default=>"192.168.1.1", :key=>"hostname", :section=>"ssh_info"}, {:prompt=>"What is the SSH username of your DD-WRT router?", :default=>"root", :key=>"username", :section=>"ssh_info"}, {:prompt=>"What SSH port does your DD-WRT router use?", :default=>22, :key=>"port", :section=>"ssh_info"}, {:prompt=>"How do you use password or key authentication?", :default=>"password", :key=>"auth_method", :section=>"ssh_info", :options=>["password", "key"]}, {:prompt=>"Where is your key located?", :default=>"~/.ssh", :key=>"key_location", :section=>"ssh_info", :requirements=>[{:key=>"auth_method", :value=>"key"}]}, {:prompt=>"What is your password?", :key=>"password", :section=>"ssh_info", :requirements=>[{:key=>"auth_method", :value=>"password"}]}]
9
+ @prefs_arr = [{:prompt=>"Where is your SSH public key located?", :config_key=>"pub_key", :config_section=>"personal_info", :behaviors=>["local_filepath"]}]
10
+ @prefs_hash = {:prompts=>@prefs_arr}
11
11
 
12
12
  @prefs_filepath = '/tmp/prefstest.yaml'
13
13
  FileUtils.cp(File.join(File.dirname(__FILE__), '..', 'test/test_files/prefstest.yaml'), @prefs_filepath)
@@ -19,11 +19,22 @@ class TestPrefs < Test::Unit::TestCase
19
19
 
20
20
  def test_file_creation
21
21
  p = CLIUtils::Prefs.new(@prefs_filepath)
22
- assert_equal(YAML::load_file(@prefs_filepath).deep_symbolize_keys!, p.prompts)
22
+ prefs = YAML::load_file(@prefs_filepath).deep_symbolize_keys
23
+
24
+ assert_equal(prefs[:prompts].map { |p| CLIUtils::Pref.new(p) }, p.prefs)
23
25
  end
24
26
 
25
- def test_hash_creation
27
+ def test_array_creation
26
28
  p = CLIUtils::Prefs.new(@prefs_arr)
27
- assert_equal({:prompts => @prefs_arr}.deep_symbolize_keys!, p.prompts)
29
+ prefs = @prefs_hash.deep_symbolize_keys
30
+
31
+ assert_equal(prefs[:prompts].map { |p| CLIUtils::Pref.new(p) }, p.prefs)
32
+ end
33
+
34
+ def test_hash_creation
35
+ p = CLIUtils::Prefs.new(@prefs_hash)
36
+ prefs = @prefs_hash.deep_symbolize_keys
37
+
38
+ assert_equal(prefs[:prompts].map { |p| CLIUtils::Pref.new(p) }, p.prefs)
28
39
  end
29
40
  end
@@ -1,36 +1,6 @@
1
1
  prompts:
2
- - prompt: What is the local hostname of your DD-WRT router?
3
- default: 192.168.1.1
4
- key: local_hostname
5
- section: ssh_info
6
- - prompt: What is the remote hostname of your DD-WRT router?
7
- key: remote_hostname
8
- section: ssh_info
9
- - prompt: What is the SSH username of your DD-WRT router?
10
- default: root
11
- key: username
12
- section: ssh_info
13
- - prompt: What SSH port does your DD-WRT router use?
14
- default: 22
15
- key: port
16
- section: ssh_info
17
- - prompt: Do you use password or key authentication?
18
- default: password
19
- key: auth_method
20
- section: ssh_info
21
- options: ['password', 'key']
22
- - prompt: Where is your key located?
23
- default: ~/.ssh
24
- key: key_location
25
- section: ssh_info
26
- requirements:
27
- - key: auth_method
28
- value: key
29
- - prompt: What is your password?
30
- key: password
31
- section: ssh_info
32
- requirements:
33
- - key: auth_method
34
- value: password
35
-
36
-
2
+ - prompt: Where is your SSH public key located?
3
+ config_key: pub_key
4
+ config_section: personal_info
5
+ behaviors:
6
+ - local_filepath
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cliutils
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Bach
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-01 00:00:00.000000000 Z
11
+ date: 2014-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -93,11 +93,18 @@ files:
93
93
  - lib/cliutils/logger-delegator.rb
94
94
  - lib/cliutils/messenging.rb
95
95
  - lib/cliutils/prefs.rb
96
+ - lib/cliutils/prefs/pref-behavior.rb
97
+ - lib/cliutils/prefs/pref-validation.rb
98
+ - lib/cliutils/prefs/pref.rb
96
99
  - lib/cliutils/pretty-io.rb
97
100
  - lib/cliutils/version.rb
98
101
  - res/readme-images/messenger-types-1.png
99
102
  - res/readme-images/messenger-warn.png
100
103
  - res/readme-images/multi-logger.png
104
+ - res/readme-images/prefs-ask-behaviors.png
105
+ - res/readme-images/prefs-ask-options.png
106
+ - res/readme-images/prefs-ask-prereqs.png
107
+ - res/readme-images/prefs-ask-validators.png
101
108
  - res/readme-images/prefs-ask.png
102
109
  - res/readme-images/prettyio-color-chart.png
103
110
  - res/readme-images/prettyio-gnarly-text.png