cliutils 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/HISTORY.md +6 -0
- data/README.md +150 -44
- data/lib/cliutils/prefs/pref-behavior.rb +14 -0
- data/lib/cliutils/prefs/pref-validation.rb +64 -0
- data/lib/cliutils/prefs/pref.rb +142 -0
- data/lib/cliutils/prefs.rb +45 -46
- data/lib/cliutils/version.rb +1 -1
- data/lib/cliutils.rb +1 -0
- data/res/readme-images/prefs-ask-behaviors.png +0 -0
- data/res/readme-images/prefs-ask-options.png +0 -0
- data/res/readme-images/prefs-ask-prereqs.png +0 -0
- data/res/readme-images/prefs-ask-validators.png +0 -0
- data/res/readme-images/prefs-ask.png +0 -0
- data/test/prefs_test.rb +19 -8
- data/test/test_files/prefstest.yaml +5 -35
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f244aff7b8639cf70d06eee8bef9f69d1ccaced
|
4
|
+
data.tar.gz: 16845634a0d353037b7c60f200bc76788abc73b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78b54b0002d6c07b1189983782b46462c2db1225b573d5924af0d53059f2a01925421c0cc2160b4410021c04d6985f739d2f695c02b6f744d5a1044c2f693807
|
7
|
+
data.tar.gz: 769aaa4b4026175ef9c4bcce2aa373639472759f5fcad62df3a90d4169316e6cdccf3f3ddbcde9b41b8d1b923942365716535aefb1edc521c0d19984fe49590f
|
data/Gemfile.lock
CHANGED
data/HISTORY.md
CHANGED
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(
|
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(:
|
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
|
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
|
-
*
|
271
|
-
*
|
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
|
280
|
-
default:
|
281
|
-
|
282
|
-
|
283
|
-
- prompt: What is
|
284
|
-
default:
|
285
|
-
|
286
|
-
|
287
|
-
- prompt:
|
288
|
-
default:
|
289
|
-
|
290
|
-
|
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.
|
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
|
data/lib/cliutils/prefs.rb
CHANGED
@@ -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
|
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 [
|
22
|
-
attr_reader :
|
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
|
-
@
|
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
|
-
|
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
|
-
|
42
|
+
data.deep_symbolize_keys!
|
43
|
+
@prefs = _generate_prefs(data)
|
48
44
|
when Array
|
49
45
|
@config_path = nil
|
50
|
-
|
51
|
-
|
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
|
-
#
|
61
|
-
# complete, questions w/
|
55
|
+
# prerequisites are examined first; once those are
|
56
|
+
# complete, questions w/ prerequisites are examined.
|
62
57
|
# @return [void]
|
63
58
|
def ask
|
64
|
-
@
|
59
|
+
@prefs.reject { |p| p.prereqs }.each do |p|
|
65
60
|
_deliver_prompt(p)
|
66
61
|
end
|
67
|
-
|
68
|
-
@
|
69
|
-
_deliver_prompt(p) if
|
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
|
75
|
+
default = p.default
|
81
76
|
|
82
77
|
unless @configurator.nil?
|
83
|
-
unless @configurator.data[p
|
84
|
-
config_val = @configurator.data[p
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
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
|
-
|
103
|
-
|
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
|
-
#
|
106
|
+
# prerequisites have already been fulfilled.
|
108
107
|
# @param [Hash] p The prompt
|
109
108
|
# @return [void]
|
110
|
-
def
|
109
|
+
def _prereqs_fulfilled?(p)
|
111
110
|
ret = true
|
112
|
-
p
|
113
|
-
a = @
|
114
|
-
answer
|
115
|
-
answer
|
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
|
data/lib/cliutils/version.rb
CHANGED
data/lib/cliutils.rb
CHANGED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/test/prefs_test.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
require '
|
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=>"
|
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
|
-
|
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
|
27
|
+
def test_array_creation
|
26
28
|
p = CLIUtils::Prefs.new(@prefs_arr)
|
27
|
-
|
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:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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.
|
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-
|
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
|