Unix and Node: Interfaces
by
at 2012-04-05 07:00:00
original http://feedproxy.google.com/~r/dailyjs/~3/8DQwxBbqODw/node-unix-interfaces
Earlier in this series I covered command-line arguments, which are well-supported in Node. There are times when a more interactive interface is required, however.
Fortunately, various node modules give us the tools to create command-line Unix programs with many different console-based interfaces, from Read-Eval-Print-Loops to GUI-like terminal control libraries.
REPL
Node’s Read-Eval-Print-Loop (REPL) is available as a module, and can be used to create interactive JavaScript shells. Node’s documentation has a cool example that uses a TCP server, so clients can connect with telnet
.
The documentation is currently slightly inaccurate with regard to REPL.start
– the callback method actually takes four arguments and won’t work as advertised. This example should work with the current version of Node 0.6:
var repl = require('repl')
, vm = require('vm');
repl.start('> ', process, function(code, context, file, callback) {
var result
, err;
try {
result = vm.runInThisContext(code, file);
} catch (err) {
console.error('Error:', err);
}
callback(err, result);
});
The process
global is passed as the stream
argument to make the REPL read and write to stdin
and stdout
. The callback method can do anything that’s required. For example, you could allow access to your database objects and methods in a web application, or provide an interactive administration interface to a daemon.
Readline
The repl
module works well when a JavaScript shell is required, but what about a custom REPL? Node actually includes a Readline module, which is perfect for this:
var readline = require('readline')
, rl;
rl = readline.createInterface(process.stdin, process.stdout, null);
rl.setPrompt('➜');
rl.on('line', function(cmd) {
if (cmd === 'quit') {
rl.question('Are you sure? (y/n) ', function(answer) {
if (answer === 'y') {
rl.close();
} else {
rl.prompt();
}
});
} else {
console.log('You typed:', cmd);
console.log('Type "quit" to exit');
}
rl.prompt();
});
rl.on('close', function() {
console.log('Bye');
process.exit();
});
rl.prompt();
Here I’ve used the readline
module to create an interface, then listen for line
events which denote a line of text was typed. The question
method will display a prompt and invoke the callback with the response.
By using simple string matching, a completely customised command-line interface can be created. The readline
module also has some useful built-in features like command history.
ncurses
The ncurses module by Brian White provides bindings to the ncurses library. This is a popular method for creating text-based user interfaces. If your application needs things like windows, menus, and more elaborate widgets such as a calendar, then ncurses is a good solution.
These bindings require a level of familiarisation with the original ncurses API. One freely available resource for learning ncurses is the NCURSES Programming HOWTO – combined with the ncurses-node README it’s possible to work out how to apply these techniques to a Node project.
Brian has also written some reusable widgets that come with the node-ncurses module:
var ncurses = require('ncurses')
, widgets = require('ncurses/lib/widgets')
, win = new ncurses.Window();
widgets.InputBox('Enter your name:', {
pos: 'center',
style: {
colors: {
bg: 'blue',
input: {
fg: 'red',
bg: 'black'
}
}
}
}, function(input) {
if (!input) {
input = 'nothing';
}
win.centertext(0, 'You entered: ' + input);
win.refresh();
setTimeout(function() { win.close(); }, 1000);
});
I’ve adapted this example from Brian’s code – it should work if you install the relevant module with npm install ncurses
. The result looks like this:
Alternatives
There are simpler alternatives to ncurses. Libraries we’ve covered before, like Commander.js have prompts and dialogs. Then there’s ansi.js (License: MIT, npm: ansi) which makes working with 256 ANSI colours relatively painless, particularly for web developers who are familiar with hex colours:
cursor.hex('#660000').bold().underline();
TermUI (npm: node-term-ui) by Josh Faul has a chainable event-based API that can move the cursor around and output text with various colours:
TermUI
.pos(10,20)
.fg(TermUI.C.w)
.bg(TermUI.C.w)
.out('Hello, world!');
There’s even a tab completion module: complete (License: MIT, npm: complete) by hij1nx. It’s actually designed to work with bash completion, and will write to .bashrc
or .bash_profile
:
# Node Completion - Auto-generated, do not touch.
shopt -s progcomp
for f in $(command ls ~/.node-completion); do
f="$HOME/.node-completion/$f"
test -f "$f" && . "$f"
done
There are dozens more interesting command-line UI libraries out there. If you’ve written something that you’d like us to feature in a Node Roundup post, then please get in touch!