Skip to content

Instantly share code, notes, and snippets.

@mrtnbroder
Forked from i-am-tom/interactive-config.js
Created November 1, 2018 09:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrtnbroder/fba3300000c7efc23e7a74575307de69 to your computer and use it in GitHub Desktop.
Save mrtnbroder/fba3300000c7efc23e7a74575307de69 to your computer and use it in GitHub Desktop.
Prompt users for missing/sensitive config data as and when it's required.
// Sometimes, you might want to supply *some* config, but
// not necessarily *all*. Maybe your password is really
// secret, so you don't want to risk saving it in a config
// file. In which case, you'll want to ask the user to enter
// it when your script runs.
// This interface provides a flexible approach, where
// the user is prompted for a missing key when it's
// requested - if a particular key isn't needed for a given
// run, the user won't need to enter it. Of course, if the
// particular key exists in the given config, the user isn't
// asked.
const Task = require('data.task')
const { ReaderT } = require('fantasy-readers')
const { traverse } = require('ramda')
const { stdin: input, stdout: output } = process
// Ask the user a question on the command line.
// question :: String -> Task e String
const question = text => new Task((_, res) => {
const rl = require('readline')
.createInterface({ input: process.stdin
, output: process.stdout
, terminal: false })
return rl.question(
`${ text }: `,
data => { rl.close()
res(data) })
})
// type ReaderTask u e a = ReaderT u (Task e a)
const ReaderTask = ReaderT(Task)
// Get a config key if it exists, or ask the user if not.
// getConfig :: String -> ReaderTask StrMap e String
const getConfig = key =>
ReaderTask.ask.chain(
config => config[key] === undefined
? ReaderTask(_ => question(key))
: ReaderTask.of(config[key]))
// Ask the user for any of the three keys that are missing,
// then return all the values to be used.
traverse(ReaderTask.of, getConfig,
[ 'host', 'username', 'password' ])
// Do whatever fun thing with the values you need: connect
// to a database, call an API, whatever.
.map(([ host, user, pass ]) => ({ host, user, pass }))
// Pass in a potentially incomplete config array, whose
// missing values will be populated by user input.
.run({ 'host': 'localhost', 'username': 'tom' })
// None of the actions here can fail, so we'll just ignore
// that and log the result!
.fork(_ => undefined,
x => console.log(x))
// $ node transformer.js
// password: my-password
// { host: 'localhost', user: 'tom', pass: 'my-password' }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment