README.md in cliutils-1.1.1 vs README.md in cliutils-1.2.0

- old
+ new

@@ -167,10 +167,12 @@ p = messenger.prompt('Are you a fan of Battlestar Galactica?', default = 'Y') messenger.info("You answered: #{ p }") ``` ![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prompting.png "Prompting") +When you pass a default to `messenging.prompt`, hitting `Enter` (i.e., leaving the prompt blank) will return the value of the default. + ### Logging 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. For instance, let's say you wanted to log a few messages to both your user's STDOUT and to `file.txt`: @@ -178,19 +180,19 @@ ```Ruby messenger.info('This should only appear in STDOUT.') # messenger.attach takes a Hash of string/symbol keys # and Logger values (so you can refer to them later on). -messenger.attach(MY\_FILE\_LOGGER: Logger.new('file.txt')) +messenger.attach(MY_FILE_LOGGER: Logger.new('file.txt')) messenger.warn('This warning should appear in STDOUT and file.txt') messenger.error('This error should appear in STDOUT and file.txt') messenger.debug('This debug message should only appear in file.txt') # messenger.detach takes the string/symbol key # defined earlier. -messenger.detach(:MY\_FILE\_LOGGER) +messenger.detach(:MY_FILE_LOGGER) messenger.section('This section message should appear only in STDOUT') ``` In STDOUT: @@ -207,11 +209,11 @@ Since you can attach Logger objects, each can have it's own format and severity level. Cool! ## Configuration -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! +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! ```Ruby include CLIUtils::Configuration ``` @@ -261,53 +263,35 @@ ## Prefs 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. +### Basic Schema + `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: -* prompt (**required**): the string to prompt your user with -* default (*optional*): an optional default to offer -* key (**required**): the key that refers to this preference -* section (**required**): the Configuration section that this preference applies to -* options (*optional*): an optional array of values; the user's choice must be in this array -* requirements (*optional*): an optional list of key/value pairs that must exist for this preference to be displayed +* `prompt` (**required**): the string to prompt your user with +* `default` (*optional*): an optional default to offer +* `config_key` (**required**): the Configurator key that this preference will use +* `config_section` (**required**): the Configurator section that this preference applies to Here's an example YAML preferences file. ```YAML prompts: - - 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: 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 + - prompt: What is your name? + default: Bob Cobb + config_key: name + config_section: personal_info + - prompt: What is your age? + default: 45 + config_key: age + config_section: personal_info + - prompt: Batman or Superman? + default: Batman + config_key: superhero + config_section: personal_info ``` Assuming the above, `Prefs` is instantiated like so: ```Ruby @@ -319,35 +303,157 @@ ```Ruby prefs.ask ``` ![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prefs-ask.png "Prefs.ask") +### Prerequisites + +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: + +```YAML +prompts: + - prompt: Batman or Superman? + default: Batman + config_key: superhero + config_section: personal_info + - prompt: Do you feel smart for preferring Batman? + default: Y + config_key: batman_answer + config_section: personal_info + prereqs: + - config_key: superhero + config_value: Batman + - prompt: Why do you prefer Superman?! + default: No clue + config_key: superman_answer + config_section: personal_info + prereqs: + - config_key: superhero + config_value: Superman +``` + +`prereqs` checks for already-answered preferences (based on a Configurator key and value); assuming everything checks out, the subsequent preferences are collected: + +```Ruby +prefs.ask +``` +![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prefs-ask-prereqs.png "Prerequisities") + +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. + +### Options + +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": + +```YAML +prompts: + - prompt: Batman or Superman? + default: Batman + config_key: superhero + config_section: personal_info + options: ['Batman', 'Superman'] + - prompt: Do you feel smart for preferring Batman? + default: Y + config_key: batman_answer + config_section: personal_info + prereqs: + - config_key: superhero + config_value: Batman + - prompt: Why do you prefer Superman?! + default: No clue + config_key: superman_answer + config_section: personal_info + prereqs: + - config_key: superhero + config_value: Superman +``` + +Once in place: + +```Ruby +prefs.ask +``` +![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prefs-ask-options.png "Options") + +### Validators + +"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: + +```YAML +prompts: + - prompt: What is your name? + config_key: name + config_section: personal_info + validators: + - alphabetic + - prompt: How old are you? + config_key: age + config_section: personal_info + validators: + - numeric +``` + +```Ruby +prefs.ask +``` +![alt text](https://raw.githubusercontent.com/bachya/cli-utils/master/res/readme-images/prefs-ask-validators.png "Validators") + +`Prefs` currently supports these validators: + +* `alphabetic`: must be made up of letters and spaces +* `alphanumeric`: must be made up of letters, numbers, and spaces +* `date`: must be a parsable date (e.g., 2014-04-03) +* `non_nil`: must be a non-nil value +* `numeric`: must be made up of numbers +* `url`: must be a fully-qualified URL + +### Behaviors + +Finally, a common desire might be to modify the user's answer in some way: + +```YAML +prompts: + - prompt: Where is your SSH public key located? + config_key: pub_key + config_section: personal_info + behaviors: + - local_filepath +``` + +This will expand the user's answer as a filepath (e.g., `~/.ssh` will get saved as `/Users/bachya/.ssh/`). + +`Prefs` currently supports these behaviors: + +* `local_filepath`: runs File.expand_path on the answer + +### Adding Pref Responses to a Configurator + Once the user has answered all the preference prompts, you can fold those answers back into a Configurator using the `ingest` method: ```Ruby -configuration.ingest(prefs) +configuration.ingest_prefs(prefs) configuration.save ``` +### Using Configurator Values as Defaults + Note that you can also initialize a `Prefs` object with a Configurator: ```Ruby prefs = CLIUtils::Prefs.new('path/to/yaml/file', my_configurator) ``` 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. -### Why a Prefs Class? - -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. - # Known Issues * 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. # Bugs and Feature Requests To report bugs with or suggest features/changes for CLIUtils, please use the [Issues Page](http://github.com/bachya/cli-utils/issues). + +* Trello Board: [https://trello.com/b/qXs7Yeir/cliutils](https://trello.com/b/qXs7Yeir/cliutils "CLIUtils on Trello") # Contributing Contributions are welcome and encouraged. To contribute: