Browse files

Generate coffee files; Integrate with coverage

  • Loading branch information...
1 parent fc4468f commit 4b0569002a55f2f3bb2721808e372a416c483518 @wdavidw committed Nov 13, 2012
Showing with 2,517 additions and 61 deletions.
  1. +12 −2 Makefile
  2. +341 −0 doc/coverage.html
  3. +40 −0 lib/NullStream.js
  4. +119 −0 lib/Request.js
  5. +27 −0 lib/Response.js
  6. +227 −0 lib/Shell.js
  7. +151 −0 lib/Styles.js
  8. +140 −0 lib/plugins/cloud9.js
  9. +129 −0 lib/plugins/coffee.js
  10. +35 −0 lib/plugins/completer.js
  11. +33 −0 lib/plugins/error.js
  12. +57 −0 lib/plugins/help.js
  13. +68 −0 lib/plugins/history.js
  14. +133 −0 lib/plugins/http.js
  15. +88 −0 lib/plugins/redis.js
  16. +177 −0 lib/plugins/router.js
  17. +90 −0 lib/plugins/stylus.js
  18. +64 −0 lib/plugins/test.js
  19. +33 −0 lib/routes/confirm.js
  20. +36 −0 lib/routes/prompt.js
  21. +17 −0 lib/routes/shellOnly.js
  22. +28 −0 lib/routes/timeout.js
  23. +352 −0 lib/start_stop.js
  24. +48 −0 lib/utils.js
  25. 0 {lib → src}/NullStream.coffee
  26. 0 {lib → src}/Request.coffee
  27. 0 {lib → src}/Response.coffee
  28. 0 {lib → src}/Shell.coffee
  29. 0 {lib → src}/Styles.coffee
  30. 0 {lib → src}/plugins/cloud9.coffee
  31. 0 {lib → src}/plugins/coffee.coffee
  32. 0 {lib → src}/plugins/completer.coffee
  33. 0 {lib → src}/plugins/error.coffee
  34. 0 {lib → src}/plugins/help.coffee
  35. 0 {lib → src}/plugins/history.coffee
  36. 0 {lib → src}/plugins/http.coffee
  37. 0 {lib → src}/plugins/redis.coffee
  38. 0 {lib → src}/plugins/router.coffee
  39. 0 {lib → src}/plugins/stylus.coffee
  40. 0 {lib → src}/plugins/test.coffee
  41. 0 {lib → src}/routes/confirm.coffee
  42. 0 {lib → src}/routes/prompt.coffee
  43. 0 {lib → src}/routes/shellOnly.coffee
  44. 0 {lib → src}/routes/timeout.coffee
  45. 0 {lib → src}/start_stop.coffee
  46. 0 {lib → src}/utils.coffee
  47. +7 −5 test/confirm.coffee
  48. +15 −12 test/error.coffee
  49. +11 −8 test/plugin_http.coffee
  50. +12 −10 test/question.coffee
  51. +18 −16 test/router.coffee
  52. +6 −5 test/shell.coffee
  53. +1 −1 test/start_stop.coffee
  54. +2 −2 test/styles.coffee
View
14 Makefile
@@ -1,5 +1,15 @@
+REPORTER = dot
-test:
- @./node_modules/.bin/mocha --compilers coffee:coffee-script
+build:
+ @./node_modules/.bin/coffee -b -o lib src
+
+test: build
+ @NODE_ENV=test ./node_modules/.bin/mocha --compilers coffee:coffee-script \
+ --reporter $(REPORTER)
+
+coverage: build
+ @jscoverage --no-highlight lib lib-cov
+ @SHELL_COV=1 $(MAKE) test REPORTER=html-cov > doc/coverage.html
+ @rm -rf lib-cov
.PHONY: test
View
341 doc/coverage.html
341 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
40 lib/NullStream.js
@@ -0,0 +1,40 @@
+// Generated by CoffeeScript 1.4.0
+var NullStream, events,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+events = require('events');
+
+module.exports = NullStream = (function(_super) {
+
+ __extends(NullStream, _super);
+
+ function NullStream() {
+ return NullStream.__super__.constructor.apply(this, arguments);
+ }
+
+ NullStream.prototype.readable = true;
+
+ NullStream.prototype.pause = function() {};
+
+ NullStream.prototype.resume = function() {};
+
+ NullStream.prototype.pipe = function() {};
+
+ NullStream.prototype.writable = true;
+
+ NullStream.prototype.write = function(data) {
+ return this.emit('data', data);
+ };
+
+ NullStream.prototype.end = function() {
+ return this.emit('close');
+ };
+
+ NullStream.prototype.destroy = function() {};
+
+ NullStream.prototype.destroySoon = function() {};
+
+ return NullStream;
+
+})(events.EventEmitter);
View
119 lib/Request.js
@@ -0,0 +1,119 @@
+// Generated by CoffeeScript 1.4.0
+var Request, each;
+
+each = require('each');
+
+module.exports = Request = (function() {
+
+ function Request(shell, command) {
+ this.shell = shell;
+ this.command = command;
+ }
+
+ /*
+ Ask one or more questions
+ */
+
+
+ Request.prototype.question = function(questions, callback) {
+ var answers, isObject, multiple, q, v,
+ _this = this;
+ isObject = function(v) {
+ return typeof v === 'object' && (v != null) && !Array.isArray(v);
+ };
+ multiple = true;
+ answers = {};
+ if (isObject(questions)) {
+ questions = (function() {
+ var _results;
+ _results = [];
+ for (q in questions) {
+ v = questions[q];
+ if (v == null) {
+ v = {};
+ }
+ if (!isObject(v)) {
+ v = {
+ value: v
+ };
+ }
+ v.name = q;
+ _results.push(v);
+ }
+ return _results;
+ })();
+ } else if (typeof questions === 'string') {
+ multiple = false;
+ questions = [
+ {
+ name: questions,
+ value: ''
+ }
+ ];
+ }
+ return each(questions).on('item', function(next, question) {
+ q = "" + question.name + " ";
+ if (question.value) {
+ q += "[" + question.value + "] ";
+ }
+ return _this.shell["interface"]().question(q, function(answer) {
+ if (answer.substr(-1, 1) === '\n') {
+ answer = answer.substr(0, answer.length - 1);
+ }
+ answers[question.name] = answer === '' ? question.value : answer;
+ return next();
+ });
+ }).on('end', function() {
+ if (!multiple) {
+ answers = answers[questions[0].name];
+ }
+ return callback(answers);
+ });
+ };
+
+ /*
+ Ask a question expecting a boolean answer
+ */
+
+
+ Request.prototype.confirm = function(msg, defaultTrue, callback) {
+ var args, keyFalse, keyTrue, key_false, key_true, question, _base, _base1, _ref, _ref1,
+ _this = this;
+ args = arguments;
+ if (!callback) {
+ callback = defaultTrue;
+ defaultTrue = true;
+ }
+ if ((_ref = (_base = this.shell.settings).key_true) == null) {
+ _base.key_true = 'y';
+ }
+ if ((_ref1 = (_base1 = this.shell.settings).key_false) == null) {
+ _base1.key_false = 'n';
+ }
+ key_true = this.shell.settings.key_true.toLowerCase();
+ key_false = this.shell.settings.key_false.toLowerCase();
+ keyTrue = defaultTrue ? key_true.toUpperCase() : key_true;
+ keyFalse = defaultTrue ? key_false : key_false.toUpperCase();
+ msg += ' ';
+ msg += "[" + keyTrue + keyFalse + "] ";
+ question = this.shell.styles.raw(msg, {
+ color: 'green'
+ });
+ return this.shell["interface"]().question(question, function(answer) {
+ var accepted, valid;
+ accepted = ['', key_true, key_false];
+ if (answer.substr(-1, 1) === '\n') {
+ answer = answer.substr(0, answer.length - 1);
+ }
+ answer = answer.toLowerCase();
+ valid = accepted.indexOf(answer) !== -1;
+ if (!valid) {
+ return _this.confirm.apply(_this, args);
+ }
+ return callback(answer === key_true || (defaultTrue && answer === ''));
+ });
+ };
+
+ return Request;
+
+})();
View
27 lib/Response.js
@@ -0,0 +1,27 @@
+// Generated by CoffeeScript 1.4.0
+var Response, pad, styles,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+styles = require('./Styles');
+
+pad = require('pad');
+
+module.exports = Response = (function(_super) {
+
+ __extends(Response, _super);
+
+ function Response(settings) {
+ this.shell = settings.shell;
+ Response.__super__.constructor.call(this, settings);
+ }
+
+ Response.prototype.pad = pad;
+
+ Response.prototype.prompt = function() {
+ return this.shell.prompt();
+ };
+
+ return Response;
+
+})(styles);
View
227 lib/Shell.js
@@ -0,0 +1,227 @@
+// Generated by CoffeeScript 1.4.0
+var EventEmitter, Interface, Request, Response, Shell, each, events, readline, styles, util, utils,
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+util = require('util');
+
+readline = require('readline');
+
+events = require('events');
+
+EventEmitter = events.EventEmitter;
+
+each = require('each');
+
+utils = require('./utils');
+
+styles = require('./Styles');
+
+Request = require('./Request');
+
+Response = require('./Response');
+
+Interface = require('readline').Interface;
+
+Interface.prototype.setPrompt = (function(parent) {
+ return function(prompt, length) {
+ var args;
+ args = Array.prototype.slice.call(arguments);
+ if (!args[1]) {
+ args[1] = styles.unstyle(args[0]).length;
+ }
+ return parent.apply(this, args);
+ };
+})(Interface.prototype.setPrompt);
+
+module.exports = Shell = (function(_super) {
+
+ __extends(Shell, _super);
+
+ function Shell(settings) {
+ var _base, _base1, _base2, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6,
+ _this = this;
+ if (settings == null) {
+ settings = {};
+ }
+ if (!(this instanceof Shell)) {
+ return new Shell(settings);
+ }
+ EventEmitter.call(this);
+ this.tmp = {};
+ this.settings = settings;
+ if ((_ref = (_base = this.settings).prompt) == null) {
+ _base.prompt = '>> ';
+ }
+ if ((_ref1 = (_base1 = this.settings).stdin) == null) {
+ _base1.stdin = process.stdin;
+ }
+ if ((_ref2 = (_base2 = this.settings).stdout) == null) {
+ _base2.stdout = process.stdout;
+ }
+ this.set('env', (_ref3 = (_ref4 = this.settings.env) != null ? _ref4 : process.env.NODE_ENV) != null ? _ref3 : 'development');
+ this.set('command', typeof settings.command !== 'undefined' ? settings.command : process.argv.slice(2).join(' '));
+ this.stack = [];
+ this.styles = styles({
+ stdout: this.settings.stdout
+ });
+ process.on('beforeExit', function() {
+ return _this.emit('exit');
+ });
+ process.on('uncaughtException', function(e) {
+ _this.emit('exit', [e]);
+ _this.styles.red('Internal error, closing...').ln();
+ console.error(e.message);
+ console.error(e.stack);
+ return process.exit();
+ });
+ this.isShell = (_ref5 = this.settings.isShell) != null ? _ref5 : process.argv.length === 2;
+ if (this.isShell) {
+ this["interface"]();
+ }
+ if ((_ref6 = settings.workspace) == null) {
+ settings.workspace = utils.workspace();
+ }
+ if (settings.chdir === true) {
+ process.chdir(settings.workspace);
+ }
+ if (typeof settings.chdir === 'string') {
+ process.chdir(settings.chdir);
+ }
+ process.nextTick(function() {
+ var command, noPrompt;
+ if (_this.isShell) {
+ command = _this.set('command');
+ noPrompt = _this.set('noPrompt');
+ if (command) {
+ return _this.run(command);
+ } else if (!noPrompt) {
+ return _this.prompt();
+ }
+ } else {
+ command = _this.set('command');
+ if (command) {
+ return _this.run(command);
+ }
+ }
+ });
+ return this;
+ }
+
+ Shell.prototype["interface"] = function() {
+ if (this._interface != null) {
+ return this._interface;
+ }
+ return this._interface = readline.createInterface(this.settings.stdin, this.settings.stdout);
+ };
+
+ Shell.prototype.configure = function(env, fn) {
+ if (typeof env === 'function') {
+ fn = env;
+ env = 'all';
+ }
+ if (env === 'all' || env === this.settings.env) {
+ fn.call(this);
+ }
+ return this;
+ };
+
+ Shell.prototype.use = function(handle) {
+ if (handle) {
+ this.stack.push({
+ route: null,
+ handle: handle
+ });
+ }
+ return this;
+ };
+
+ Shell.prototype.cmds = {};
+
+ Shell.prototype.run = function(command) {
+ var index, next, req, res, self;
+ command = command.trim();
+ this.emit('command', [command]);
+ this.emit(command, []);
+ self = this;
+ req = new Request(this, command);
+ res = new Response({
+ shell: this,
+ stdout: this.settings.stdout
+ });
+ index = 0;
+ next = function(err) {
+ var arity, layer, text;
+ layer = self.stack[index++];
+ if (!layer) {
+ if (err) {
+ return self.emit('error', err);
+ }
+ if (command !== '') {
+ text = "Command failed to execute " + command;
+ if (err) {
+ text += ": " + (err.message || err.name);
+ }
+ res.red(text);
+ }
+ return res.prompt();
+ }
+ arity = layer.handle.length;
+ if (err) {
+ if (arity === 4) {
+ self.emit('error', err);
+ return layer.handle(err, req, res, next);
+ } else {
+ return next(err);
+ }
+ } else if (arity < 4) {
+ return layer.handle(req, res, next);
+ } else {
+ return next();
+ }
+ };
+ return next();
+ };
+
+ Shell.prototype.set = function(setting, val) {
+ if (!(val != null)) {
+ if (this.settings.hasOwnProperty(setting)) {
+ return this.settings[setting];
+ } else if (this.parent) {
+ return this.parent.set(setting);
+ }
+ } else {
+ this.settings[setting] = val;
+ return this;
+ }
+ };
+
+ Shell.prototype.prompt = function() {
+ var text;
+ if (this.isShell) {
+ text = this.styles.raw(this.settings.prompt, {
+ color: 'green'
+ });
+ return this["interface"]().question(text, this.run.bind(this));
+ } else {
+ this.styles.ln();
+ if (process.versions) {
+ return this.quit();
+ } else {
+ this.settings.stdout.destroySoon();
+ return this.settings.stdout.on('close', function() {
+ return process.exit();
+ });
+ }
+ }
+ };
+
+ Shell.prototype.quit = function(params) {
+ this.emit('quit');
+ this["interface"]().close();
+ return this.settings.stdin.destroy();
+ };
+
+ return Shell;
+
+})(EventEmitter);
View
151 lib/Styles.js
@@ -0,0 +1,151 @@
+// Generated by CoffeeScript 1.4.0
+var Styles, bgcolors, code, color, colors, _fn;
+
+colors = {
+ black: 30,
+ red: 31,
+ green: 32,
+ yellow: 33,
+ blue: 34,
+ magenta: 35,
+ cyan: 36,
+ white: 37
+};
+
+bgcolors = {
+ black: 40,
+ red: 41,
+ green: 42,
+ yellow: 43,
+ blue: 44,
+ magenta: 45,
+ cyan: 46,
+ white: 47
+};
+
+module.exports = Styles = function(settings) {
+ var _ref;
+ if (settings == null) {
+ settings = {};
+ }
+ if (!(this instanceof Styles)) {
+ return new Styles(settings);
+ }
+ this.settings = settings;
+ this.settings.stdout = (_ref = settings.stdout) != null ? _ref : process.stdout;
+ this.current = {
+ weight: 'regular'
+ };
+ this.colors = colors;
+ this.bgcolors = bgcolors;
+ return this;
+};
+
+Styles.prototype.color = function(color, text) {
+ this.print(text, {
+ color: color
+ });
+ if (!text) {
+ this.current.color = color;
+ }
+ return this;
+};
+
+_fn = function(color) {
+ return Styles.prototype[color] = function(text) {
+ return this.color(color, text);
+ };
+};
+for (color in colors) {
+ code = colors[color];
+ _fn(color);
+}
+
+Styles.prototype.nocolor = function(text) {
+ return this.color(null, text);
+};
+
+Styles.prototype.bgcolor = function(bgcolor) {
+ if (bgcolor == null) {
+ bgcolor = 0;
+ }
+ this.print('\x1B[' + bgcolor + ';m39');
+ return this;
+};
+
+Styles.prototype.weight = function(weight, text) {
+ this.print(text, {
+ weight: weight
+ });
+ if (!text) {
+ this.current.weight = weight;
+ }
+ return this;
+};
+
+Styles.prototype.bold = function(text) {
+ return this.weight('bold', text);
+};
+
+Styles.prototype.regular = function(text) {
+ return this.weight('regular', text);
+};
+
+Styles.prototype.print = function(text, settings) {
+ this.settings.stdout.write(this.raw(text, settings));
+ return this;
+};
+
+Styles.prototype.println = function(text) {
+ this.settings.stdout.write(text + '\n');
+ return this;
+};
+
+Styles.prototype.ln = function() {
+ this.settings.stdout.write('\n');
+ return this;
+};
+
+Styles.prototype.raw = function(text, settings) {
+ var raw;
+ raw = '';
+ if (settings == null) {
+ settings = {};
+ }
+ if (settings.color !== null && (settings.color || this.current.color)) {
+ raw += '\x1b[' + this.colors[settings.color || this.current.color] + 'm';
+ } else {
+ raw += '\x1b[39m';
+ }
+ switch (settings.weight || this.current.weight) {
+ case 'bold':
+ raw += '\x1b[1m';
+ break;
+ case 'regular':
+ raw += '\x1b[22m';
+ break;
+ default:
+ throw new Error('Invalid weight "' + weight + '" (expect "bold" or "regular")');
+ }
+ if (text) {
+ raw += text;
+ if (this.current.color && this.current.color !== settings.color) {
+ raw += this.raw(null, this.current.color);
+ }
+ if (this.current.weight && this.current.weight !== settings.weight) {
+ raw += this.raw(null, this.current.weight);
+ }
+ }
+ return raw;
+};
+
+Styles.prototype.reset = function(text) {
+ return this.print(null, {
+ color: null,
+ weight: 'regular'
+ });
+};
+
+Styles.unstyle = function(text) {
+ return text.replace(/\x1b.*?m/g, '');
+};
View
140 lib/plugins/cloud9.js
@@ -0,0 +1,140 @@
+// Generated by CoffeeScript 1.4.0
+var start_stop;
+
+start_stop = require('../start_stop');
+
+/*
+
+Cloud9 plugin
+=============
+
+Register two commands, `cloud9 start` and `cloud9 stop`. Unless provided,
+the Cloud9 workspace will be automatically discovered if your project root
+directory contains a "package.json" file or a "node_module" directory.
+
+Options:
+
+- `config` , Load the configuration from a config file. Overrides command-line options. Defaults to `null`.
+- `group` , Run child processes with a specific group.
+- `user` , Run child processes as a specific user.
+- `action` , Define an action to execute after the Cloud9 server is started. Defaults to `null`.
+- `ip` , IP address where Cloud9 will serve from. Defaults to `"127.0.0.1"`.
+- `port` , Port number where Cloud9 will serve from. Defaults to `3000`.
+- `workspace`, Path to the workspace that will be loaded in Cloud9, Defaults to `Shell.set('workspace')`.
+- `detached` , Wether the Cloud9 process should be attached to the current process. If not defined, default to `false` (the server doesn't run as a daemon).
+- `pidfile` , Path to the file storing the daemon process id. Defaults to `"/.node_shell/#{md5}.pid"`
+- `stdout` , Writable stream or file path to redirect cloud9 stdout.
+- `stderr` , Writable stream or file path to redirect cloud9 stderr.
+
+Example:
+
+```javascript
+var app = new shell();
+app.configure(function() {
+ app.use(shell.router({
+ shell: app
+ }));
+ app.use(shell.cloud9({
+ shell: app,
+ ip: '0.0.0.0'
+ }));
+ app.use(shell.help({
+ shell: app,
+ introduction: true
+ }));
+});
+```
+
+**Important:** If you encounter issue while installing cloud9, it might be because the npm module expect an older version of Node.
+
+Here's the procedure to use the latest version:
+
+```
+git clone https://github.com/ajaxorg/cloud9.git
+cd cloud9
+git submodule update --init --recursive
+npm link
+```
+*/
+
+
+module.exports = function(settings) {
+ var cmd;
+ if (settings == null) {
+ settings = {};
+ }
+ cmd = function() {
+ var args;
+ args = [];
+ args.push('-w');
+ args.push(settings.workspace);
+ if (settings.config) {
+ args.push('-c');
+ args.push(settings.config);
+ }
+ if (settings.group) {
+ args.push('-g');
+ args.push(settings.group);
+ }
+ if (settings.user) {
+ args.push('-u');
+ args.push(settings.user);
+ }
+ if (settings.action) {
+ args.push('-a');
+ args.push(settings.action);
+ }
+ if (settings.ip) {
+ args.push('-l');
+ args.push(settings.ip);
+ }
+ if (settings.port) {
+ args.push('-p');
+ args.push(settings.port);
+ }
+ return "cloud9 " + (args.join(' '));
+ };
+ return function(req, res, next) {
+ var app, _ref;
+ app = req.shell;
+ if (app.tmp.cloud9) {
+ return next();
+ }
+ app.tmp.cloud9 = true;
+ if ((_ref = settings.workspace) == null) {
+ settings.workspace = app.set('workspace');
+ }
+ if (!settings.workspace) {
+ return next(new Error('No workspace provided'));
+ }
+ settings.cmd = cmd();
+ app.cmd('cloud9 start', 'Start Cloud9', function(req, res, next) {
+ return start_stop.start(settings, function(err, pid) {
+ var ip, message, port;
+ if (err) {
+ return next(err);
+ }
+ if (!pid) {
+ res.cyan('Cloud9 already started').ln();
+ return res.prompt();
+ }
+ ip = settings.ip || '127.0.0.1';
+ port = settings.port || 3000;
+ message = "Cloud9 started http://" + ip + ":" + port;
+ res.cyan(message).ln();
+ return res.prompt();
+ });
+ });
+ app.cmd('cloud9 stop', 'Stop Cloud9', function(req, res, next) {
+ return start_stop.stop(settings, function(err, success) {
+ if (success) {
+ res.cyan('Cloud9 successfully stoped').ln();
+ } else {
+ res.magenta('Cloud9 was not started').ln();
+ }
+ return res.prompt();
+ });
+ });
+ return next();
+ };
+};
View
129 lib/plugins/coffee.js
@@ -0,0 +1,129 @@
+// Generated by CoffeeScript 1.4.0
+var enrichFiles, fs, start_stop;
+
+fs = require('fs');
+
+start_stop = require('../start_stop');
+
+enrichFiles = function(files) {
+ return files.split(' ').map(function(file) {
+ if (file.substr(0, 1) !== '/') {
+ file = '/' + file;
+ }
+ if (file.substr(-1, 1) !== '/' && fs.statSync(file).isDirectory()) {
+ file += '/';
+ }
+ return file;
+ }).join(' ');
+};
+
+/*
+
+CoffeeScript plugin
+===================
+
+Start Coffee in `--watch` mode, so scripts are instantly compiled into Javascript.
+
+Options:
+
+- `src` , Directory where ".coffee" are stored. Each ".coffee" script will be compiled into a .js JavaScript file of the same name.
+- `join` , Before compiling, concatenate all scripts together in the order they were passed, and write them into the specified file. Useful for building large projects.
+- `output` , Directory where compiled JavaScript files are written. Used in conjunction with "compile".
+- `lint` , If the `jsl` (JavaScript Lint) command is installed, use it to check the compilation of a CoffeeScript file.
+- `require` , Load a library before compiling or executing your script. Can be used to hook in to the compiler (to add Growl notifications, for example).
+- `detached` , Wether the Coffee process should be attached to the current process. If not defined, default to `false` (the server doesn't run as a daemon).
+- `pidfile` , Path to the file storing the daemon process id. Defaults to `"/.node_shell/#{md5}.pid"`
+- `stdout` , Writable stream or file path to redirect cloud9 stdout.
+- `stderr` , Writable stream or file path to redirect cloud9 stderr.
+- `workspace`, Project directory used to resolve relative paths.
+
+Example:
+
+```javascript
+var app = new shell();
+app.configure(function() {
+ app.use(shell.router({
+ shell: app
+ }));
+ app.use(shell.coffee({
+ shell: app
+ }));
+ app.use(shell.help({
+ shell: app,
+ introduction: true
+ }));
+});
+```
+*/
+
+
+module.exports = function(settings) {
+ var cmd, shell, _ref;
+ if (settings == null) {
+ settings = {};
+ }
+ if (!settings.shell) {
+ throw new Error('No shell provided');
+ }
+ shell = settings.shell;
+ if ((_ref = settings.workspace) == null) {
+ settings.workspace = shell.set('workspace');
+ }
+ if (!settings.workspace) {
+ throw new Error('No workspace provided');
+ }
+ cmd = function() {
+ var args;
+ args = [];
+ if (settings.join) {
+ args.push('-j');
+ args.push(enrichFiles(settings.join));
+ }
+ args.push('-w');
+ if (settings.lint) {
+ args.push('-l');
+ }
+ if (settings.require) {
+ args.push('-r');
+ args.push(settings.require);
+ }
+ args.push('-b');
+ if (settings.output) {
+ args.push('-o');
+ args.push(enrichFiles(settings.output));
+ }
+ if (!settings.compile) {
+ settings.compile = settings.workspace;
+ }
+ if (settings.compile) {
+ args.push('-c');
+ args.push(enrichFiles(settings.compile));
+ }
+ return cmd = 'coffee ' + args.join(' ');
+ };
+ settings.cmd = cmd();
+ shell.cmd('coffee start', 'Start CoffeeScript', function(req, res, next) {
+ return start_stop.start(settings, function(err, pid) {
+ var message;
+ if (err) {
+ return next(err);
+ }
+ if (!pid) {
+ return res.cyan('Already Started').ln();
+ }
+ message = "CoffeeScript started";
+ res.cyan(message).ln();
+ return res.prompt();
+ });
+ });
+ return shell.cmd('coffee stop', 'Stop CoffeeScript', function(req, res, next) {
+ return start_stop.stop(settings, function(err, success) {
+ if (success) {
+ res.cyan('CoffeeScript successfully stoped').ln();
+ } else {
+ res.magenta('CoffeeScript was not started').ln();
+ }
+ return res.prompt();
+ });
+ });
+};
View
35 lib/plugins/completer.js
@@ -0,0 +1,35 @@
+// Generated by CoffeeScript 1.4.0
+/*
+
+Completer plugin
+================
+
+Provides tab completion. Options passed during creation are:
+
+- `shell` , (required) A reference to your shell application.
+*/
+
+module.exports = function(settings) {
+ var shell;
+ if (!settings.shell) {
+ throw new Error('No shell provided');
+ }
+ shell = settings.shell;
+ if (!shell.isShell) {
+ return;
+ }
+ shell["interface"]().completer = function(text, cb) {
+ var command, route, routes, suggestions, _i, _len;
+ suggestions = [];
+ routes = shell.routes;
+ for (_i = 0, _len = routes.length; _i < _len; _i++) {
+ route = routes[_i];
+ command = route.command;
+ if (command.substr(0, text.length) === text) {
+ suggestions.push(command);
+ }
+ }
+ return cb(false, [suggestions, text]);
+ };
+ return null;
+};
View
33 lib/plugins/error.js
@@ -0,0 +1,33 @@
+// Generated by CoffeeScript 1.4.0
+
+module.exports = function(settings) {
+ var shell;
+ if (!settings.shell) {
+ throw new Error('No shell provided');
+ }
+ shell = settings.shell;
+ shell.on('error', function() {});
+ return function(err, req, res, next) {
+ var k, v;
+ if (err.message) {
+ res.red(err.message).ln();
+ }
+ if (err.stack) {
+ res.red(err.stack).ln();
+ }
+ for (k in err) {
+ v = err[k];
+ if (k === 'message') {
+ continue;
+ }
+ if (k === 'stack') {
+ continue;
+ }
+ if (typeof v === 'function') {
+ continue;
+ }
+ res.magenta(k).white(': ').red(v).ln();
+ }
+ return res.prompt();
+ };
+};
View
57 lib/plugins/help.js
@@ -0,0 +1,57 @@
+// Generated by CoffeeScript 1.4.0
+var pad;
+
+pad = require('pad');
+
+/*
+
+Help Plugin
+-----------
+
+Display help when the user types "help" or runs commands without arguments.
+Command help is only displayed if a description was provided during the
+command registration. Additionnaly, a new `shell.help()` function is made available.
+
+Options passed during creation are:
+
+- `shell` , (required) A reference to your shell application.
+- `introduction` , Print message 'Type "help" or press enter for a list of commands' if boolean `true`, or a custom message if a `string`
+
+Usage
+
+ app = shell()
+ app.configure ->
+ app.use shell.router shell: app
+ app.use shell.help
+ shell: app
+ introduction: true
+*/
+
+
+module.exports = function(settings) {
+ var shell, text;
+ if (!settings.shell) {
+ throw new Error('No shell provided');
+ }
+ shell = settings.shell;
+ shell.help = function(req, res, next) {
+ var route, routes, text, _i, _len;
+ res.cyan('Available commands:');
+ res.ln();
+ routes = shell.routes;
+ for (_i = 0, _len = routes.length; _i < _len; _i++) {
+ route = routes[_i];
+ text = pad(route.command, 20);
+ if (route.description) {
+ res.cyan(text).white(route.description).ln();
+ }
+ }
+ return res.prompt();
+ };
+ shell.cmd('help', 'Show this message', shell.help.bind(shell));
+ shell.cmd('', shell.help.bind(shell));
+ if (shell.isShell && settings.introduction) {
+ text = typeof settings.introduction === 'string' ? settings.introduction : 'Type "help" or press enter for a list of commands';
+ return shell.styles.println(text);
+ }
+};
View
68 lib/plugins/history.js
@@ -0,0 +1,68 @@
+// Generated by CoffeeScript 1.4.0
+var Interface, crypto, fs, hash;
+
+fs = require('fs');
+
+crypto = require('crypto');
+
+Interface = require('readline').Interface;
+
+hash = function(value) {
+ return crypto.createHash('md5').update(value).digest('hex');
+};
+
+/*
+
+History plugin
+==============
+
+Persistent command history over multiple sessions. Options passed during creation are:
+
+- `shell` , (required) A reference to your shell application.
+- `name` , Identify your project history file, default to the hash of the exectuted file
+- `dir` , Location of the history files, defaults to `"#{process.env['HOME']}/.node_shell"`
+*/
+
+
+module.exports = function(settings) {
+ var file, json, shell, stream, _ref, _ref1;
+ if (!settings.shell) {
+ throw new Error('No shell provided');
+ }
+ shell = settings.shell;
+ if (!settings.shell.isShell) {
+ return;
+ }
+ if ((_ref = settings.dir) == null) {
+ settings.dir = "" + process.env['HOME'] + "/.node_shell";
+ }
+ if ((_ref1 = settings.name) == null) {
+ settings.name = hash(process.argv[1]);
+ }
+ file = "" + settings.dir + "/" + settings.file;
+ if (!fs.existsSync(settings.dir)) {
+ fs.mkdirSync(settings.dir, 0x1c0);
+ }
+ if (fs.existsSync(file)) {
+ try {
+ json = fs.readFileSync(file, 'utf8') || '[]';
+ settings.shell["interface"]().history = JSON.parse(json);
+ } catch (e) {
+ settings.shell.styles.red('Corrupted history file').ln();
+ }
+ }
+ stream = fs.createWriteStream(file, {
+ flag: 'w'
+ });
+ Interface.prototype._addHistory = (function(parent) {
+ return function() {
+ var buffer;
+ if (this.history.length) {
+ buffer = new Buffer(JSON.stringify(this.history));
+ fs.write(stream.fd, buffer, 0, buffer.length, 0);
+ }
+ return parent.apply(this, arguments);
+ };
+ })(Interface.prototype._addHistory);
+ return null;
+};
View
133 lib/plugins/http.js
@@ -0,0 +1,133 @@
+// Generated by CoffeeScript 1.4.0
+var existsSync, fs, path, start_stop;
+
+fs = require('fs');
+
+path = require('path');
+
+existsSync = fs.existsSync || path.existsSync;
+
+start_stop = require('../start_stop');
+
+/*
+
+HTTP server
+===========
+
+Register two commands, `http start` and `http stop`. The start command will
+search for "./server.js" and "./app.js" (and additionnaly their CoffeeScript
+alternatives) to run by `node`.
+
+The following properties may be provided as settings:
+
+- `message_start` Message to display once the server is started
+- `message_stop` Message to display once the server is stoped
+- `workspace` Project directory used to resolve relative paths and search for "server" and "app" scripts.
+- `cmd` Command to start the server, not required if path is provided or if the script is discoverable
+- `path` Path to the js/coffee script starting the process, may be relative to the workspace, extension isn't required.
+
+Properties derived from the start_stop utility:
+
+- `detached` Wether the HTTP process should be attached to the current process. If not defined, default to `false` (the server doesn't run as a daemon).
+- `pidfile` Path to the file storing the daemon process id. Defaults to `"/.node_shell/#{md5}.pid"`
+- `stdout` Writable stream or file path to redirect the server stdout.
+- `stderr` Writable stream or file path to redirect the server stderr.
+
+Example:
+
+```javascript
+var app = new shell();
+app.configure(function() {
+ app.use(shell.router({
+ shell: app
+ }));
+ app.use(shell.http({
+ shell: app
+ }));
+ app.use(shell.help({
+ shell: app,
+ introduction: true
+ }));
+});
+```
+*/
+
+
+module.exports = function() {
+ var cmd, http, route, settings;
+ settings = {};
+ cmd = function() {
+ var search, searchs, _i, _len;
+ searchs = settings.path ? [settings.path] : ['app', 'server', 'lib/app', 'lib/server'];
+ for (_i = 0, _len = searchs.length; _i < _len; _i++) {
+ search = searchs[_i];
+ search = path.resolve(settings.workspace, search);
+ if (existsSync("" + search)) {
+ if (search.substr(-4) === '.coffee') {
+ return "coffee " + search;
+ } else {
+ return "node " + search;
+ }
+ }
+ if (existsSync("" + search + ".js")) {
+ return "node " + search + ".js";
+ } else if (existsSync("" + search + ".coffee")) {
+ return "coffee " + search + ".coffee";
+ }
+ }
+ throw new Error('Failed to discover a "server.js" or "app.js" file');
+ };
+ http = null;
+ route = function(req, res, next) {
+ var app, _ref, _ref1, _ref2;
+ app = req.shell;
+ if (app.tmp.http) {
+ return next();
+ }
+ app.tmp.http = true;
+ if ((_ref = settings.workspace) == null) {
+ settings.workspace = app.set('workspace');
+ }
+ if (!settings.workspace) {
+ throw new Error('No workspace provided');
+ }
+ if ((_ref1 = settings.message_start) == null) {
+ settings.message_start = 'HTTP server successfully started';
+ }
+ if ((_ref2 = settings.message_stop) == null) {
+ settings.message_stop = 'HTTP server successfully stopped';
+ }
+ if (!settings.cmd) {
+ settings.cmd = cmd();
+ }
+ app.cmd('http start', 'Start HTTP server', function(req, res, next) {
+ return http = start_stop.start(settings, function(err, pid) {
+ if (err) {
+ return next(err);
+ }
+ if (!pid) {
+ return res.cyan('HTTP server already started').ln() && res.prompt();
+ }
+ res.cyan(settings.message_start).ln();
+ return res.prompt();
+ });
+ });
+ app.cmd('http stop', 'Stop HTTP server', function(req, res, next) {
+ return start_stop.stop(settings, function(err, success) {
+ if (success) {
+ res.cyan(settings.message_stop).ln();
+ } else {
+ res.magenta('HTTP server was not started').ln();
+ }
+ return res.prompt();
+ });
+ });
+ return next();
+ };
+ if (arguments.length === 1) {
+ settings = arguments[0];
+ return route;
+ } else {
+ return route.apply(null, arguments);
+ }
+};
View
88 lib/plugins/redis.js
@@ -0,0 +1,88 @@
+// Generated by CoffeeScript 1.4.0
+var start_stop;
+
+start_stop = require('../start_stop');
+
+/*
+Redis Plugin
+============
+
+Register two commands, `redis start` and `redis stop`. The following properties may be provided as settings:
+
+- `config` , Path to the configuration file. Required to launch redis.
+- `detached` , Wether the Redis process should be attached to the current process. If not defined, default to `false` (the server doesn't run as a daemon).
+- `pidfile` , Path to the file storing the daemon process id. Defaults to `"/.node_shell/#{md5}.pid"`
+- `stdout` , Writable stream or file path to redirect cloud9 stdout.
+- `stderr` , Writable stream or file path to redirect cloud9 stderr.
+
+Example:
+
+```javascript
+var app = shell();
+app.configure(function() {
+ app.use(shell.router({
+ shell: app
+ }));
+ app.use(shell.redis({
+ shell: app,
+ config: __dirname+'/redis.conf')
+ }));
+ app.use(shell.help({
+ shell: app,
+ introduction: true
+ }));
+});
+```
+*/
+
+
+module.exports = function() {
+ var redis, route, settings;
+ settings = {};
+ redis = null;
+ route = function(req, res, next) {
+ var app, _ref, _ref1;
+ app = req.shell;
+ if (app.tmp.redis) {
+ return next();
+ }
+ app.tmp.redis = true;
+ if ((_ref = settings.workspace) == null) {
+ settings.workspace = app.set('workspace');
+ }
+ if ((_ref1 = settings.config) == null) {
+ settings.config = '';
+ }
+ settings.cmd = "redis-server " + settings.config;
+ app.cmd('redis start', 'Start Redis', function(req, res, next) {
+ return redis = start_stop.start(settings, function(err, pid) {
+ if (err) {
+ return next(err);
+ }
+ if (!pid) {
+ res.cyan('Redis already started').ln();
+ return res.prompt();
+ }
+ res.cyan('Redis started').ln();
+ return res.prompt();
+ });
+ });
+ app.cmd('redis stop', 'Stop Redis', function(req, res, next) {
+ return start_stop.stop(settings, function(err, success) {
+ if (success) {
+ res.cyan('Redis successfully stoped').ln();
+ } else {
+ res.magenta('Redis was not started').ln();
+ }
+ return res.prompt();
+ });
+ });
+ return next();
+ };
+ if (arguments.length === 1) {
+ settings = arguments[0];
+ return route;
+ } else {
+ return route.apply(null, arguments);
+ }
+};
View
177 lib/plugins/router.js
@@ -0,0 +1,177 @@
+// Generated by CoffeeScript 1.4.0
+var match, normalize, querystring, utils,
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+utils = require('../utils');
+
+querystring = {
+ unescape: function(str) {
+ return decodeURIComponent(str);
+ },
+ parse: function(qs, sep, eq) {
+ var k, kvp, obj, v, vkps, x, _i, _len, _ref;
+ sep = sep || '&';
+ eq = eq || '=';
+ obj = {};
+ if (typeof qs !== 'string') {
+ return obj;
+ }
+ vkps = qs.split(sep);
+ for (_i = 0, _len = vkps.length; _i < _len; _i++) {
+ kvp = vkps[_i];
+ x = kvp.split(eq);
+ k = querystring.unescape(x[0], true);
+ v = querystring.unescape(x.slice(1).join(eq), true);
+ if (_ref = !k, __indexOf.call(obj, _ref) >= 0) {
+ obj[k] = v;
+ } else if (!Array.isArray(obj[k])) {
+ obj[k] = [obj[k], v];
+ } else {
+ obj[k].push(v);
+ }
+ }
+ return obj;
+ }
+};
+
+normalize = function(command, keys, sensitive) {
+ command = command.concat('/?').replace(/\/\(/g, '(?:/').replace(/:(\w+)(\(.*\))?(\?)?/g, function(_, key, format, optional) {
+ keys.push(key);
+ format = format || '([^ ]+)';
+ optional = optional || '';
+ return format + optional;
+ }).replace(/([\/.])/g, '\\$1').replace(/\*/g, '(.+)');
+ return new RegExp('^' + command + '$', (sensitive != null ? 'i' : void 0));
+};
+
+match = function(req, routes, i) {
+ var captures, index, j, key, keys, regexp, route, val;
+ if (i == null) {
+ i = 0;
+ }
+ while (i < routes.length) {
+ route = routes[i];
+ regexp = route.regexp;
+ keys = route.keys;
+ captures = regexp.exec(req.command);
+ if (captures) {
+ route.params = {};
+ index = 0;
+ j = 1;
+ while (j < captures.length) {
+ key = keys[j - 1];
+ val = typeof captures[j] === 'string' ? querystring.unescape(captures[j]) : captures[j];
+ if (key) {
+ route.params[key] = val;
+ } else {
+ route.params['' + index] = val;
+ index++;
+ }
+ j++;
+ }
+ req._route_index = i;
+ return route;
+ }
+ i++;
+ }
+ return null;
+};
+
+module.exports = function(settings) {
+ var params, routes, shell, _ref;
+ if (!settings.shell) {
+ throw new Error('No shell provided');
+ }
+ shell = settings.shell;
+ if ((_ref = settings.sensitive) == null) {
+ settings.sensitive = true;
+ }
+ routes = shell.routes = [];
+ params = {};
+ shell.param = function(name, fn) {
+ if (Array.isArray(name)) {
+ name.forEach(function(name) {
+ return this.param(name, fn);
+ }, this);
+ } else {
+ if (':' === name[0]) {
+ name = name.substr(1);
+ }
+ params[name] = fn;
+ }
+ return this;
+ };
+ shell.cmd = function(command, description, middleware1, middleware2, fn) {
+ var args, keys, route;
+ args = Array.prototype.slice.call(arguments);
+ route = {};
+ route.command = args.shift();
+ if (typeof args[0] === 'string') {
+ route.description = args.shift();
+ }
+ route.middlewares = utils.flatten(args);
+ keys = [];
+ route.regexp = route.command instanceof RegExp ? route.command : normalize(route.command, keys, settings.sensitive);
+ route.keys = keys;
+ routes.push(route);
+ return this;
+ };
+ shell.cmd('quit', 'Exit this shell', shell.quit.bind(shell));
+ return function(req, res, next) {
+ var i, pass, route, self;
+ route = null;
+ self = this;
+ i = 0;
+ pass = function(i) {
+ var keys, param;
+ route = match(req, routes, i);
+ if (!route) {
+ return next();
+ }
+ i = 0;
+ keys = route.keys;
+ req.params = route.params;
+ param = function(err) {
+ var fn, key, nextMiddleware, val;
+ try {
+ key = keys[i++];
+ val = req.params[key];
+ fn = params[key];
+ if ('route' === err) {
+ return pass(req._route_index + 1);
+ } else if (err) {
+ return next(err);
+ } else if (fn) {
+ if (1 === fn.length) {
+ req.params[key] = fn(val);
+ return param();
+ } else {
+ return fn(req, res, param, val);
+ }
+ } else if (!key) {
+ i = 0;
+ nextMiddleware = function(err) {
+ fn = route.middlewares[i++];
+ if ('route' === err) {
+ return pass(req._route_index + 1);
+ } else if (err) {
+ return next(err);
+ } else if (fn) {
+ return fn(req, res, nextMiddleware);
+ } else {
+ return pass(req._route_index + 1);
+ }
+ };
+ return nextMiddleware();
+ } else {
+ return param();
+ }
+ } catch (err) {
+ return next(err);
+ }
+ };
+ return param();
+ };
+ return pass();
+ };
+};
View
90 lib/plugins/stylus.js
@@ -0,0 +1,90 @@
+// Generated by CoffeeScript 1.4.0
+var enrichFiles, path, start_stop;
+
+path = require('path');
+
+start_stop = require('../start_stop');
+
+enrichFiles = function(files) {
+ return files.split(' ').map(function(file) {
+ path.normalize(file);
+ if (file.substr(-1, 1) === '/') {
+ file = file.substr(0, file.length - 1);
+ }
+ return file;
+ }).join(' ');
+};
+
+/*
+
+Stylus plugin
+-------------
+Start/stop a daemon to watch and convert stylus files to css.
+
+Options include:
+* `output` Output to <dir> when passing files.
+* `input` Add <path> to lookup paths
+*/
+
+
+module.exports = function(settings) {
+ var cmd, shell, _ref;
+ if (settings == null) {
+ settings = {};
+ }
+ if (!settings.shell) {
+ throw new Error('No shell provided');
+ }
+ shell = settings.shell;
+ if ((_ref = settings.workspace) == null) {
+ settings.workspace = shell.set('workspace');
+ }
+ if (!settings.workspace) {
+ throw new Error('No workspace provided');
+ }
+ cmd = function() {
+ var args;
+ args = [];
+ args.push('-w');
+ if (settings.use) {
+ args.push('-u');
+ args.push(enrichFiles(settings.use));
+ }
+ if (settings.output) {
+ args.push('-o');
+ args.push(enrichFiles(settings.output));
+ }
+ if (!settings.input) {
+ settings.input = settings.workspace;
+ }
+ if (settings.input) {
+ args.push(enrichFiles(settings.input));
+ }
+ return cmd = 'stylus ' + args.join(' ');
+ };
+ settings.cmd = cmd();
+ shell.cmd('stylus start', 'Start CoffeeScript', function(req, res, next) {
+ return start_stop.start(settings, function(err, pid) {
+ var message;
+ if (err) {
+ return next(err);
+ }
+ if (!pid) {
+ return res.cyan('Already Started').ln();
+ }
+ message = "Stylus started";
+ res.cyan(message).ln();
+ return res.prompt();
+ });
+ });
+ return shell.cmd('stylus stop', 'Stop Stylus', function(req, res, next) {
+ return start_stop.stop(settings, function(err, success) {
+ if (success) {
+ res.cyan('Stylus successfully stoped').ln();
+ } else {
+ res.magenta('Stylus was not started').ln();
+ }
+ return res.prompt();
+ });
+ });
+};
View
64 lib/plugins/test.js
@@ -0,0 +1,64 @@
+// Generated by CoffeeScript 1.4.0
+var exec, existsSync, fs, path;
+
+fs = require('fs');
+
+path = require('path');
+
+existsSync = fs.existsSync || path.existsSync;
+
+exec = require('child_process').exec;
+
+module.exports = function(settings) {
+ var shell, _ref, _ref1;
+ if (!settings.shell) {
+ throw new Error('No shell provided');
+ }
+ shell = settings.shell;
+ if ((_ref = settings.workspace) == null) {
+ settings.workspace = shell.set('workspace');
+ }
+ if (!settings.workspace) {
+ throw new Error('No workspace provided');
+ }
+ if ((_ref1 = settings.glob) == null) {
+ settings.glob = 'test/*.js';
+ }
+ shell.cmd('test', 'Run all test', function(req, res, next) {
+ var p, paths, run, _i, _len;
+ run = function(cmd) {
+ var args, expresso;
+ args = [];
+ args.push(cmd);
+ if (settings.coverage) {
+ args.push('--cov');
+ }
+ if (settings.serial) {
+ args.push('--serial');
+ }
+ if (settings.glob) {
+ args.push(settings.glob);
+ }
+ expresso = exec('cd ' + settings.workspace + ' && ' + args.join(' '));
+ expresso.stdout.on('data', function(data) {
+ return res.cyan(data);
+ });
+ expresso.stderr.on('data', function(data) {
+ return res.magenta(data);
+ });
+ return expresso.on('exit', function(code) {
+ return res.prompt();
+ });
+ };
+ paths = [].concat(module.paths, require.paths);
+ for (_i = 0, _len = paths.length; _i < _len; _i++) {
+ p = paths[_i];
+ if (existsSync(p + '/expresso/bin/expresso')) {
+ return run(p);
+ }
+ }
+ res.magenta('Expresso not found').ln();
+ return res.prompt();
+ });
+ return shell.cmd('test :pattern', 'Run specific tests', function(req, res, next) {});
+};
View
33 lib/routes/confirm.js
@@ -0,0 +1,33 @@
+// Generated by CoffeeScript 1.4.0
+/*
+
+Confirm route
+=============
+
+The `confirm` route ask the user if he want to continue the process. If the answer is `true`, the following routes are executed. Otherwise, the process is stoped.
+
+```javascript
+var app = new shell();
+app.configure(function() {
+ app.use(shell.router({
+ shell: app
+ }));
+});
+app.cmd('install', [
+ shell.routes.confirm('Do you confirm?'),
+ my_app.routes.download,
+ my_app.routes.configure
+]);
+```
+*/
+
+module.exports = function(message) {
+ return function(req, res, next) {
+ return req.confirm(message, true, function(confirmed) {
+ if (!confirmed) {
+ return res.prompt();
+ }
+ return next();
+ });
+ };
+};
View
36 lib/routes/prompt.js
@@ -0,0 +1,36 @@
+// Generated by CoffeeScript 1.4.0
+/*
+
+Prompt route
+============
+
+The `prompt` route is a convenient function to stop command once a few routes are executed. You can simply pass the the `shell.routes.prompt` function or call it with a message argument.
+
+```javascript
+var app = new shell();
+app.configure(function() {
+ app.use(shell.router({
+ shell: app
+ }));
+});
+app.cmd('install', [
+ my_app.routes.download,
+ my_app.routes.configure,
+ shell.routes.prompt('Installation is finished')
+]);
+```
+*/
+
+module.exports = function(req, res, next) {
+ var message;
+ if (arguments.length === 1) {
+ message = arguments[0];
+ return function(req, res, next) {
+ res.white(message);
+ res.ln();
+ return res.prompt();
+ };
+ } else {
+ return res.prompt();
+ }
+};
View
17 lib/routes/shellOnly.js
@@ -0,0 +1,17 @@
+// Generated by CoffeeScript 1.4.0
+/*
+
+`routes.shellOnly`
+==================
+
+Ensure the current process is running in shell mode.
+*/
+
+module.exports = function(req, res, next) {
+ if (!req.shell.isShell) {
+ res.red('Command may only be executed inside a running shell');
+ res.prompt();
+ return;
+ }
+ return next();
+};
View
28 lib/routes/timeout.js
@@ -0,0 +1,28 @@
+// Generated by CoffeeScript 1.4.0
+/*
+
+Timeout route
+=============
+
+The `timeout` route will wait for the provided period (in millisenconds) before executing the following route.
+
+```javascript
+var app = new shell();
+app.configure(function() {
+ app.use(shell.router({
+ shell: app
+ }));
+});
+app.cmd('restart', [
+ my_app.routes.stop,
+ shell.routes.timeout(1000),
+ my_app.routes.start
+]);
+```
+*/
+
+module.exports = function(timeout) {
+ return function(req, res, next) {
+ return setTimeout(timeout, next);
+ };
+};
View
352 lib/start_stop.js
@@ -0,0 +1,352 @@
+// Generated by CoffeeScript 1.4.0
+var crypto, exec, exists, fs, md5, path, spawn, start_stop, _ref;
+
+crypto = require('crypto');
+
+_ref = require('child_process'), exec = _ref.exec, spawn = _ref.spawn;
+
+fs = require('fs');
+
+path = require('path');
+
+exists = fs.exists || path.exists;
+
+md5 = function(cmd) {
+ return crypto.createHash('md5').update(cmd).digest('hex');
+};
+
+/*
+`start_stop`: Unix process management
+-------------------------------------
+
+The library start and stop unix child process. Process are by default
+daemonized and will keep running even if your current process exit. For
+conveniency, they may also be attached to the current process by
+providing the `attach` option.
+*/
+
+
+module.exports = start_stop = {
+ /*
+
+ `start(options, callback)`
+ --------------------------
+ Start a prcess as a daemon (default) or as a child of the current process. Options includes
+ all the options of the "child_process.exec" function plus a few specific ones.
+
+ `options` , Object with the following properties:
+ * `cmd` , Command to run
+ * `cwd` , Current working directory of the child process
+ * `detached` , Detached the child process from the current process
+ * `pidfile` , Path to the file storing the child pid
+ * `stdout` , Path to the file where standard output is redirected
+ * `stderr` , Path to the file where standard error is redirected
+ * `strict` , Send an error when a pid file exists and reference
+ an unrunning pid.
+ * `watch` , Watch for file changes
+ * `watchIgnore` , List of ignore files
+
+ `callback` , Received arguments are:
+ * `err` , Error if any
+ * `pid` , Process id of the new child
+ */
+
+ start: function(options, callback) {
+ var c, check_pid, child, cmdStderr, cmdStdout, start, stderr, stdout, watch;
+ if (options.attach != null) {
+ console.log('Option attach was renamed to attached to be consistent with the new spawn option');
+ options.detached = !options.attach;
+ }
+ if (options.detached) {
+ child = null;
+ cmdStdout = typeof options.stdout === 'string' ? options.stdout : '/dev/null';
+ cmdStderr = typeof options.stderr === 'string' ? options.stderr : '/dev/null';
+ check_pid = function() {
+ return start_stop.pid(options, function(err, pid) {
+ if (!pid) {
+ return watch();
+ }
+ return start_stop.running(pid, function(err, pid) {
+ if (pid) {
+ return callback(new Error("Pid " + pid + " already running"));
+ }
+ if (options.strict) {
+ return callback(new Error("Pid file reference a dead process"));
+ } else {
+ return watch();
+ }
+ });
+ });
+ };
+ watch = function() {
+ var ignore, ioptions;
+ if (!options.watch) {
+ return start();
+ }
+ if (typeof options.watch !== 'string') {
+ options.watch = options.cwd || process.cwd;
+ }
+ ioptions = {
+ path: options.watch,
+ ignoreFiles: [".startstopignore"] || options.watchIgnoreFiles
+ };
+ console.log('ioptions', ioptions);
+ ignore = require('fstream-ignore');
+ ignore(ioptions).on('child', function(c) {
+ console.log(c.path);
+ return fs.watchFile(c.path, function(curr, prev) {
+ console.log(c.path);
+ return start_stop.stop(options, function(e) {
+ return start_stop.start(options, function(e) {
+ return console.log('restarted', e);
+ });
+ });
+ });
+ });
+ return start();
+ };
+ start = function() {
+ var cmd, info, pipe;
+ pipe = "</dev/null >" + cmdStdout + " 2>" + cmdStdout;
+ info = 'echo $? $!';
+ cmd = "" + options.cmd + " " + pipe + " & " + info;
+ return child = exec(cmd, options, function(err, stdout, stderr) {
+ var code, msg, pid, _ref1;
+ _ref1 = stdout.split(' '), code = _ref1[0], pid = _ref1[1];
+ code = parseInt(code, 10);
+ pid = parseInt(pid, 10);
+ if (code !== 0) {
+ msg = "Process exit with code " + code;
+ return callback(new Error(msg));
+ }
+ return exists(path.dirname(options.pidfile), function(exists) {
+ if (!exists) {
+ return callback(new Error("Pid directory does not exist"));
+ }
+ return fs.writeFile(options.pidfile, '' + pid, function(err) {
+ return callback(null, pid);
+ });
+ });
+ });
+ };
+ return check_pid();
+ } else {
+ c = exec(options.cmd);
+ if (typeof options.stdout === 'string') {
+ stdout = fs.createWriteStream(options.stdout);
+ } else if (options.stdout !== null && typeof options.stdout === 'object') {
+ stdout = options.stdout;
+ } else {
+ stdout = null;
+ }
+ if (typeof options.stderr === 'string') {
+ stdout = fs.createWriteStream(options.stderr);
+ } else if (options.stderr !== null && typeof options.stderr === 'object') {
+ stderr = options.stderr;
+ } else {
+ stderr = null;
+ }
+ return process.nextTick(function() {
+ options.pid = c.pid;
+ return callback(null, c.pid);
+ });
+ }
+ },
+ /*
+
+ `stop(options, callback)`
+ -------------------------
+ Stop a process. In daemon mode, the pid is obtained from the `pidfile` option which, if
+ not provided, can be guessed from the `cmd` option used to start the process.
+
+ `options` , Object with the following properties:
+ * `detached` , Detach the child process to the current process
+ * `cmd` , Command used to run the process, in case no pidfile is provided
+ * `pid` , Pid to kill in attach mode
+ * `pidfile` , Path to the file storing the child pid
+ * `strict` , Send an error when a pid file exists and reference
+ an unrunning pid.
+
+ `callback` , Received arguments are:
+ * `err` , Error if any
+ * `stoped` , True if the process was stoped
+ */
+
+ stop: function(options, callback) {
+ var kill;
+ if (options.attach != null) {
+ console.log('Option attach was renamed to attached to be consistent with the new spawn option');
+ options.detached = !options.attach;
+ }
+ if (typeof options === 'string' || typeof options === 'number') {
+ options = {
+ pid: parseInt(options, 10),
+ detached: false
+ };
+ }
+ kill = function(pid) {
+ var cmds;
+ cmds = "for i in `ps -ef | awk '$3 == '" + pid + "' { print $2 }'`\ndo\n kill $i\ndone\nkill " + pid;
+ return exec(cmds, function(err, stdout, stderr) {
+ if (err) {
+ return callback(new Error("Unexpected exit code " + err.code));
+ }
+ options.pid = null;
+ return callback(null, true);
+ });
+ };
+ if (options.detached) {
+ return start_stop.pid(options, function(err, pid) {
+ if (err) {
+ return callback(err);
+ }
+ if (!pid) {
+ return callback(null, false);
+ }
+ return fs.unlink(options.pidfile, function(err) {
+ if (err) {
+ return callback(err);
+ }
+ return start_stop.running(pid, function(err, running) {
+ if (!running) {
+ if (options.strict) {
+ return callback(new Error("Pid file reference a dead process"));
+ } else {
+ return callback(null, false);
+ }
+ }
+ return kill(pid);
+ });
+ });
+ });
+ } else {
+ return kill(options.pid);
+ }
+ },
+ /*
+
+ `pid(options, callback)`
+ ------------------------
+ Retrieve a process pid. The pid value is return only if the command is running
+ otherwise it is set to false.
+
+ `options` , Object with the following properties:
+ * `detached` , True if the child process is not attached to the current process
+ * `cmd` , Command used to run the process, in case no pidfile is provided
+ * `pid` , Pid to kill if not running in detached mode
+ * `pidfile` , Path to the file storing the child pid
+
+
+ `callback` , Received arguments are:
+ * `err` , Error if any
+ * `pid` , Process pid. Pid is null if there are no pid file or
+ if the process isn't running.
+ */
+
+ pid: function(options, callback) {
+ if (options.attach != null) {
+ console.log('Option attach was renamed to attached to be consistent with the new spawn option');
+ options.detached = !options.attach;
+ }
+ if (!options.detached) {
+ if (options.pid == null) {
+ return new Error('Expect a pid property in attached mode');
+ }
+ return callback(null, options.pid);
+ }
+ return start_stop.file(options, function(err, file, exists) {
+ if (!exists) {
+ return callback(null, false);
+ }
+ return fs.readFile(options.pidfile, 'ascii', function(err, pid) {
+ if (err) {
+ return callback(err);
+ }
+ pid = pid.trim();
+ return callback(null, pid);
+ });
+ });
+ },
+ /*
+
+ `file(options, callback)`
+ -------------------------
+ Retrieve information relative to the file storing the pid. Retrieve
+ the path to the file storing the pid number and whether
+ it exists or not. Note, it will additionnaly enrich the `options`
+ argument with a pidfile property unless already present.
+
+ `options` , Object with the following properties:
+ * `detached` , True if the child process is not attached to the current process
+ * `cmd` , Command used to run the process, in case no pidfile is provided
+ * `pid` , Pid to kill in attach mode
+ * `pidfile` , Path to the file storing the child pid
+
+ `callback` , Received arguments are:
+ * `err` , Error if any
+ * `path` , Path to the file storing the pid, null in attach mode
+ * `exists` , True if the file is created
+ */
+
+ file: function(options, callback) {
+ var createDir, pidFileExists, start;
+ if (options.attach != null) {
+ console.log('Option attach was renamed to detached to be consistent with the spawn API');
+ options.detached = !options.attach;
+ }
+ if (!options.detached) {
+ return callback(null, null, false);
+ }
+ start = function() {
+ var dir, file;
+ if (options.pidfile) {
+ return pidFileExists();
+ }
+ dir = path.resolve(process.env['HOME'], '.node_shell');
+ file = md5(options.cmd);
+ options.pidfile = "" + dir + "/" + file + ".pid";
+ return exists(dir, function(dirExists) {
+ if (!dirExists) {
+ return createDir();
+ }
+ return pidFileExists();
+ });
+ };
+ createDir = function() {
+ return fs.mkdir(dir, 0x1c0, function(err) {
+ if (err) {
+ return callback(err);
+ }
+ return pidFileExists();
+ });
+ };
+ pidFileExists = function() {
+ return exists(options.pidfile, function(pidFileExists) {
+ return callback(null, options.pidfile, pidFileExists);
+ });
+ };
+ return start();
+ },
+ /*
+
+ `running(pid, callback)`
+ ------------------------
+
+ Test if a pid match a running process.
+
+ `pid` , Process id to test
+
+ `callback` , Received arguments are:
+ * `err` , Error if any
+ * `running` , True if pid match a running process
+ */
+
+ running: function(pid, callback) {
+ return exec("ps -ef " + pid + " | grep -v PID", function(err, stdout, stderr) {
+ if (err && err.code !== 1) {
+ return callback(err);
+ }
+ return callback(null, !err);
+ });
+ }
+};
View
48 lib/utils.js
@@ -0,0 +1,48 @@
+// Generated by CoffeeScript 1.4.0
+var existsSync, fs, path;
+
+fs = require('fs');
+
+path = require('path');
+
+existsSync = fs.existsSync || path.existsSync;
+
+module.exports = {
+ flatten: function(arr, ret) {
+ var i, _i, _ref;
+ if (ret == null) {
+ ret = [];
+ }
+ for (i = _i = 0, _ref = arr.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
+ if (Array.isArray(arr[i])) {
+ this.flatten(arr[i], ret);
+ } else {
+ ret.push(arr[i]);
+ }
+ }
+ return ret;
+ },
+ workspace: function() {
+ var dir, dirs, _i, _len;
+ dirs = require('module')._nodeModulePaths(process.argv[1]);
+ for (_i = 0, _len = dirs.length; _i < _len; _i++) {
+ dir = dirs[_i];
+ if (existsSync(dir) || existsSync(path.normalize(dir + '/../package.json'))) {
+ return path.normalize(dir + '/..');
+ }
+ }
+ },
+ checkPort: function(port, host, callback) {
+ var cmd;
+ cmd = exec("nc " + host + " " + port + " < /dev/null");
+ return cmd.on('exit', function(code) {
+ if (code === 0) {
+ return callback(true);
+ }
+ if (code === 1) {
+ return callback(false);
+ }
+ return callback(new Error('The nc (or netcat) utility is required'));
+ });
+ }
+};
View
0 lib/NullStream.coffee → src/NullStream.coffee
File renamed without changes.
View
0 lib/Request.coffee → src/Request.coffee
File renamed without changes.
View
0 lib/Response.coffee → src/Response.coffee
File renamed without changes.
View
0 lib/Shell.coffee → src/Shell.coffee
File renamed without changes.
View
0 lib/Styles.coffee → src/Styles.coffee
File renamed without changes.
View
0 lib/plugins/cloud9.coffee → src/plugins/cloud9.coffee
File renamed without changes.
View
0 lib/plugins/coffee.coffee → src/plugins/coffee.coffee
File renamed without changes.
View
0 lib/plugins/completer.coffee → src/plugins/completer.coffee
File renamed without changes.
View
0 lib/plugins/error.coffee → src/plugins/error.coffee
File renamed without changes.
View
0 lib/plugins/help.coffee → src/plugins/help.coffee
File renamed without changes.
View
0 lib/plugins/history.coffee → src/plugins/history.coffee
File renamed without changes.
View
0 lib/plugins/http.coffee → src/plugins/http.coffee
File renamed without changes.
View
0 lib/plugins/redis.coffee → src/plugins/redis.coffee
File renamed without changes.
View
0 lib/plugins/router.coffee → src/plugins/router.coffee
File renamed without changes.
View
0 lib/plugins/stylus.coffee → src/plugins/stylus.coffee
File renamed without changes.
View
0 lib/plugins/test.coffee → src/plugins/test.coffee
File renamed without changes.
View
0 lib/routes/confirm.coffee → src/routes/confirm.coffee
File renamed without changes.
View
0 lib/routes/prompt.coffee → src/routes/prompt.coffee
File renamed without changes.
View
0 lib/routes/shellOnly.coffee → src/routes/shellOnly.coffee
File renamed without changes.
View
0 lib/routes/timeout.coffee → src/routes/timeout.coffee
File renamed without changes.