finally works

This commit is contained in:
Craig Condon
2012-11-30 20:11:55 -06:00
parent 45dd2d2680
commit 11aaccdf66
4443 changed files with 947516 additions and 27340 deletions
Generated Vendored
-1
View File
@@ -1 +0,0 @@
../sardines/bin/sardines
-2
View File
@@ -1,2 +0,0 @@
[commands]
proj = subl --project project.sublime-project
-2
View File
@@ -1,2 +0,0 @@
node_modulesnode_modules
-20
View File
@@ -1,20 +0,0 @@
Copyright (c) 2011 Craig Condon
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-357
View File
@@ -1,357 +0,0 @@
### C-e-L-er-I
![Alt command line](http://i.imgur.com/DA77U.png)
### Features:
- History (up/down arrows)
- Progress Bar
- Loading/busy spinner
- Password input
- Confirmation
- Prompt
- Multi-line tables
- Build flexible commands via [beanpole](https://github.com/spiceapps/beanpole)
- OR statement
- Middleware
- Parameters
- Trees
### To Do:
- Help menu api
- Title View
- Custom colors for each view (input, loader, progress, table, etc.): exports.colors = {}
- Error handling (display of command not found)
- Add transports instead of depending on native stdin/stdout
- Ability to use online
## Usage:
### .on(event, callback)
Listens for a key (enter, up, left, backspace, etc.), or command. See [beanpole](https://github.com/spiceapps/beanpole) for documentation.
#### Hello World:
```javascript
var celeri = require('celeri');
celeri.on('hello :name', function(data)
{
console.log("Hello %s!", data.name);
});
//open up character input!
celeri.open();
//parse the command line args
celeri.parse(process.argv);
```
In terminal:
node ./hello ↩
> hello world ↩
hello world!
passed as arguments:
node ./hello hello:world ↩
hello world!
#### "OR" statement:
```javascript
celeri.on('hello :name OR hi :name', function(data)
{
console.log('Hello ' + data.name +'!');
});
celeri.on('set address :zip OR set address :city :state :zip', function(data)
{
console.log("City: %s, State: %s, Zip: %s ", data.city || 'None provided', data.state || 'None provided', data.zip);
});
```
#### Middleware "->" statement:
```javascript
celeri.on('delay :seconds', function(data)
{
console.log("delaying for %s seconds", data.seconds);
setTimeout(function(self)
{
if(!self.next()) console.log("done!");
}, Number(data.seconds) * 1000, this);
});
celeri.on('delay 1 -> say hello :name', function(data)
{
console.log('hello %s!', data.name);
});
```
here's what you get:
> delay 5 ↩
delaying for 5 seconds
done
> say hello craig ↩
delaying for 1 seconds
hello craig!
### .progress(label, percent)
```javascript
var i = 0;
var interval = setInterval(function()
{
celeri.progress('Label: ', i++);
if(i == 100) clearInterval(i);
}, 10);
```
### .loading(label)
```javascript
var spinner = celeri.loading('Processing: ');
setTimeout(function()
{
spinner.done(true);//undefined = done, true = success, false = fail
}, 1000);
````
### .prompt(label, callback)
```javascript
celeri.prompt('Username: ', function(input)
{
});
````
### .confirm(message, callback)
```javascript
celeri.confirm("Do you want to continue?", function(yes)
{
if(yes)
{
//continue
}
});
```
### .password(label[, mask], callback)
```javascript
//mask = *
celeri.password('Password: ', '*', function(input)
{
//password
});
//no mask
celeri.password('Password: ', function(input)
{
//password
});
```
### .auth(callback)
```javascript
celeri.auth(function(user, pass)
{
//auth here
});
```
### .loadHelp(filePath)
```javascript
celeri.on('help', function()
{
celeri.loadHelp(__dirname +'/help.txt');
});
```
### .drawTable(objects, ops)
```javascript
var objects = [
{
name: 'Craig',
age: 21,
interests: 'Cooking, espresso, backpacking, coding'
},
{
name: 'Tim',
age: 21,
interests: 'Design, Traveling, Photography'
}
];
celeri.drawTable(objects, {
columns: ['name','age','interests']
});
```
Gives you something like:
![Alt command line](http://i.imgur.com/oUtC9.png)
Here's a multi-line table:
![Alt command line](http://i.imgur.com/O5o47.png)
### .drawTree(tree)
Draws a tree
````javascript
//print out the contents of the celeri object
celeri.drawTree(celeri);
````
Here's another example:
![Alt command line](http://i.imgur.com/4F0e0.png)
### Let's kick it up a notch
```javascript
var celeri = require('../lib');
var credentials;
celeri.on('login OR login :user :pass', function(data)
{
//reference to the current request
var self = this;
//called after auth credentials have been entered in
function onAuth(creds)
{
//credits wrong? DO NOT CONTINUE
if(creds.user != 'user' || creds.pass != 'pass')
{
return console.log("Incorrect user / pass".red);
}
//otherwise, add the user to the CURRENT request so it can be passed
//onto the next route listener
self.user = creds.user;
//cache the credentials so the user doesn't have to login each time
credentials = creds;
//not another listener? display a success response
if(!self.next()) console.log("Logged in as %s", creds.user.green);
}
//user already logged in? pass!
if(credentials)
{
onAuth(credentials);
}
//otherwise check if the user is passed in the route
else
if(data.user && data.pass)
{
onAuth(data);
}
//or prompt for authentication
else
{
celeri.auth(function(user, pass)
{
onAuth({ user: user, pass: pass });
});
}
});
/**
* This stuff's private. The user has to be authenticated *before* this command is executed
*/
celeri.on('login -> account', function()
{
console.log('Here\'s your account info %s!', this.user.green);
});
celeri.open();
celeri.parse(process.argv);
```
Here's what you get:
![Alt command line](http://i.imgur.com/g7ywq.png)
-13
View File
@@ -1,13 +0,0 @@
var celery = require('../lib');
celery.prompt('Username: ', function(user)
{
celery.password('Password: ', function(pass)
{
console.log('user: %s'.green, user);
console.log('pass: %s'.green, pass);
});
});
celery.open();
-69
View File
@@ -1,69 +0,0 @@
var celery = require('../lib');
celery.open({
//required for middleware / or statements
//delimiter: '/',
});
var credentials;
celery.on('login OR login :user :pass', function(data)
{
var self = this;
function onAuth(creds)
{
if(creds.user != 'user' || creds.pass != 'pass')
{
return console.log("Incorrect user / pass".red);
}
self.user = creds.user;
//cache the credentials so the user doesn't have to login each time
credentials = creds;
if(!self.next()) console.log("Logged in as %s", creds.user.green);
}
//user already logged in? pass!
if(credentials)
{
onAuth(credentials);
}
//otherwise check if the user is passed in the route
else
if(data.user && data.pass)
{
onAuth(data);
}
//or prompt for authentication
else
{
celery.auth(function(user, pass)
{
onAuth({ user: user, pass: pass });
});
}
});
/**
* private account into
*/
celery.on('login -> account', function()
{
console.log('Here\'s your account info %s!', this.user.green);
});
celery.parse(process.argv);
-16
View File
@@ -1,16 +0,0 @@
var celery = require('../lib');
celery.confirm('Do you want to continue?', function(yes)
{
if(yes)
{
console.log("YES!".green);
}
else
{
console.log("NO!".red);
}
});
celery.open();
-11
View File
@@ -1,11 +0,0 @@
var celeri = require('../lib');
celeri.exec('ls', ['-help'], {
exit: function()
{
console.log("DONE");
}
});
celeri.open();
-52
View File
@@ -1,52 +0,0 @@
var celery = require('../lib');
celery.open({
prefix: 'hello > '
});
celery.on('hello :name', function(data)
{
console.log('hello %s, how are you doing?', data.name);
});
celery.on('download/:file', function(data)
{
console.log("starting");
var i = 0;
data.file = decodeURIComponent(data.file);
var interval = setInterval(function()
{
celery.progress('Downloading '+data.file+':', i++);
if(i > 100)
{
clearInterval(interval);
celery.newLine('done!');
}
}, 10);
});
celery.on(['timeout :ttl','timeout :ttl :status'], function(data)
{
var loader = celery.loading('timeout for '+data.ttl+' seconds: ');
setTimeout(function()
{
if(data.status != undefined)
{
loader.done(data.status == 'success');
}
else
{
loader.done();
}
}, Number(data.ttl) * 1000);
});
celery.parse(process.argv);
-19
View File
@@ -1,19 +0,0 @@
var celery = require('../lib');
celery.on('delay :seconds', function(data)
{
console.log("delaying for %s seconds", data.seconds);
setTimeout(function(self)
{
if(!self.next()) console.log("done!");
}, Number(data.seconds) * 1000, this);
});
celery.on('delay 1 -> say hello :name', function(data)
{
console.log('hello %s!', data.name);
});
celery.open();
-44
View File
@@ -1,44 +0,0 @@
var celery = require('../lib');
var objects = [
{
name: 'Craig',
age: 21,
bio: 'Cras dictum convallis fermentum. Quisque ut urna velit, at porta nibh. Nam iaculis dignissim nisl, non elementum tortor iaculis et. Curabitur vulputate, sapien eget'
},
{
name: 'Tim',
age: 21,
bio: 'Praesent ligula est, pellentesque vel euismod vitae, condimentum convallis odio. Suspendisse potenti. Fusce lacus arcu, bibendum in gravida at, dictum vitae mauris. Cras viverra, dui ac elementum fringilla, purus mauris rutrum nibh, id ultrices diam magna id ante. Integer elit ligula, cursus at accumsan in, scelerisque at dui. Sed pellentesque justo sit amet nibh sodales sed malesuada nulla cursus. Maecenas eu felis leo, a volutpat sapien. Duis eget porta urna. Maecenas ligula elit, vulputate ac bibendum eu, dapibus ut lacus. Fusce ac tincidunt eros. Pellentesque ut turpis ac ante interdum rutrum. Suspendisse tempor lobortis semper.'
},
{
name: 'Michael',
age: 23,
bio: 'Vestibulum ligula elit, vehicula a lacinia at, aliquet sed felis. In rutrum pulvinar ultrices. Donec interdum ullamcorper neque ut pretium. Donec varius massa vitae ipsum feugiat feugiat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce a nulla a odio congue hendrerit in non tellus. Cras gravida vestibulum augue vitae dapibus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Fusce porttitor rutrum faucibus. Vivamus interdum porta orci, vitae fermentum nunc vestibulum at. Maecenas porttitor sollicitudin pharetra. Ut risus mauris, tincidunt vitae laoreet quis, rhoncus eget risus'
},
{
name: 'Sarah',
age: 19,
bio: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin fringilla interdum nibh eget luctus. In facilisis varius lacus, ut adipiscing turpis posuere a. Nunc id sapien eu urna'
}
];
celery.drawTable(objects, {
columns: {
name: 15,
age: 5,
bio: 30
},
horz: ' ',
});
celery.open();
-29
View File
@@ -1,29 +0,0 @@
var celery = require('../lib');
var objects = [ { name: 'bean.cupboard.scaffold',
hasUpdates: '',
published: 'published: 2 hours agofdsfsafsfadffdsf afsd fasf as fsd fsdf sdf sdf sd ' },
{ name: 'teamdigest',
hasUpdates: '',
published: 'published: 2 hours ago' }];
console.log(celery.columns())
celery.drawTable(objects, {
columns: [{
width: 15,
minWidth:20,
name: 'name'
},
{
name: 'published',
width:15,
align:'right'
}],
horz: ' ',
vert: '|'
});
celery.open();
-59
View File
@@ -1,59 +0,0 @@
var celery = require('../lib');
var objects = [ { command: 'init',
desc: 'Adds a project in cwd to cupboard.' },
{ command: 'remove <proj>',
desc: 'Removes project from cupboard.' },
{ command: '<cmd> <proj>',
desc: 'Calls custom command specified in project cupboard config.' },
{ command: 'untouch <proj>',
desc: 'Flags given project as updated.' },
{ command: 'publish <proj>',
desc: 'Publishes target project.' },
{ command: 'updates',
desc: 'List all projects with updates.' },
{ command: 'list',
desc: 'List all projects in cupboard organized by most recently updated.' },
{ command: 'install <cupboard plugin>',
desc: 'Installs a third-party cupboard plugin.' },
{ command: 'uninstall <cupboard plugin>',
desc: 'Uninstalls a third-party cupboard plugin.' },
{ command: 'plugins',
desc: 'Lists all third-party plugins.' },
{ command: 'scaffold <type>',
desc: 'Initializes target scaffold in cwd' },
{ command: 'github <proj>',
desc: 'Open github page of target project' },
{ command: 'github-issues <proj>',
desc: 'Open github issues of target project.' },
{ command: 'help', desc: 'Shows the help menu.' },
{ command: 'dir <proj>',
desc: 'Returns the project path.' },
{ command: 'details <proj>',
desc: 'Returns details about the given project such as modified files, and number of updates.' } ];
celery.drawTable(objects, {
columns: [ {
minWidth: 25,
width: 20,
name: 'command'
},
{
name: 'desc',
width: 80,
align: 'left'
} ],
pad: {
left: 10,
right: 10,
top: 2,
bottom: 2
},
ellipsis: true
});
celery.open();
-34
View File
@@ -1,34 +0,0 @@
var celeri = require('../lib');
celeri.drawTree({
'home/':{
'network/':{
'apps/' :[
"cliqly",
"clove",
"team digest"
],
'public/': {
'git/':{
'some-project':[
'index.js'
]
},
'npm/':{
'some-project':['index.js']
}
}
}
},
'downloads/': {
'libcpp': [
'makefile'
]
}
})
//celeri.drawTree(celeri);
celeri.open();
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

-401
View File
@@ -1,401 +0,0 @@
var tty = require('tty'),
fs = require('fs'),
Structr = require('structr'),
beanpole = require('beanpole'),
_buffer = '',
router = beanpole.router();
exports.utils = require('./utils');
//CLEAN ME!!!!!!!!!!!!!!!!!
_delimiterRepl = /\s+/g,
_delimiter = ' ';
var _queue = [],
_queueRunning = false;
/**
* queued commands
*/
exports.next = function(callback)
{
exports.queue(callback)();
return this;
}
exports.queue = function(callback)
{
return function()
{
var args = arguments,
listeners = [],
self = {
next: function()
{
var args = arguments;
listeners.forEach(function(listener)
{
listener.apply(null, args);
})
_queueRunning = true;
if(_queue.length)
{
_queue.shift()();
}
else
{
_queueRunning = false;
}
},
attach: function(callback)
{
listeners.push(callback);
}
}
_queue.push(function()
{
callback.apply(self, args);
});
if(!_queueRunning)
{
self.next();
}
return exports;
}
return exports;
}
/**
* spacing between arguments
*/
exports.delimiter = function(value)
{
if(!arguments.length) return _delimiter;
_delimiterRepl = new RegExp(value + '+','ig');
return _delimiter = value;
};
/**
* Converts CLI arguments to beanpole routes. Nifty huh?
*/
function _toRoute(type)
{
return type.replace(_delimiterRepl, '\/');
}
/**
* size of the terminal window
*/
exports.size = function()
{
return tty.getWindowSize();
}
/**
* number of columns in the terminal window (characters / width)
*/
exports.columns = function()
{
return exports.size()[1] || exports.size();
}
/**
* number of rows in the terminal window
*/
exports.rows = function()
{
return exports.size()[0];
}
var _inputPrefix = '> ';
exports.inputPrefix = function(prefix)
{
if(!arguments.length) return _inputPrefix;
return _inputPrefix = prefix || '';
}
exports.on = function(type, callback)
{
if(typeof type == 'object')
{
var disposables = [];
if(type instanceof Array)
{
for(var i = type.length; i--;)
{
disposables.push(exports.on(type[i], callback));
}
}
else
{
for(var t in type)
{
disposables.push(exports.on(t, type[t]));
}
}
return {
dispose: function()
{
disposables.forEach(function(disposable)
{
disposable.dispose();
});
}
};
}
return router.on('push /'+_toRoute(type), callback);
}
exports.emit = function(type, data)
{
//catch parse errors
try
{
return router.push(_toRoute(type), data, { meta: { passive: 1 } });
}
catch(e)
{
//console.log(e.stack);
}
return false;
}
exports.buffer = function(value, ignoreReplace)
{
if(!arguments.length) return _buffer;
_buffer = value;
return !ignoreReplace ? exports.replaceLine(value) : value;
}
/**
* Replaces the current line in the terminal window
*/
exports.replaceLine = function(buffer, cursorTo)
{
//need to clear the line before replacing it
process.stdout.clearLine();
//set the cursor to zero, otherwise we'll have padding we don't want
exports.cursor(0);
//write the buffer
process.stdout.write(buffer);
//set the cursor to the new position!
exports.cursor(cursorTo != undefined ? cursorTo : buffer.length);
return _buffer = buffer;
}
/**
* inserts text into the current line
*/
exports.insertText = function(str, position)
{
var buffer = exports.buffer();
exports.replaceLine(buffer.substr(0, position) + str + buffer.substr(position), position + str.length);
}
/**
* Takes a chunk of text out of the current line
*/
exports.spliceLine = function(position, length, newCursor)
{
var buffer = exports.buffer();
exports.replaceLine(buffer.substr(0, position) + buffer.substr(position + length), newCursor);
}
var _cursorPosition = 0;
/**
*/
exports.cursor = function(position)
{
if(!arguments.length) return _cursorPosition;
process.stdout.cursorTo(position);
return _cursorPosition = position;
}
function stringRep(key)
{
var chain = '';
for(var prop in key)
{
if(!key[prop]) continue;
if(key[prop] == true)
{
chain = prop+'-'+chain;
}
else
{
chain += key.name;
}
}
return chain;
}
exports.write = function(string)
{
process.stdout.write(string);
}
exports.newLine = function(buffer)
{
if(buffer) exports.write(buffer);
exports.write('\n');
exports.buffer('');
}
exports.open = function(ops)
{
if(ops)
{
if(ops.prefix) exports.inputPrefix(ops.prefix);
if(ops.delimiter) exports.delimiter(ops.delimiter);
}
if(exports.opened) return;
exports.opened = true;
stdin = process.openStdin();
stdin.setEncoding('utf8');
tty.setRawMode(true);
stdin.on('keypress', function(c, key)
{
//integer?
if(!key) key = { name: c.toString() }
var chain = stringRep(key),
emitSuccess = exports.emit(chain);
//not a handled item to boot? Probably enter, delete, left, right, up, etc. Append it.
if(!emitSuccess && c)
{
if(!_buffer.length)
{
exports.insertText(exports.inputPrefix(), _cursorPosition);
}
exports.insertText(c, _cursorPosition);
//exports.insertText(c, Math.max(_cursorPosition, exports.inputPrefix().length));
}
//new line? reset
if(key && key.name == 'enter')
{
//exports.replaceLine(exports.buffer().magenta);
process.stdout.write('\n');
_buffer = '';
_cursorPosition = 0;
//exports.insertText(exports.inputPrefix(), _cursorPosition);
//exports.cursor(0);
}
//for custom handlers: password, confirm, etc.
exports.emit('keypress', { char: c, key: key });
//character code?
if(key.name.length == 1)
{
exports.emit('charpress', c);
}
});
}
exports.parse = function(args)
{
var self = this;
args.forEach(function(arg)
{
var argParts = arg.split(':');
for(var i = argParts.length; i--;)
{
argParts[i] = encodeURIComponent(argParts[i]);
}
self.emit(argParts.join('/'));
});
}
fs.readdirSync( __dirname + '/modules').forEach(function(module)
{
require( __dirname + '/modules/'+module).plugin(exports);
});
-18
View File
@@ -1,18 +0,0 @@
exports.plugin = function(cli)
{
cli.auth = cli.queue(function(callback)
{
var self = this;
self.attach(callback);
cli.prompt('Username: ', function(user)
{
cli.password('Password: ','*', function(pass)
{
self.next(user, pass);
});
});
});
}
-22
View File
@@ -1,22 +0,0 @@
exports.plugin = function(cli)
{
cli.on('backspace', function()
{
var pos = cli.cursor();
if(pos == cli.inputPrefix().length) return;
cli.spliceLine(pos-1, 1, pos-1);
//cli.replaceLine(buffer.substr(0, pos-1) + buffer.substr(pos, buffer.length-pos), pos-1);
});
cli.on('delete', function()
{
var pos = cli.cursor();
cli.spliceLine(pos, 1, pos);
});
}
-38
View File
@@ -1,38 +0,0 @@
exports.plugin = function(cli)
{
cli.confirm = cli.queue(function(label, callback)
{
label += ' [y/n]: ';
cli.write(label);
var input = '', yes = 'y', no = 'n',
self = this;
self.attach(callback);
var disposable = cli.on({
'keypress': function(data)
{
input = data.char;
cli.buffer(label + input);
},
'enter': function()
{
if(input != yes && input != no)
{
return cli.write('\nPlease enter "'+yes+'" or "'+no+'"');
}
disposable.dispose();
setTimeout(self.next, 1, input == 'y');
}
});
});
}
-38
View File
@@ -1,38 +0,0 @@
exports.plugin = function(cli)
{
cli.on('enter', function()
{
var buffer = cli.buffer(),
commands = buffer.split('&&');
commands.forEach(function(command)
{
command = command.replace(cli.inputPrefix(),'');
var args = command.match(/((["'])([^\\](?!\2)|\\.|\w)+\2|[^\s"']+)/ig);
if(!args) return;
for(var i = args.length; i--;)
{
args[i] = encodeURIComponent(args[i]);
}
function emit(operation)
{
if(!cli.emit(args.join(cli.delimiter())))
{
//console.log('illegal operation: "%s"'.red, operation);
//cli.emit('help');
}
}
setTimeout(emit, 1, args.join(cli.delimiter()));
});
});
}
-87
View File
@@ -1,87 +0,0 @@
var spawn = require('child_process').spawn;
exports.plugin = function(cli)
{
/**
* goal = dead simple and painless.
*/
cli.exec = function(command, args, ops, callback)
{
if(typeof args == 'function')
{
callback = args;
args = [];
ops = {};
}
else
if(!(args instanceof Array))
{
callback = ops;
ops = args;
args = [];
}
if(typeof ops == 'function')
{
callback = ops;
ops = {};
}
//no callbacks? THEY MUST EXIST!!!!
if(!callback) callback = ops.callback || ops;
var callbacks = {};
if(typeof callback != 'function')
{
callbacks = callback;
}
if(!callbacks.error) callbacks.error = function(){};
if(!callbacks.exit) callbacks.exit = function(){};
if(!callbacks.data) callbacks.data = function(){};
var procOps = {
cwd: ops.cwd,
env: ops.env,
customFds: ops.customFds
};
var proc = spawn(command, ops.args || args, procOps), errors = [], buffer = [];
proc.stdout.on('data', function(data)
{
buffer.push(data);
callbacks.data(data);
});
proc.stderr.on('data', function(data)
{
errors.push(data);
callbacks.error(data);
});
proc.on('exit', function(code)
{
if(typeof callback == 'function')
{
callback(errors.length ? errors.join(' ') : false, buffer.length ? buffer.join(' ') : false);
}
else
if(callbacks.exit)
{
callbacks.exit(code);
}
});
}
}
-42
View File
@@ -1,42 +0,0 @@
var fs = require('fs');
exports.plugin = function(cli)
{
//cli.help = function(){};
cli.openHelp = function(filePath, prefix)
{
if(!prefix) prefix = '';
var helpTxt = fs.readFileSync(filePath,'utf8'), coloredText;
//colored text
while(coloredText = helpTxt.match(/<(.*?)>([\w\W]*?)<\/\1>/))
{
var colorAttrs = coloredText[1].split(' '),
text = coloredText[2];
colorAttrs.forEach(function (attr)
{
text = text[attr];
});
helpTxt = helpTxt.replace(coloredText[0],text);
}
/*helpTxt.split('\n').forEach(function(line)
{
console.log(prefix+line);
});*/
console.log(helpTxt);
}
/*cli.on('help', function()
{
cli.help();
});*/
}
-33
View File
@@ -1,33 +0,0 @@
exports.plugin = function(cli)
{
var history = [], cursor = 0, max = 20;
cli.on('enter', function()
{
history.push(cli.buffer());
if(history.length > max)
{
history.shift();
}
cursor = history.length-1;
});
cli.on('up', function()
{
if(cursor == -1) return;
cli.replaceLine(history[cursor--]);
});
cli.on('down', function()
{
if(cursor == history.length-1) return;
cli.replaceLine(history[++cursor]);
});
}
-43
View File
@@ -1,43 +0,0 @@
exports.plugin = function(cli)
{
cli.loading = function(label, callback)
{
if(typeof label == 'function')
{
callback = label;
label = '';
}
if(!label) label = '';
function done(message, success)
{
if(!message) message = success == undefined ? 'done' : (success ? 'success' : 'fail') ;
var color = success == undefined ? 'grey' : (success ? 'green' : 'red');
clearInterval(interval);
cli.replaceLine(label + message[color]['bold']);
cli.newLine();
}
if(callback) callback(done);
var seq = '\|/';
pos = 0,
interval = setInterval(function()
{
cli.replaceLine(label+'['+seq[pos++%seq.length].blue+'] ');
}, 200);
return {
done: done
};
}
}
-13
View File
@@ -1,13 +0,0 @@
exports.plugin = function(cli)
{
cli.on('left', function()
{
cli.cursor(cli.cursor()-1);
});
cli.on('right', function()
{
cli.cursor(cli.cursor()+1);
});
}
-58
View File
@@ -1,58 +0,0 @@
exports.plugin = function(cli)
{
function getMask(mask, length)
{
if(!mask) return '';
var buffer = '';
for(var i = length; i--;)
{
buffer += mask;
}
return buffer;
}
cli.password = cli.queue(function(label, mask, callback)
{
if(typeof mask == 'function')
{
callback = mask;
mask = undefined;
}
cli.write(label);
var input = '',
self = this;
self.attach(callback);
var disposable = cli.on({
'backspace': function()
{
if(!input.length) return;
input = input.substr(0, input.length-1);
},
'keypress': function(data)
{
if(data.key.name != 'backspace')
input += data.char;
cli.buffer(label+getMask(mask, input.length));
},
'enter': function()
{
setTimeout(self.next,1,input);
disposable.dispose();
}
});
});
}
-33
View File
@@ -1,33 +0,0 @@
exports.plugin = function(cli)
{
var spaces = 20,
perc = 0;
function percentBuffer(label, color)
{
var diff = Math.round((spaces/100) * perc);
return label + ' ['+ cli.utils.fill('#'[color],diff,' ', spaces-diff) + '] '+ perc +'% ';
}
cli.progress = function(label, percent)
{
//fail
if(percent === false)
{
cli.replaceLine(percentBuffer(label, perc, 'red'));
return cli.newLine();
}
perc = Math.min(percent, 100);
cli.replaceLine(percentBuffer(label, perc == 100 ? 'green' : 'blue'));
if(perc == 100)
{
return cli.newLine();
}
};
}
-41
View File
@@ -1,41 +0,0 @@
exports.plugin = function(cli)
{
cli.prompt = cli.queue(function(label, callback)
{
var input = '',
self = this;
self.attach(callback);
function write()
{
cli.replaceLine(label+input);
}
var disp = cli.on({
'backspace': function()
{
if(!input.length) return;
input = input.substr(0, input.length-1);
},
'keypress': function(data)
{
if(data.key.name != 'backspace')
input += data.char;
write();
},
'enter': function()
{
setTimeout(self.next, 1, input);
disp.dispose();
}
});
write();
});
}
-7
View File
@@ -1,7 +0,0 @@
exports.plugin = function(cli)
{
cli.on('ctrl-c', function()
{
process.exit();
});
}
-6
View File
@@ -1,6 +0,0 @@
exports.plugin = function(cli)
{
cli.section = function(message)
{
}
}
-292
View File
@@ -1,292 +0,0 @@
var Structr = require('structr');
exports.plugin = function(cli)
{
function normalizeOptions(ops)
{
var cliWidth = cli.columns();
var columns = ops.columns,
normCols = [],
vert = ops.vertical || ops.vert || ' ',
horz = ops.horizontal || ops.horz || '',
pad = Structr.copy(ops.pad, { left: 0, right: 0, top: 0, bottom: 0 });
if(ops.padRight) pad.right = ops.padRight;
if(ops.padLeft) pad.left = ops.padLeft;
if(ops.padTop) pad.top = ops.padTop;
if(ops.padBottom) pad.bottom = ops.padBottom;
if(!ops.width) ops.width = cliWidth;
ops.width = Math.min(ops.width, cliWidth - pad.left - pad.right);
if(ops.border)
{
if(vert == ' ') vert = ' | ';
if(horz == '') horz = '';
}
//sum width of all columns combined
var sumWidth = 0;
for(var prop in columns)
{
var columnValue = columns[prop],
normCol = { minWidth: 6, align: 'left' },
tocv = typeof columnValue;
normCols.push(normCol)
//['columnName']
if(tocv == 'string')
{
normCol.name = columnValue;
}
else
{
//{columnName:value}
if(typeof prop == 'string')
{
normCol.name = prop;
}
if(tocv == 'number')
{
normCol.width = columnValue;
}
else
if(tocv == 'object')
{
Structr.copy(columnValue, normCol);
}
}
if(!normCol.width) normCol.width = 100/columns.length;
sumWidth += normCol.width;
}
//subtract vert padding
var sumActualWidth = 0,
colsTaken = 0,
numCols = normCols.length;
var tableWidth = ops.width - vert.length * numCols;
for(var i = 0; i < numCols; i++)
{
var column = normCols[i]/*,
isLast = i == numCols-1,
align = column.align;
if(!align)
{
column.align = isLast ? 'right' : 'left';
}*/
var percWidth = Math.round(column.width/sumWidth * tableWidth);
actualWidth = Math.max(column.minWidth, percWidth),
//diff between calculated perc width, and min width. used for penalization
difference = actualWidth - percWidth;
sumActualWidth += actualWidth;
//this may happen if there are too many minWidths specified. In which case, not all columns
//will be shown. Actual width will be set to zero
if(sumActualWidth > ops.width) actualWidth -= Math.min(actualWidth, sumActualWidth - tableWidth);
//actual width CANNOT be less than min width. e.g: single char column couold be huge.
if(actualWidth < column.minWidth) actualWidth = 0;
column.actualWidth = actualWidth;
//penalize the rest of the columns. Width must *not* cli width
sumWidth -= difference;
}
return {
columns: normCols,
ellipsis: ops.ellipsis,
vert: vert,
horz: horz,
pad: pad,
width: ops.width,
numColumns: numCols,
showLabels: !!ops.showLabels
}
}
function addLines(colLines, value, column)
{
var newLines = (value ? value.toString() : '').split(/[\r\n]/g);
for(var i = 0, n = newLines.length; i < n; i++)
{
//trim whitespace off the ends. line breaks = whitespace
var buffer = newLines[i].replace(/^\s+|\s+$/g,'');
var colors = buffer.match(/\u001b\[\d+m/g) || [];
var padding = column.actualWidth - buffer.length + colors.join('').length;
switch(column.align)
{
case 'right':
buffer = cli.utils.padLeft(buffer, padding, ' ');
break;
case 'center':
buffer = cli.utils.pad(buffer, Math.floor(padding/2), ' ', Math.ceil(padding/2));
break;
default:
buffer = cli.utils.padRight(buffer, padding, ' ');
break;
}
colLines.push(buffer);
}
}
function getRowLineTable(item, ops)
{
var lineTable = [],
numLines = 0,
cols = ops.numColumns;
//get the lines.
for(var j = 0, jn = cols; j < jn; j++)
{
var column = ops.columns[j],
value = item[ops.columns[j].name] || '',
colLines = [],
actualWidth = column.actualWidth;
//width not present? skip the column
if(!actualWidth) continue;
if(value.length > actualWidth)
{
if(ops.ellipsis)
{
var newBuffer = value.substr(0, actualWidth-3) + cli.utils.repeat('.', Math.min(actualWidth, 3));
addLines(colLines, newBuffer, column);
}
else
{
var start = 0;
//splice apart the single line, treating each chunk as a new line
for(var k = actualWidth; k < value.length + actualWidth; k += actualWidth)
{
addLines(colLines, value.substr(start, actualWidth), column);
start = k;
}
}
}
else
{
addLines(colLines, value, column);
}
//if this column lines
numLines = Math.max(numLines, colLines.length);
//next add the line - need to check against all lines now
lineTable.push(colLines);
//need to go through all the column lines again, and
//make sure all the lines match up properly
for(var k = 0, kn = lineTable.length; k < kn; k++)
{
var line = lineTable[k];
//must have same number of lineTable
if(line.length < numLines)
{
addLines(lineTable[k], cli.utils.repeat('\n', numLines - line.length -1), ops.columns[k]);
}
}
}
var rows = [];
//inversed
for(var i = 0, n = lineTable.length; i < n; i++)
{
var col = lineTable[i];
//each row
for(var j = 0, jn = col.length; j < jn; j++)
{
if(!rows[j]) rows[j] = [];
rows[j].push(col[j]);
}
}
return rows;
}
function logRow(ops, buffer)
{
console.log(cli.utils.repeat(' ', ops.pad.left) + buffer);
}
function drawBreak(ops)
{
if(ops.horz) logRow(ops, cli.utils.repeat(ops.horz, ops.width));
}
function drawRow(lineTable, ops)
{
for(var i = 0, n = lineTable.length; i < n; i++)
{
logRow(ops, lineTable[i].join(ops.vert))
}
drawBreak(ops);
}
function drawTable(source, ops)
{
if(ops.pad.top) console.log(cli.utils.repeat('\n', ops.pad.top-1));
drawBreak(ops);
for(var i = source.length; i--;)
{
var lineTable = getRowLineTable(source[i], ops)
drawRow(lineTable, ops);
}
if(ops.pad.bottom) console.log(cli.utils.repeat('\n', ops.pad.bottom-1));
}
cli.drawTable = function(source, ops)
{
ops = normalizeOptions(ops);
drawTable(source, ops);
// console.log(ops)
}
}
-275
View File
@@ -1,275 +0,0 @@
//oh my god what have I written. Quick, look away!!!
exports.plugin = function(cli)
{
//draw table params & columns
//cli.drawTable(..., {name: 20, age: 20}
//TODO
function addLines(ops)
{
var columnWidth = ops.columnWidth,
lines = ops.lines,
buffer = ops.buffer,
currentWidth = ops.currentWidth,
align = ops.align,
ellipsis = ops.ellipsis,
windowWidth = ops.windowWidth,
columnIndex = ops.columnIndex,
columns = ops.columns,
vert;
//last element? fill in the rest
if(columnIndex == columns.length-1)
{
columnWidth = windowWidth - currentWidth;
vert = '';
}
else
{
vert = ops.vert;
}
var maxWidth = columnWidth - vert.length;
buffer.split('\n').forEach(function(line, index)
{
if(line.length > maxWidth)
{
//text too long? option to still keep it a one-liner
if(ellipsis)
{
line = line.substr(0, maxWidth-3) + '...';
}
else
{
var start = 0;
var newLines = [];
for(var i = maxWidth; i < line.length + maxWidth; i += maxWidth)
{
newLines.push(line.substr(start, maxWidth));
start = i;
}
return addLines({
buffer: newLines.join('\n'),
columnWidth: columnWidth,
currentWidth: currentWidth,
align: ops.align,
windowWidth: windowWidth,
vert: vert,
lines: lines,
columnIndex: columnIndex,
columns: columns
});
}
}
padding = Math.max(columnWidth - line.length, 0);
var colStr = '';
switch(align)
{
case 'right':
colStr = cli.utils.padLeft(line, padding, ' ');
break;
case 'center':
colStr = cli.utils.pad(str, Math.floor(padding/2), ' ', Math.ceil(padding/2));
break;
default:
colStr = cli.utils.padRight(line, padding, ' ');
break;
}
if(!lines[index] )
{
var buffer = '';
for(var i = 0, n = columnIndex; i < n; i++)
{
buffer += cli.utils.padLeft(vert, columns[i].width-vert.length, ' ');
}
lines[index] = buffer;
}
else
{
console.log('GGG'+colStr);
console.log(index)
}
//console.log(maxWidth + " " + currentWidth+" "+lines[index].length);
lines[index] += colStr + vert;
});
}
cli.drawTable2 = cli.table = function(objects, ops)
{
if(!(objects instanceof Array)) objects = [objects];
var windowWidth = ops.width || cli.columns(),
columns = ops.columns,
colArray = [],
vert = ops.vertical || ops.vert || ' ',
horz = ops.horizontal || ops.horz || '',
ellipsis = ops.ellipsis;
if(ops.padRight) windowWidth -= ops.padRight;
if(ops.border)
{
if(vert == ' ') vert = ' | ';
if(horz == '') horz = '';
}
var tableWidth = 0,
title = {},
medianColWidth;
//convert the object into an array
if(columns instanceof Array)
{
colArray = columns;
var numColumns = colArray.length, medianColWidth = Math.round(numColumns/windowWidth)
for(var i = numColumns; i--;)
{
var col = colArray[i];
if(typeof col == 'string')
{
colArray[i] = col = {
name: col,
width: medianColWidth
};
}
title[col.name] = col.name;
tableWidth += col.width;
}
}
else
{
for(var property in columns)
{
var param = columns[property];
if(typeof param == 'number')
{
param = {
width: param,
};
}
param.name = property;
tableWidth += param.width || 0;
title[param.name] = param.name;
colArray.push(param);
}
}
//objects.unshift(title);
objects.forEach(function(object)
{
var lines = [];
var buffer = '', currentWidth = 0;
for(var i = 0, n = colArray.length; i < n; i++)
{
var columnInfo = colArray[i];
if(typeof columnInfo == 'string')
{
columnInfo = {
name: columnInfo
};
}
if(!columnInfo.width) columnInfo.width = Math.round(100/n) + '%';
columnName = columnInfo.name;
//percent
if(true || typeof columnInfo.width == 'string')
{
//var cw = columnInfo.width.substr(0, columnInfo.width.length-1);
columnWidth = Math.min(columnInfo.width, Math.floor((columnInfo.width/tableWidth) * windowWidth));
}
else
{
columnWidth = columnInfo.width;
}
//the string element. could be from a method, prop, or it's undefined
var str = (columnInfo.get ? columnInfo.get(object) : object[columnName] || 'Undefined').toString();
addLines({
buffer: str,
lines: lines,
columnWidth: columnWidth,
currentWidth: currentWidth,
align: columnInfo.align,
windowWidth: windowWidth,
vert: vert,
ellipsis: ellipsis,
columnIndex: i,
columns: colArray
});
currentWidth += columnWidth + vert.length;
}
if(horz.length) console.log(cli.utils.repeat(horz, windowWidth));
lines.forEach(function(line)
{
console.log(line);
});
});
if(horz) console.log(cli.utils.repeat(horz,windowWidth));
}
}
-95
View File
@@ -1,95 +0,0 @@
utils = require('../utils'),
Structr = require('structr');
//see http://en.wikipedia.org/wiki/Box-drawing_characters
exports.plugin = function(cli)
{
//┌──
//├──
//└──
cli.drawTree = cli.tree = function(tree, ops, tab)
{
if(!ops) ops = {};
var parts = { };
if(ops.pretty)
{
parts = {
pipe: '|',
tee: '┬',
dash: '─',
leftCorner: '└',
left: '├',
branch: utils.repeat('─', 1)
}
}
//plays a little more nicely with fonts such as terminus
else
{
parts = {
pipe: '|',
tee: '+',
dash: '-',
leftCorner: '+',
left: '|',
branch: utils.repeat('-', 1)
}
}
Structr.copy(ops.parts || {}, parts);
parts.tabs = utils.repeat(' ',parts.branch.length+1);
if(!tab) tab = '';
var n = utils.objectSize(tree),
i = 0,
printedBreak = false;
for(var index in tree)
{
var childrenOrValue = tree[index],
toc = typeof childrenOrValue,
toi = typeof index,
edge = i < n-1 ? parts.left : parts.leftCorner;
var value = !isNaN(Number(index)) ? childrenOrValue : index + ': ' + childrenOrValue;
console.log('%s%s%s %s', tab, edge, parts.branch + ((toc == 'object') ? parts.tee : parts.dash), toc != 'object' ? value: index);
if(toc == 'object')
{
printedBreak = cli.drawTree(childrenOrValue, ops, n > 1 && i < n-1 ? tab + parts.pipe + parts.tabs.substr(1) : tab + parts.tabs);
}
/*else
if(toc == 'string' || toc == 'number')
{
}*/
i++;
}
//add extra breaks for folders - a little more readable
if(!printedBreak)
{
console.log('%s',tab);
printedBreak = true;
}
return printedBreak;
}
}
-44
View File
@@ -1,44 +0,0 @@
exports.padLeft = function(buffer, n, char)
{
return exports.repeat(char, n) + buffer;
}
exports.padRight = function(buffer, n, char)
{
return buffer + exports.repeat(char, n);
}
//pad on left & right
exports.pad = function(buffer, ln, char, rn)
{
return exports.repeat(char, ln) + buffer + exports.repeat(char, rn || ln);
}
exports.fill = function(leftChar, ln, rightChar, rn)
{
return exports.repeat(leftChar, ln) + exports.repeat(rightChar, rn);
}
exports.repeat = function(char, n)
{
var buffer = '';
for(var i = Math.abs(n); i--;)
{
buffer += char;
}
return buffer;
}
exports.objectSize = function(target)
{
var n = 0;
for(var i in target) n++;
return n;
}
-2
View File
@@ -1,2 +0,0 @@
[commands]
proj = subl --project project.sublime-project
-2
View File
@@ -1,2 +0,0 @@
node_modules
n
-20
View File
@@ -1,20 +0,0 @@
Copyright (c) 2011 Craig Condon
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-392
View File
@@ -1,392 +0,0 @@
## Beanpole - Routing framework
### What are some features?
- Syntactic sugar (see below).
- Works with many protocols: amqp, http, websockets, etc.
- Hooking with other applications is a breeze with [daisy](https://github.com/spiceapps/daisy).
- Works well with coffeescript
### Projects using Beanpole
- [celeri](https://github.com/spiceapps/celeri) - CLI library
- [bonsai](https://github.com/spiceapps/bonsai) - application server
- [leche](https://github.com/spiceapps/leche) - Framework to build frontend / backend applications with the same code.
- [daisy](https://github.com/spiceapps/daisy) - Expose beanpole to: http, websockets, amqp (rabbitmq), etc.
- [beandocs](https://github.com/spiceapps/beandocs) - Generate documentation from your beanpole route comments.
- [beanprep](https://github.com/spiceapps/beanprep) - Scans beans in a given directory, and installs their dependencies.
- [cupboard](https://github.com/spiceapps/beanprep) - Reverse package manager.
### Beanpole ports
- [Actionscript](https://github.com/spiceapps/beanpole.as)
- [C++](https://github.com/spiceapps/beanpoll)
### Overview
![Alt ebnf diagram](http://i.imgur.com/v1wdO.png)
The basic route consists of a few parts: the `type` of route, and the `channel`. Here are some examples:
router.on('pull hello/:name', ...);
and
router.on('push hello/:name', ...);
#### Push Routes:
- Used to broadcast a message, or change (1 to many).
- Doesn't expect a response.
- Multiple listeners per route.
#### Pull Routes:
- Used to request data from a particular route (1 to 1).
- Expects a response.
- One listener per route.
- examples:
- request to http-exposed route
Using both `push`, and `pull` allows you to **bind** to a particular route. For example:
````javascript
var _numUsers = 0;
//numUser getter / setter function
function numUsers(value)
{
if(!arguments.length) return _numUsers;
_numUsers = value;
router.push('users/online', value);
}
//the request handler. This could be called in-app. It's also just as easily exposable as an API to http, websockets, etc.
router.on('pull users/online', function(request)
{
request.end(numUsers());
});
//pull num users initially, then listen for when num users changes.
router.on('push -pull users/online', function(response)
{
//handle change here..
console.log(response); //0, 3, 10...
});
//triggers above listener
numUsers(3);
numUsers(10);
````
Okay, so you might have noticed I added something funky here: `-pull` - that's a tag. Tags are structured like so:
router.on('pull -tagName hello/:route', ...);
or, you can add a value to it:
router.on('pull -method=GET hello/:route', ...);
As mentioned above, you can only have *one* listener per `pull` route. HOWEVER, you can have multiple listeners per `pull` route *if* you provide different tag values. For example:
````javascript
router.on({
/**
* returns the given user
*/
'pull -method=GET users/:userId': function(request)
{
//get the specific user
},
/**
* updates a user
*/
'pull -method=UPDATE users/:userId': function(request)
{
//update user here
},
/**
* deletes a user
*/
'pull -method=DELETE users/:userId': function(request)
{
//delete user
}
});
````
The above chunk of code is well suited for a REST-ful api without explicilty writing it *for* an http server. It can be used for any protocol. For example - say I wanted to *delete* a user using the code above:
````javascript
router.pull('users/' + someUserId, { tag: { method: 'DELETE'} }, function()
{
//delete user response
});
````
You might have guessed - tags can be used to filter routes. Okay, onto something a little more advanced: **middleware**. Here's an example:
````javascript
router.on({
/**
*/
'pull authorize': function(request)
{
if(request.data.secret != 'superSecret')
{
request.end('You shall not pass!');
}
else
{
//onto the next route
request.next();
}
},
/**
*/
'pull -method=GET authorize -> my/profile': function(request)
{
request.end('Super secret stuff!');
}
});
````
The token `->` denotes `my/profile` must go *through* the `authorize` route. Here are a few more use-cases:
````javascript
router.on({
/**
*/
'pull post/body': function(request)
{
//post http request body here. This is implemented in daisy
},
/**
*/
'pull session': function(request)
{
//initialize cookies for the user. Again, implemented in daisy
},
/**
*/
'pull -method=POST post/body -> session -> upload/video': function()
{
//passed through 2 routes before getting here.
},
/**
*/
'pull cache/:ttl': function(request)
{
//used to check if the *next* route is cached. If it is, then return the value vs continuing
},
/**
*/
'pull cache/10000 -> some/heavy/request': function(request)
{
//do some heavy stuff here, but go through the cache route so it's not called on each request
}
})
````
Middleware is especially useful for a REST-ful interface:
````javascript
router.on({
/**
* returns the given user
* @example /users/665468459
*/
'pull users/:userId': function(request)
{
getUser(request.data.postId, funciton(user)
{
request.user = user;
//route being used as middleware?
if(request.hasNext()) return request.next();
//return the user
request.end(user);
})
},
/**
* Returns a post made by a particular user
* @example /users/665468459/posts/54353499534
*/
'pull users/:userId -> users/:userId/posts/:postId': function(request)
{
getPosts(request.user, request.data.postId, function(posts)
{
request.end(posts);
})
}
});
````
Middleware can also be specified without using the token: `->`.An example:
````javascript
router.on({
/**
*/
'pull my/*': function()
{
//authorize user
},
/**
*/
'pull my/profile': function()
{
//goes through authorization first
}
});
````
Providing a wildcard `*` tells the router that **anything** after the route must go through it.
### Methods
#### router.on(type[,listener])
Listens to the given routes
- `type` - string or object. String would contain the route. Object would contain multiple routes / listeners
- `listener` - function listening to the route given.
#### router.push(route[, data][, options])
- `type` - the channel broadcast a message to.
- `data` - the data to push to the given route
- `options` - options for the given route
- `meta` - tags to use to filter out listeners
#### router.pull(route[, data][, options][, callback])
same as push, but expects a response
#### router.channels()
returns all registered channels
#### router.getRoute(route)
returns route expression
#### request.write(chunk)
Initializes a streamed response. Great for sending files
#### request.end([chunk])
Ends a response
#### request.hasNext()
Returns TRUE if there's a listener after the current one.
#### request.next()
Moves onto the next route.
#### request.forward(channel, callback)
Forwards the current request to the given channel
#### request.thru(channel[ ,options])
Treats the given channel as middleware
#### request.data
Data is added here
### One last goodie
Beanpole works well with coffeescript:
````coffeescript
router.on
#
'pull -method=GET say/hello': ->
"hello world!"
````
-185
View File
@@ -1,185 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>currentDocument</key>
<string>lib/core/middleware/route/pushPull/pull/request.js</string>
<key>documents</key>
<array>
<dict>
<key>expanded</key>
<true/>
<key>name</key>
<string>beanpole</string>
<key>regexFolderFilter</key>
<string>!.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$</string>
<key>sourceDirectory</key>
<string></string>
</dict>
</array>
<key>fileHierarchyDrawerWidth</key>
<integer>200</integer>
<key>metaData</key>
<dict>
<key>examples/bean.hello/beans/hello.core/index.js</key>
<dict>
<key>caret</key>
<dict>
<key>column</key>
<integer>14</integer>
<key>line</key>
<integer>9</integer>
</dict>
<key>columnSelection</key>
<false/>
<key>firstVisibleColumn</key>
<integer>0</integer>
<key>firstVisibleLine</key>
<integer>0</integer>
<key>selectFrom</key>
<dict>
<key>column</key>
<integer>3</integer>
<key>line</key>
<integer>9</integer>
</dict>
<key>selectTo</key>
<dict>
<key>column</key>
<integer>14</integer>
<key>line</key>
<integer>9</integer>
</dict>
</dict>
<key>examples/bean.hook.hello/beans/test/index.js</key>
<dict>
<key>caret</key>
<dict>
<key>column</key>
<integer>0</integer>
<key>line</key>
<integer>0</integer>
</dict>
<key>firstVisibleColumn</key>
<integer>0</integer>
<key>firstVisibleLine</key>
<integer>0</integer>
</dict>
<key>examples/ws/server.js</key>
<dict>
<key>caret</key>
<dict>
<key>column</key>
<integer>14</integer>
<key>line</key>
<integer>34</integer>
</dict>
<key>columnSelection</key>
<false/>
<key>firstVisibleColumn</key>
<integer>0</integer>
<key>firstVisibleLine</key>
<integer>0</integer>
<key>selectFrom</key>
<dict>
<key>column</key>
<integer>3</integer>
<key>line</key>
<integer>34</integer>
</dict>
<key>selectTo</key>
<dict>
<key>column</key>
<integer>14</integer>
<key>line</key>
<integer>34</integer>
</dict>
</dict>
<key>lib/core/concrete/collection.js</key>
<dict>
<key>caret</key>
<dict>
<key>column</key>
<integer>0</integer>
<key>line</key>
<integer>13</integer>
</dict>
<key>firstVisibleColumn</key>
<integer>0</integer>
<key>firstVisibleLine</key>
<integer>0</integer>
</dict>
<key>lib/core/concrete/router.js</key>
<dict>
<key>caret</key>
<dict>
<key>column</key>
<integer>37</integer>
<key>line</key>
<integer>162</integer>
</dict>
<key>firstVisibleColumn</key>
<integer>0</integer>
<key>firstVisibleLine</key>
<integer>142</integer>
</dict>
<key>lib/core/controller.js</key>
<dict>
<key>caret</key>
<dict>
<key>column</key>
<integer>1</integer>
<key>line</key>
<integer>8</integer>
</dict>
<key>firstVisibleColumn</key>
<integer>0</integer>
<key>firstVisibleLine</key>
<integer>0</integer>
</dict>
<key>lib/core/middleware/meta/rotate.js</key>
<dict>
<key>caret</key>
<dict>
<key>column</key>
<integer>60</integer>
<key>line</key>
<integer>22</integer>
</dict>
<key>firstVisibleColumn</key>
<integer>0</integer>
<key>firstVisibleLine</key>
<integer>0</integer>
</dict>
<key>lib/core/middleware/route/pushPull/pull/request.js</key>
<dict>
<key>caret</key>
<dict>
<key>column</key>
<integer>0</integer>
<key>line</key>
<integer>14</integer>
</dict>
<key>firstVisibleColumn</key>
<integer>0</integer>
<key>firstVisibleLine</key>
<integer>0</integer>
</dict>
</dict>
<key>openDocuments</key>
<array>
<string>lib/core/controller.js</string>
<string>examples/bean.hello/beans/hello.core/index.js</string>
<string>examples/bean.hook.hello/beans/test/index.js</string>
<string>lib/core/concrete/collection.js</string>
<string>lib/core/concrete/router.js</string>
<string>lib/core/middleware/meta/rotate.js</string>
<string>lib/core/middleware/route/pushPull/pull/request.js</string>
<string>examples/ws/server.js</string>
</array>
<key>showFileHierarchyDrawer</key>
<true/>
<key>windowFrame</key>
<string>{{0, 4}, {1470, 1024}}</string>
</dict>
</plist>
-76
View File
@@ -1,76 +0,0 @@
### Beans
Beans are a fancy name for **plugin**.
### Organization
This is still very much a work in progress, but personally, I try to stick to a few conventions which help organize my "beans". Here are some general rules I follow:
- All beans are placed in a single directory. The directory name depends on what the application does.
- If the app serves one platform, then place the beans in `app/beans`;
- If the app serves multiple platforms, then place the beans in:
- `app/node/beans` for node.js specific beans.
- `app/web/beans` for web-specific beans.
- `app/shared/beans` for beans usable across all platforms.
- Bean names should reflect any RESTful API used in the bean.
- Beans in NPM have `bean` prepended to the name e.g: `bean.database.mongo`.
### Naming Conventions
Start off with the category of the bean first, and then the subject. A few examples:
- database.mongo
- database.redis
- database.mysql
Using `database` as the category tells that all beans share the same API. I can easily add / remove any `database` bean I want without breaking the application.
If you have a plugin that uses many plugins, then try this naming convention:
- `category`.core
And for beans that make up `category.core`:
- `category`.part.`subject`
For example:
- stream.core
- stream.part.facebook
- stream.part.twitter
- stream.part.google
Where all the **parts** make up `stream.core`. Remember that parts shouldn't do **anything**. They make-up core plugins. If you have a plugin that serves several plugins, split it up like so:
- posting.part.facebook
- friends.part.facebook
You could also do something like:
- stream.core
- group.core
- group.part.stream.core `part of stream.core`
Try and follow a RESTful naming convention. For example:
- stream.core
- stream.part.subscription.core `listening for streamed content, and sending off to registered subscribers`
- stream.part.subscription.email `subscription listening to stream, and sending a newsletter`
- stream.part.subscription.facebook `subscription listening to a stream, and posting out to facebook`
Note that `part` was dropped after `subscription`. I find it reduntant to use it after the first instance. We already know that `stream.part.subscription.core` is nothing without `stream.core`, so anything *after* that is also useless without the root plugin.
@@ -1 +0,0 @@
These notes are just so I can straighten out ideas about the architecture.
@@ -1 +0,0 @@
- modularized skyscraper
-155
View File
@@ -1,155 +0,0 @@
---------------------------------
Abstracting beanpole:
---------------------------------
Plugins *MUST NOT* know if whether a computer is networked. That job is delegated for *one* particular module: glue.core. If a client connects to a server, and contains a hook which acts like a plugin, the backend should treat it exactly as it would with a another networked server, or even internally. Glue.core would handle the handshake / authentication.
Likewise, connected servers using brazen must not handle communication with other servers differently than internally.
For security sake, each channel must provide metadata identifying whether it's private, protected, or public. Channels default to private if it's absent.
- Public channels are available to call without authentication
- Protected channels are available to call only by other crusted servers / clients
- Private channels can only be handled within the application.
All metadata is handled by the parser / router.
---------------------------------
Routing:
---------------------------------
The routing mechanism should parse syntactic sugar to identify how a particular channel is handled. Each channel must be separated by backslashes to allow for parameters. This is primarily for future implementation where beanpole might support HTTP requests.
'[type] [meta]* [channel_name]* [channel/:param] [additional]*'
- type: the type of channel - push, pull, ???
- meta: information attached to channel help glue.core, and other handlers.
- channel_name: the name of the channel. Private use from channel. Useful if channel needs to change, like a variable name.
- channel: the physical channel separated/by/backslashes
- additional: additional parameters specific to the channel type.
on push application/ready: function(data){}
'push private -pull application/ready'
on private pull of application ready passing through application exists:
'pull private application/exists -> application/ready': function(pull){ }
Calling back:
There needs to be a way for modules handling a particular request to identify where it's coming from.
---------------------------------
Types of channels:
---------------------------------
- pull - (getting) channels which respond to a particular request which maybe pushed out at a later time. This is from the source that makes the "push" with the same name.
- push - (setting) channels which are pushed out are used after a particular change has occurred. This is one to many.
Why I chose for push/pull to be different handlers with the same name:
Inspiration was actually taken back in the day when I was developing in Actionscript (I know, stfu). Bindable metadata was a slick way of getting a particular property from an object, and then sticking to it for any changes. For beanpole, "pull" would be the action of getting the current value of a particular "pod" (object), and "push" is the method of sticking to it.
Problems:
How do we distinguish single pulls from multiple pulls? For instance, for multiple pulls, I may want to "pull" stats from all the servers, but for a single pull, I may want to register a particular queue, and *only* send it to one instance.
Possible solutions:
on('pull multi…')
proxy.pull('multi…')
is a different channel handing than
on('pull…')
proxy.pull('…')
So technically, if I register
proxy.on('pull get.name', …')
proxy.on('pull multi get.name')
there wouldn't be any complaining, since I can call only one, or the other. Where the first one can be the *only* one, and the second one can have multiple. SO
proxy.pull('get.name')
would be the first one, and
proxy.pull('multi get.name')
would be the second one.
Multiple pulls also need to return to the requestor how many items are handling the request. This probably needs to take on a response, ondata, and end approach similar to node.js's streaming api
Streaming:
Without adding too much shit to the architecture, there may come a time where content is streamed to the particular requestor. This could be highly beneficial for programs which send files back and forth. Something which beanpole isn't necessarily equipped for at the moment. So, without changing the architecture, perhaps providing a method for supporting such a feature. For example:
This could be used:
proxy.pull('get.file','text.txt', {
data: function(buffer)
{
},
end: function()
{
}
});
or this could be used:
proxy.pull('get.file','text.txt',function(body)
{
});
But what about pulling from multiple sources?
proxy.pull('multi get.file', 'text.txt', function(source) //callback multiple times
{
source.on('data…
});
Hmmmm…
--------------------------------
Network Topology
--------------------------------
The architecture, mainly glue.core must have the ability to manage connected servers. Many of which should only communicate to particular applications. For instance: Currently there are two applications on spice.io which register queue's to the queue app . Each application has a slave which handles the cue, but the queue must know which slaves to send to. So:
- Each app must have an identifier shared amongst other apps it needs to communicate with (_appId)
- Since queues are protected, the handshake to between the queue app and the requester must be authenticated.
- The queue must receive the app id, and know what apps to send to via registered channels.
What if there are dozens of slaves running across a cluster of servers?
there *must* be a way for glue.core to use a load-balancing mechanism to send individual pulls to servers: round robin, least connected, etc. Stats from servers to see which is less busy?
What if there are multiple singleton pulls registered to glue.core?
Duh, fucking round-robin that shit when a pull-request is made.
-5
View File
@@ -1,5 +0,0 @@
#!/usr/bin/env node
var ebnf = require('ebnf-diagram');
ebnf.fromFile(__dirname + '/syntax.ebnf', __dirname + '/diagram.png',3000, 1654);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

-5
View File
@@ -1,5 +0,0 @@
"" {
syntax = ('push' | 'pull') { tag } channel { '->' channel }.
tag = '-' name [ '=' value ].
channel = '/' { [':'] name '/' } ['*'].
}
@@ -1,18 +0,0 @@
exports.plugin = function(mediator)
{
function init()
{
console.log('calling plugins to say hello...');
mediator.pull(' -multi say/hello', function(response)
{
console.log(response);
});
}
mediator.on({
'push init': init
});
}
@@ -1,13 +0,0 @@
exports.plugin = function(mediator)
{
function sayHello(pull)
{
pull.end('Hello Friend!');
}
mediator.on({
'pull -multi -public say/hello': sayHello
});
}
@@ -1,13 +0,0 @@
exports.plugin = function(mediator)
{
function sayHello(pull)
{
pull.end('Hello Neighbor!');
}
mediator.on({
'pull -multi -public say/hello': sayHello
})
}
@@ -1,3 +0,0 @@
var beanpole = require('../../lib/node');
beanpole.require(__dirname + '/beans').push('init');
@@ -1,72 +0,0 @@
var lazy = require('sk/core/lazy').callback;
exports.plugin = function(mediator)
{
var ops, sent = 0, received = 0, pulls = 0, capp = 0, interval;
function testSend(pull)
{
// console.log('received: %d', ++received);
/*if(received != sent)
{
console.log(recieved-sent)
}*/
pull.end(++received);
}
function pushHook(data, err, request)
{
console.success('starting test.');
timeout(data, ++capp, request);
}
function timeout(data, index, request)
{
var margin = 0;
setTimeout(function()
{
for(var i = ops.concurrent; i--;)
{
// console.log('sent %d', ++sent);
margin++;
request.from.pull('test/send', function(rcv)
{
// console.log('received: %d', rcv);
//wait till everything's done. Stop if anything's missed.
if(!(--margin))
{
console.log('#%d: done pulling %d requests from app #%d', ++pulls, ops.concurrent, index);
timeout(request, index);
}
});
}
}, ops.speed);
}
function init(op)
{
ops = op;
console.success('ready: speed=%d, concurrent=%d', ops.speed, ops.concurrent);
console.success('Now open another terminal window with the same script'.underline);
}
mediator.on({
'push init': init,
'pull -public test/send': testSend,
'push hook/connection': pushHook
});
}
@@ -1,28 +0,0 @@
var beanpole = require('../../lib/node'),
cli = require('sk/node/cli');
var ops = {};
process.stdout.write('num concurrent calls: ');
cli.next(function(arg)
{
ops.concurrent = Number(arg) || 10;
process.stdout.write('speed (ms): ');
cli.next(function(arg)
{
ops.speed = Number(arg) || 200;
beanpole.require(['hook.core','hook.http.mesh']).
require(__dirname + '/beans').push('init', ops);
})
})
//
@@ -1,41 +0,0 @@
var lazy = require('sk/core/lazy').callback;
exports.plugin = function(mediator)
{
var myName;
function pushSayHello(guestName)
{
console.log('hello %s!', guestName);
if(this.from)
{
this.from.push('say/hello/back', myName);
}
}
function pushSayHelloBack(guestName, err, push)
{
console.log('%s said hello back!', guestName);
}
function pushHook(data, err, push)
{
console.success('A person decided to join the partayyy.');
push.from.push('say/hello', myName)
}
function init(n)
{
myName = n;
pushSayHello(n);
}
mediator.on({
'push init': init,
'push hook/connection': pushHook,
'push -public say/hello': pushSayHello,
'push -public say/hello/back': pushSayHelloBack,
})
}
@@ -1,26 +0,0 @@
var beanpole = require('../../lib/node'),
argv = process.argv.concat();
argv.splice(0,2);
var name = argv[0];
function onName(name)
{
beanpole.require(['hook.core','hook.http.mesh']).
require(__dirname + '/beans').push('init', name.toString().replace('\n',''));
process.stdin.removeListener('data', onName);
};
if(name)
{
onName(name)
}
else
{
process.stdout.write('Name: ');
process.stdin.on('data', onName);
process.openStdin();
}
@@ -1,41 +0,0 @@
var lazy = require('sk/core/lazy').callback;
exports.plugin = function(mediator)
{
var myName;
function pushSayHello(guestName)
{
console.log('hello %s!', guestName);
if(this.from)
{
this.from.push('say/hello/back', myName);
}
}
function pushSayHelloBack(guestName, push)
{
console.log('%s said hello back!', guestName);
}
function pushHook(data, push)
{
console.success('A person decided to join the partayyy.');
push.from.push('say/hello', myName)
}
function init(n)
{
myName = n;
pushSayHello(n);
}
mediator.on({
'push init': init,
'push hook/connection': pushHook,
'push -public say/hello': pushSayHello,
'push -public say/hello/back': pushSayHelloBack,
})
}
@@ -1,32 +0,0 @@
var beanpole = require('../../lib/node'),
argv = process.argv.concat();
beanpole.require(['hook.http.mesh','hook.core']);
beanpole.on({
'pull -public hook2': function()
{
this.next();
},
'pull -public hook2 -> thru/hook2 -> test/hook': function()
{
console.log("fetching account...");
return "Successfuly passed through remote middleware";
},
'push -public hook2/ready': function()
{
console.log("Connected to hook 2");
beanpole.pull('test/hook', function(message)
{
console.success(message);
})
}
});
beanpole.push('init');
beanpole.push('ready','hook1');
console.log('hook1 is ready');
@@ -1,32 +0,0 @@
var beanpole = require('../../lib/node'),
argv = process.argv.concat();
beanpole.require(['hook.http.mesh','hook.core']);
beanpole.on({
'pull -public thru/hook2': function()
{
console.log("Through hook 2");
setTimeout(function(self)
{
console.success("done!")
self.next();
}, 500, this)
},
'push -public hook1/ready': function()
{
console.log("Connected to hook 1");
}
});
beanpole.push('init');
beanpole.push('ready','hook2');
console.log('hook2 is ready');
@@ -1,26 +0,0 @@
var beanpole = require('../../lib/node'),
argv = process.argv.concat();
beanpole.require(['hook.http.mesh','hook.core']);
beanpole.on({
'pull -public thru/hook3': function()
{
console.log('thru hook 3');
if(!this.next()) return "authenticated"
},
'push -public hook1/ready': function()
{
console.log("connected to hook 1");
}
});
beanpole.push('init');
beanpole.push('ready','hook2');
console.log('hook2 is ready');
@@ -1,101 +0,0 @@
var beanpole = require('../../lib/node').router(),
express = require('express'),
Url = require('url');
beanpole.require('middleware.core');
beanpole.on({
/**
*/
'pull -public basic/auth/root/pass -> basic/auth': function()
{
if(!this.next()) return 'authorized!';
},
/**
*/
'pull -public basic/auth -> auth/text': function()
{
return "authorized: " + this.user.name;
},
/**
*/
'pull -public session -> test/session': function()
{
var sess = this.session.data;
if(!sess.numVisits) sess.numVisits = 0;
sess.numVisits++;
return 'Num Visits: '+sess.numVisits;
},
/**
*/
'push init': function(init)
{
var srv = express.createServer(),
channels = beanpole.channels();
function initPath(path, expr)
{
srv.get('/' + path, function(req, res)
{
beanpole.pull(Url.parse(req.url).pathname, null, { meta: { stream: 1 }, req: req }, function(writer)
{
writer.on({
response: function(headers)
{
if(headers.session)
{
res.setHeader('Set-Cookie', headers.session.http)
}
if(headers.authorization)
{
res.statusCode = 401;
res.setHeader('WWW-Authenticate', headers.authorization.http);
}
},
write: function(data)
{
res.write(typeof data == 'object' ? JSON.stringify(data) : data);
},
end: function()
{
res.end();
}
});
})
});
}
for(var channel in channels)
{
var expr = channels[channel];
if(expr.type == 'pull' && expr.meta.public)
{
initPath(channel, expr);
}
}
srv.listen(8032);
}
});
beanpole.push('init');
@@ -1,34 +0,0 @@
exports.plugin = function(mediator)
{
function meta1(pull)
{
pull.end('metadata 1');
}
function meta2(pull)
{
pull.end('metadata 2');
}
function meta3(pull)
{
pull.end('metadata 3');
}
function filterMeta(pull)
{
mediator.pull('meta', null, { meta: { group: pull.data.group } }, function(data)
{
pull.end(data || '');
});
}
mediator.on({
'pull -public -rotate -group=1 meta': meta1,
'pull -public -rotate -group=2 meta': meta2,
'pull -public -rotate -group=3 meta': meta3,
'pull -public meta/:group': filterMeta
});
}
@@ -1,41 +0,0 @@
var express = require('express');
exports.plugin = function(mediator)
{
function init(pull)
{
var srv = express.createServer();
mediator.pull('hook', function(data)
{
data.channels.forEach(function(channel)
{
srv.get('/'+channel.name, function(req, res)
{
var name = channel.name;
for(var param in req.params)
{
name = name.replace(':'+param,req.params[param])
}
mediator.pull('-stream ' + name, function(writer)
{
writer.pipe(res);
})
});
});
srv.listen(8032);
});
}
console.log('Server running on port ' + 8032+'. try http://localhost:8032/meta/[1,2,3], or omit the number and watch them rotate.');
mediator.on({
'push init': init
});
}
@@ -1,3 +0,0 @@
var beanpole = require('../../lib/node');
beanpole.require(['hook.core','hook.http.mesh']).require(__dirname + '/beans').push('init');
@@ -1,46 +0,0 @@
exports.plugin = function(mediator)
{
function init()
{
var i = 0;
//default
mediator.pull('load/site', { site: 'http://www.google.com/' }, function(chunk)
{
console.log('http://www.google.com chunk #%d', i++);
});
//letting brazilnut handle the combining of buffers
mediator.pull('-batch load/site', { site: 'http://www.engadget.com/' }, function(buffer)
{
console.log('done reading http://engadget.com with %d chunks.', buffer.length);
});
//streaming data
mediator.pull('-stream load/site', { site: 'http://www.google.com/' }, function(reader)
{
var buffer = [];
reader.on({
write: function(chunk)
{
buffer.push(chunk)
},
end: function()
{
console.log('done reading http://google.com with %d chunks.', buffer.length);
}
})
});
}
mediator.on({
'push init': init
});
}
@@ -1,3 +0,0 @@
var brazilnut = require('../../lib/node');
brazilnut.require(__dirname + '/beans').push('init');
@@ -1,32 +0,0 @@
var beanpole = require('../../lib/node').router(),
Structr = require('structr');
function hello()
{
return "hello!.";
}
beanpole.on({
'pull hello/silly/world': hello
})
var start = new Date();
for(var i = 10000; i--;)
{
beanpole.pull('hello/silly/world', function (res)
{
// console.log(res)
});
}
console.log(new Date().getTime() - start.getTime());
-49
View File
@@ -1,49 +0,0 @@
var beanpole = require('../../lib/node').router();
function all()
{
console.log('all');
if(!this.next())
{
console.log("DONE!")
}
}
function all2()
{
console.log("ROTATE")
}
function thru()
{
console.log('thru');
if(!this.next())
{
console.log("CANNOT CONTINUE!")
}
}
function thru2()
{
console.log("THRU 2");
this.next();
}
function sayHi(message)
{
console.log(message);
}
beanpole.on({
'dispatch /*': all,
// 'dispatch -d /*': all2,
'dispatch thru2': thru2,
'dispatch thru2 -> thru': thru,
'dispatch thru2 -> thru -> thru -> hello': sayHi
});
// beanpole.dispatch('hello','world!');
// beanpole.dispatch('hello','world!');
beanpole.dispatch('hello','world!');
@@ -1,6 +0,0 @@
exports.plugin = function(mediator)
{
}
@@ -1,75 +0,0 @@
var beanpole = require('../../lib/node').router();
function hello1(pull)
{
return "hello 1!";
}
function hello2(pull)
{
return "hello 2!";
}
function hello3(pull)
{
return "hello 3!";
}
function init()
{
//hello 3!
beanpole.pull('say/hello', function(msg)
{
console.log(msg)
});
//hello 1!
beanpole.pull('say/hello', function(msg)
{
console.log(msg)
});
//hello 2!
beanpole.pull('say/hello', function(msg)
{
console.log(msg)
});
//hello 2!
beanpole.pull('-name=group1 say/hello', function(msg)
{
console.log(msg)
});
//hello 3!
beanpole.pull('-name=group1 say/hello', function(msg)
{
console.log(msg)
});
//hello 1!
beanpole.pull('-name=group2 say/hello', function(msg)
{
console.log(msg)
});
//hello 1!
beanpole.pull('-name=group2 say/hello', function(msg)
{
console.log(msg)
});
}
beanpole.on({
'push init': init,
//NOTE: rotate=N is provided just so the properties aren't overridden
'pull -name=group2 -rotate=3 say/hello': hello1,
'pull -name=group1 -rotate=2 say/hello': hello2,
'pull -name=group1 -rotate=1 say/hello': hello3
});
beanpole.push('init');
@@ -1,53 +0,0 @@
var beanpole = require('../../lib/node').router();
function groupHello1(pull)
{
return "hello!";
}
function groupHello2(pull)
{
return "hello again!";
}
function groupHello3(pull)
{
return "hello for a third time!";
}
function init()
{
//hello!
//hello again!
//hello for a third time!
beanpole.pull('-multi say/hello', function(msg)
{
console.log(msg)
});
//hello!
//hello again!
beanpole.pull('-multi -name=group1 say/hello', function(msg)
{
console.log(msg)
});
//hello for a third time!
beanpole.pull('-multi -name=group2 say/hello', function(msg)
{
console.log(msg)
});
}
beanpole.on({
'push init': init,
//-multi=N is set so props aren't overridden
'pull -name=group2 -multi=3 say/hello': groupHello3,
'pull -name=group1 -multi=2 say/hello': groupHello2,
'pull -name=group1 -multi=1 say/hello': groupHello1
});
beanpole.push('init')
@@ -1,33 +0,0 @@
var beanpole = require('../../lib/node');
var router = beanpole.router();
router.params({
'http.gateway': {
http:{
port: 8080
}
}
});
router.on({
'push -public spice.io/theurld/ready': function()
{
console.log("READY");
}
});
router.require(['http.server', 'http.gateway','hook.http','hook.core']);
router.push('init');
router.push('hook/add','http://theurld.spice.io');
@@ -1,43 +0,0 @@
var beanpole = require('../../lib/node');
var router = beanpole.router();
router.params({
'http.gateway': {
http:{
port: 8081
}
}
});
router.on({
'pull -http hello': function()
{
return "Hello world!";
},
'push -public head/ready': function()
{
console.log("READ READY");
},
'push -public hook/ready': function()
{
console.log("LOL HOOKKS");
}
});
router.require(['http.server', 'http.gateway','hook.http.mesh2','hook.core']);
router.push('init');
router.push('ready','hook');
router.pull('hooks/add', 'http://localhost:8080', function(){});
@@ -1,38 +0,0 @@
var beanpole = require('../../lib/node');
var router = beanpole.router();
router.params({
'http.gateway': {
http:{
port: 8082
}
}
});
router.on({
'pull -http hello': function()
{
return "Hello world!";
},
'push -public head/ready': function()
{
console.log("READ READY");
}
});
router.require(['http.server', 'http.gateway','hook.http.mesh2','hook.core']);
router.push('init');
router.push('ready','hook');
router.pull('hooks/add', 'http://localhost:8080', function(){});
@@ -1,43 +0,0 @@
var beanpole = require('../../lib/node');
var router = beanpole.router();
router.params({
'http.gateway': {
http:{
port: 8083
}
}
});
router.on({
'pull -http hello': function()
{
return "Hello world!";
},
'push -public head/ready': function()
{
console.log("READ READY");
},
'push -public hook/ready': function()
{
console.log("LOL HOOKKS");
}
});
router.require(['hook.http.mesh','hook.core']);
router.push('init');
router.push('ready','hook');
router.pull('hooks/add', 'http://localhost:8080', function(){});
@@ -1,48 +0,0 @@
var beanpole = require('../../lib/node');
var router = beanpole.router();
router.params({
'http.gateway': {
http:{
port: 8083
}
}
});
var id = Date.now();
console.log(id);
router.on({
'pull -http hello': function()
{
return "Hello world!";
},
'push -public sibling/ready': function(id)
{
console.log("SIBLING READY: " + id);
},
'push -public hook/ready': function()
{
if(this.from == router) return;
this.from.push('sibling/ready', id);
}
});
router.require(['hook.http.mesh','hook.core']);
router.push('init');
router.push('ready','hook');
@@ -1,41 +0,0 @@
var beanpole = require('../../lib/node');
var router = beanpole.router();
router.params({
'http.gateway': {
http:{
port: 8080
}
}
});
router.on({
'pull -http hello': function()
{
return "Hello world!";
},
'push -public hook/ready': function()
{
console.log("READY");
},
'push -public duck': function()
{
console.log("DUCK!");
}
});
router.require(['http.server', 'http.gateway','hook.http.mesh2','hook.core']);
router.push('init');
router.push('ready','head');
@@ -1,31 +0,0 @@
var beanpole = require('../../lib/node').router();
function intercept(pull)
{
//change the data
pull.data.sport = 'soccer';
pull.next();
}
function test(pull)
{
pull.end(pull.data.sport)
}
function init()
{
beanpole.pull('test', { sport: 'football' }, function(result)
{
console.log(result)
});
}
beanpole.on({
'push init': init,
'pull -intercept=sport te`st/interception': intercept,
'pull test': test
})
beanpole.push('init');
@@ -1,20 +0,0 @@
var beanpole = require('../../lib/node'),
argv = process.argv.concat();
var router = beanpole.router(),
router2 = beanpole.router();
var listen = {
'push hello': function()
{
console.log('hello!')
}
}
router.on(listen).dispose();
router2.on(listen);
router.push('hello')
-32
View File
@@ -1,32 +0,0 @@
var beanpole = require('../../lib/node').router();
function delay()
{
setTimeout(function (self){ self.next(); }, this.data.seconds * 1000, this);
}
beanpole.on({
'push -filter=BOMB (say/hello or say/hello/world) or blah': function()
{
console.log("hello world!")
},
'push -filter=GOLD blah': function()
{
console.log("GO")
},
'pull /*': function()
{
console.log("G")
}
})
beanpole.push('say/hello');
beanpole.push('say/hello/world');
beanpole.push('-filter=GOLD blah');
beanpole.push('-filter=BOMB blah');
beanpole.push('/');
@@ -1,21 +0,0 @@
var beanpole = require('../../lib/node').router();
beanpole.on({
/**
*/
'pull -overridable say/hello': function()
{
return "hello";
},
'pull say/hello': function()
{
return "hello world!"
}
});
-61
View File
@@ -1,61 +0,0 @@
var beanpole = require('../../lib/node').router();
function sayHi()
{
console.log(this.data)
console.log('hi')
// this.next();
if(!this.next()) return "don't pass " + this.data.name+ " "+this.data.last;
}
function sayHi2()
{
console.log('pass!');
if(!this.next())
{
this.end("GO")
}
}
function sayHiCraig()
{
if(!this.next()) return "GO"
}
function sayHi3()
{
if(!this.next()) return "hello";
}
function init()
{
beanpole.pull('hello2/craig', function(result)
{
console.log(result)
});
}
function test2()
{
}
beanpole.on({
'push init': init,
'pull /*': function()
{
console.log("PSSS") ;
this.next();
},
'pull hellotest': sayHi2,
'pull hellotest -> hellotest -> hello2/:name': sayHi3
});
// console.log(beanpole.routeMiddleware._routers.pull._collection._routes)
beanpole.push('init');
-46
View File
@@ -1,46 +0,0 @@
var beanpole = require('../../lib/node').router();
beanpole.on({
'pull name': function()
{
setTimeout(function()
{
beanpole.push('name', function()
{
return "SLAYERS!";
});
beanpole.push('name', 'PUSH');
// beanpole.push('name', 'PUSH');
// beanpole.push('name', 'PUSH');
// beanpole.push('name', 'PUSH');
// beanpole.push('name', 'PUSH');
}, 100);
return 'PULL';
},
'push /*': function()
{
console.log("PUSH INITIALIZED!");
this.next();
},
'push through': function(data)
{
console.log("delay!")
setTimeout(function(self){ self.next(); }, 500, this)
},
'push -pull name': function(name)
{
console.log(name)
},
'push -pull name': function(name)
{
console.log(name)
},
/*'push name': function(name)
{
console.log(name +' PUSH')
}*/
});
-35
View File
@@ -1,35 +0,0 @@
var beanpole = require('../../lib/node').router();
function sayHi2()
{
console.log('pass!');
// return 'ga'
if(!this.next())
{
console.log("FAIL")
}
}
function sayHi3()
{
console.log(this.data)
}
function init()
{
beanpole.push('hello3', 'craig!');
}
beanpole.on({
'push init': init,
'push hello2 -> hello2 -> hello2 -> hello2 -> hello2 -> hello2 -> hello': sayHi2,
'push hello2': sayHi2,
'push hello -> hello2 -> hello3': sayHi3
})
beanpole.push('init');
@@ -1,43 +0,0 @@
var beanpole = require('../../lib/node').router();
function delay()
{
setTimeout(function (self){ self.next(); }, this.data.seconds * 1000, this);
}
function sayHi()
{
return "I.";
}
function sayHi2()
{
return "Love.";
}
function sayHi3()
{
return "Coffee.";
}
function init()
{
for(var i = 3; i--;)
{
beanpole.pull('say/hi', function (res)
{
console.log(res)
});
}
}
beanpole.on({
'push init': init,
'pull delay/:seconds': delay,
'pull -rotate delay/1 -> say/hi': sayHi,
'pull -rotate delay/2 -> say/hi': sayHi2,
'pull -rotate delay/3 -> say/hi': sayHi3
})
beanpole.push('init');
-87
View File
@@ -1,87 +0,0 @@
var beanpole = require('../../lib/node');
var router = beanpole.router();
router.on({
/**
*/
'pull /*': function()
{
console.log("THRU");
this.next();
},
/**
*/
'pull -two /*': function()
{
console.log("THRU MOO");
this.next();
},
/**
*/
'pull -two /hello/*': function()
{
console.log("THRU hoo");
this.next();
},
/**
*/
'pull /hello/param/*': function()
{
console.log("THRU AGAIN");
this.next();
},
/**
*/
'pull -t /hello/param/*': function()
{
console.log("THRU AGAIN");
this.next();
},
/**
*/
'pull -t2 /hello/param/*': function()
{
console.log("THRU AGAIN");
this.next();
},
/**
*/
'pull -t3 /hello/param/*': function()
{
console.log("THRU AGAIN");
this.next();
},
/**
*/
'pull hello/param': function()
{
console.log("HELLO!");
}
});
router.pull('hello/param', function()
{
});
@@ -1,9 +0,0 @@
<html>
<head>
<title> Socket Test </title>
<script src="http://localhost:6032/socket.io/socket.io.js" type="text/javascript"></script>
<script src="index.js" type="text/javascript"> </script>
</head>
<body>
</body>
</html>
File diff suppressed because it is too large Load Diff
-37
View File
@@ -1,37 +0,0 @@
var beanpole = require('../../lib/node').router();
beanpole.require(['hook.core','hook.http.mesh']);
beanpole.on({
/**
*/
'push init': function()
{
beanpole.push('ready', 'spice.io');
},
/**
*/
'push -public call/later': function(data)
{
console.log('calling later');
var self = this;
setTimeout(function()
{
self.from.pull(data.channel, data.data, { inner: self.inner }, function(result)
{
console.log(result)
})
},Math.max(0, data.sendAt - new Date().getTime()));
// setTimeout()
}
});
beanpole.push('init');
-40
View File
@@ -1,40 +0,0 @@
var beanpole = require('../../lib/node').router();
beanpole.require(['hook.core','hook.socket.io.server','hook.http.mesh']);
beanpole.on({
/**
*/
'push init': function()
{
beanpole.push('ready', 'spice.io');
},
/**
*/
'pull -public say/hello': function()
{
return "hello MADRE!";
},
/**
*/
'push -public client/ready': function()
{
console.log("CLIENT READY");
this.from.pull('get/name', function(name)
{
console.log(name)
})
}
});
beanpole.push('init');
@@ -1,338 +0,0 @@
var Structr = require('structr'),
Parser = require('./parser'),
utils = require('./utils'),
Request = require('./request'),
Janitor = require('sk/core/garbage').Janitor;
/**
* collection for routes
*/
/**
* IMPORTANT notes regarding this class
* 1. you can have multiple explicit middleware (/path/*)
*/
var Collection = Structr({
/**
* Constructor. What else do you think it is?
*/
'__construct': function(ops)
{
//the options for the router
this._ops = ops || {};
//these are the channels parsed into a traversable route
this._routes = this._newRoute();
//these get executed whenever there's a new "on"
this._middleware = this._newRoute();
//the current route index. increments on every route!
this._routeIndex = 0;
},
/**
*/
'has': function(expr)
{
var routes = this.routes(expr);
for(var i = routes.length; i--;)
{
if(routes[i].target) return true;
}
return false;
},
/**
*/
'route': function(channel)
{
return this._route(channel.paths);
},
/**
*/
'routes': function(expr)
{
var channels = expr.channels,
routes = [];
for(var i = channels.length; i--;)
{
routes.push(this.route(channels[i]));
}
return routes;
},
/**
* listens to the given expression for any chandage
*/
'add': function(expr, callback)
{
var janitor = new Janitor();
for(var i = expr.channels.length; i--;)
{
janitor.addDisposable(this._add(expr.channels[i], expr.meta, callback));
}
return janitor;
},
/**
*/
'_add': function(channel, meta, callback)
{
var paths = channel.paths,
isMiddleware = channel.isMiddleware,
middleware = channel.thru,
//middleware isn't used explicitly. Rather, it's *injected* into the routes which ARE used. Remember that.
//explicit middleware looks like some/path/*
currentRoute = this._start(paths, isMiddleware ? this._middleware : this._routes, true);
//some explicit middleware might already be defined, so we need to get the *one* to pass through.
var before = this._before(paths, currentRoute);
if(middleware) this._endMiddleware(middleware).thru = before;
//the final callback for the route
var listener = {
callback: callback,
//metadata for the expression
meta: meta,
//keeps tabs for later use (in request)
id: 'r'+(this._routeIndex++),
//this is a queue where the first item is executed first, then on until we reach the last item
thru: middleware || before,
isMiddleware: channel.isMiddleware,
path: paths,
dispose: function()
{
var i = currentRoute.listeners.indexOf(listener);
if(i > -1) currentRoute.listeners.splice(i, 1);
}
};
currentRoute.meta = Structr.copy(meta, currentRoute.meta);
//at this point we can inject the listener into the current route IF it's middleware.
if(isMiddleware) this._injectMiddleware(listener, paths);
//now that we're in the clear, need to add the listener!
if(!currentRoute.listeners) currentRoute.listeners = [];
//now to add it. Please take remember, for MOST CASES, "_listeners" will only have one, especially for http / requests
currentRoute.listeners.push(listener);
//the return statement allows for the item to be disposed of
return listener;
},
/**
*/
'_endMiddleware': function(target)
{
var current = target || {};
while(current.thru)
{
current = current.thru;
}
return current;
},
/**
* injects explicit middleware (/path/*) in all the routes which go through its path
*/
'_injectMiddleware': function(listener, paths)
{
//level is only important for
listener.level = paths.length;
//need to go through *all* routes ~ even middleware, because middleware also have
//routes to pass through ~ Inception.
var afterListeners = this._after(paths, this._routes).concat(this._after(paths, this._middleware));
//go through ALL items to put before this route, but make sure the item we're replacing isn't higher
//in the middleware chain, because higher methods will already *have* reference to this pass-thru
for(var i = afterListeners.length; i--;)
{
var currentListener = afterListeners[i];
var currentMiddleware = currentListener.thru,
previousMiddleware = currentListener;
while(currentMiddleware)
{
if(currentMiddleware.level != undefined)
{
if(currentMiddleware.level < listener.level)
{
previousMiddleware.thru = listener;
}
break;
}
previousMiddleware = currentMiddleware;
currentMiddleware = currentMiddleware.thru;
}
if(!currentMiddleware) previousMiddleware.thru = listener;
}
},
/**
* reveals routes which must come *before* a middleware
* after beats circular references
* TODO: following code is __ugly as fuck__.
*/
'_before': function(paths, after)
{
var current = this._middleware,
listeners = [];
for(var i = 0, n = paths.length; i < n; i++)
{
//this makes sure we don't get to the end for pass thrus
if(current.listeners) listeners = current.listeners;
var path = paths[i],
newCurrent = path.param ? current._route._param : current._route[path.name];
if(current == after || !newCurrent) break;
current = newCurrent;
}
// if(current != after)
//this is a check against pass thrus to beat circular references. It *also* allows this: hello/* -> hello
if(current != after && current.listeners) listeners = current.listeners;
return listeners[0];
},
/**
* reveals everyhing that comes *after* a route (for pass-thru's)
*/
'_after': function(paths, routes)
{
return this._flatten(this._start(paths, routes));
},
/**
* returns the starting point of a route
*/
'_route': function(paths, routes, create, retControl)
{
var control = (routes || this._routes),
current = control._route;
for(var i = 0, n = paths.length; i < n; i++)
{
var path = paths[i],
name = path.param ? '_param' : path.name;
if(!current[name] && create)
{
current[name] = this._newRoute(i);
}
if(current[name])
{
current = current[name];
}
else
{
current = current._param;
}
if(!current) return {};
control = current;
current = current._route;
}
return control;
},
/**
*/
'_start': function(paths, routes)
{
return this._route(paths, routes, true);
},
/**
*/
'_newRoute': function(level)
{
return { _route: { }, _level: level || 0 };
},
/**
* flattens all routes into a single array
*/
'_flatten': function(route)
{
var listeners = route.listeners ? route.listeners.concat() : [];
for(var path in route._route)
{
listeners = listeners.concat(this._flatten(route[path] || {}));
}
return listeners;
}
});
module.exports = Collection;
-595
View File
@@ -1,595 +0,0 @@
var Structr = require('structr');
/**
* parses syntactic sugar.
Why the hell are you using a parser for something so simple? Because I wanted to. Yeah, I could have done it in Regexp, but fuck that >.>... This
Is much more fun.
*/
//follow the pattern below when adding tokens plz.
var Token = {
// A-Z
WORD: 1,
// -metadata
METADATA: 1 << 1,
// =
PATH: 1 << 2,
// :param
PARAM: 1 << 3,
// ->
TO: 1 << 4,
// for routing
BACKSLASH: 1 << 5,
// .
DOT: 1 << 6,
// * - this is an auto-middleware
STAR: 1 << 7,
// "or"
OR: 1 << 8,
// (
LP: 1 << 9,
// )
RP: 1 << 10,
// =
EQ: 1 << 11,
//whitespace
WHITESPACE: 1 << 12
};
//reserved keywords
var Reversed = {
or: Token.OR
}
var Tokenizer = function()
{
//source of the string to tokenize
var source = '',
//the position of the parser
pos = 0,
//the current token
currentToken,
self = this;
/**
* getter / setter for the source
*/
this.source = function(value)
{
if(value)
{
source = value+' '; //padding
pos = 0;
}
return source;
}
/**
* next token
*/
this.next = function(keepWhite)
{
return currentToken = nextToken(keepWhite);
}
/**
*/
this.peekChars = function(n)
{
return source.substr(pos, n);
}
/**
*/
this.current = function(keepWhite)
{
return currentToken || self.next(keepWhite);
}
/**
*/
this.position = function()
{
return pos;
}
/**
*/
var nextToken = function(keepWhite, ignoreError)
{
if(!keepWhite) skipWhite();
if(eof()) return null;
var c = currentChar(), ccode = c.charCodeAt(0);
if(isWhite(ccode))
{
skipWhite();
return token(' ',Token.WHITESPACE);
}
//a-z0-9
if(isAlpha(ccode))
{
var w = nextPath();
return token(w, Reversed[w.toLowerCase()] || Token.WORD);
}
switch(c)
{
//for middleware
case '-':
if(nextChar() == '>') return token('->', Token.TO, true);
if(isAlpha(currentCharCode())) return token(nextPath(), Token.METADATA);
error();
//parameters for routes
case ':':
if(isAlpha(nextCharCode())) return token(nextPath(), Token.PARAM);
error();
case '/': return token('/', Token.BACKSLASH, true);
case '.': return token('.', Token.DOT, true);
case '*': return token('*', Token.STAR, true);
case '(': return token('(', Token.LP, true);
case ')': return token(')', Token.RP, true);
case '=': return token('=', Token.EQ, true);
//path part
default: return token(nextPath(), Token.PATH);
}
//eof
return null;
}
var error = function()
{
throw new Error('Unexpected character "'+currentChar()+'" at position '+pos+' in "'+source+'"');
}
/**
*/
var token = function(value, type, skipOne)
{
if(skipOne) nextChar();
return { value: value, type: type };
}
/**
*/
var nextChar = this.nextChar = function()
{
return source[++pos];
}
/**
*/
var currentChar = this.currentChar = function()
{
return source[pos];
}
/**
*/
var isAlpha = this.isAlpha = function(c)
{
return (c > 96 && c < 123) || (c > 64 && c < 91) || isNumber(c) || c == 95;
}
/**
*/
var isWhite = this.isWhite = function(c)
{
return c == 32 || c == 9 || c == 10;
}
/**
*/
var isNumber = this.isNumber = function(c)
{
return c > 47 && c < 58;
}
/**
*/
var nextCharCode = function()
{
return nextChar().charCodeAt(0);
}
/**
*/
var currentCharCode = function()
{
return currentChar().charCodeAt(0);
}
/**
*/
var rewind = function(steps)
{
pos -= (steps || 1);
}
/**
*/
var skipWhite = function()
{
var end = false;
while(!(end = eof()))
{
if(!isWhite(currentCharCode())) break;
nextChar();
}
return !end;
}
/**
*/
var nextNumber = function()
{
var buffer = currentChar();
while(!eof())
{
if(isNumber(nextCharCode()))
{
buffer += currentChar();
}
else
{
break;
}
}
return buffer;
}
/**
*/
var nextPath = function()
{
var buffer = currentChar();
while(!eof())
{
if(!isWhite(nextCharCode()) && !currentChar().match(/[\/=()]/g))
// if(isAlpha(nextCharCode()) || isNumber(currentCharCode()))
{
buffer += currentChar();
}
else
{
break;
}
}
return buffer;
}
/**
* end of file
*/
var eof = function()
{
return pos > source.length-2;
}
}
var ChannelParser = function()
{
var tokenizer = new Tokenizer(),
cache = {};
/**
* parses a string into a handleable expression
*/
this.parse = function(source)
{
if(!source) throw new Error('Source is not defined');
//stuff might have happened to the expression, so we need to clone it. it DEFINITELY changes
//when pull requests are made...
if(cache[source]) return Structr.copy(cache[source]);
tokenizer.source(source);
return Structr.copy(cache[source] = rootExpr());
}
var rootExpr = function()
{
var expr = tokenizer.current(),
type,
meta = {};
//type is not defined, but that's okay!
if(expr.type == Token.WORD && tokenizer.isWhite(tokenizer.peekChars(1).charCodeAt(0)) && tokenizer.position() < tokenizer.source().length-1)
{
type = expr.value;
tokenizer.next();
}
var token, channels = [];
while(token = tokenizer.current())
{
switch(token.type)
{
//-metadata=test
case Token.METADATA:
meta[token.value] = metadataValue();
break;
case Token.BACKSLASH:
case Token.WORD:
case Token.STAR:
channels = channels.concat(channelsExpr());
break;
case Token.OR:
tokenizer.next();
break;
default:
tokenizer.next();
break;
}
}
return { type: type, meta: meta, channels: channels };
}
var metadataValue = function()
{
if(tokenizer.currentChar() == '=')
{
tokenizer.next();
var v = tokenizer.next().value;
tokenizer.next();
return v;
}
tokenizer.next();
return 1;
}
var channelsExpr = function()
{
var channels = [],
to;
while(hasNext())
{
if(currentTypeIs(Token.LP))
{
tokenizer.next();
}
if(currentTypeIs(Token.WORD | Token.PARAM | Token.STAR | Token.BACKSLASH))
{
channels.push([channelPathsExpr()]);
while(currentTypeIs(Token.OR))
{
tokenizer.next();
channels[channels.length-1].push(channelPathsExpr());
}
}
else
{
break;
}
if(currentTypeIs(Token.RP))
{
tokenizer.next();
}
if(currentTypeIs(Token.TO))
{
tokenizer.next();
}
}
var _orChannels = splitChannelExpr(channels.concat(), []),
channelsThru = [];
for(var i = _orChannels.length; i--;)
{
var chain = Structr.copy(_orChannels[i]),
current = channel = chain[chain.length-1];
for(var j = chain.length-1; j--;)
{
current = current.thru = chain[j];
}
channelsThru.push(channel);
}
return channelsThru;
}
var splitChannelExpr = function(orChannels, stack)
{
if(!orChannels.length) return [stack];
var current = orChannels.shift();
if(current.length == 1)
{
stack.push(current[0]);
return splitChannelExpr(orChannels, stack);
}
else
{
var split = [];
for(var i = current.length; i--;)
{
var stack2 = stack.concat();
stack2.push(current[i]);
split = split.concat(splitChannelExpr(orChannels.concat(), stack2));
}
return split;
}
}
var channelPathsExpr = function(type)
{
var paths = [],
token,
isMiddleware = false,
cont = true;
while(cont && (token = tokenizer.current()))
{
switch(token.type)
{
case Token.WORD:
case Token.PARAM:
case Token.PATH:
paths.push({ name: token.value, param: token.type == Token.PARAM });
break;
case Token.BACKSLASH:
break;
default:
cont = false;
break;
}
if(cont) tokenizer.next();
}
if(currentTypeIs(Token.STAR))
{
isMiddleware = true;
tokenizer.next();
}
return { paths: paths, isMiddleware: isMiddleware };
}
var currentToken = function(type, igError)
{
return checkToken(tokenizer.current(), type, igError);
}
var nextToken = function(type, igError, keepWhite)
{
return checkToken(tokenizer.next(keepWhite), type, igError);
}
var checkToken = function(token, type, igError)
{
if(!token || !(type & token.type))
{
if(!igError) throw new Error('Unexpected token "'+(token || {}).value+'" at position '+tokenizer.position()+' in '+tokenizer.source());
return null;
}
return token;
}
var currentTypeIs = function(type)
{
var current = tokenizer.current();
return current && !!(type & current.type);
}
var hasNext = function()
{
return !!tokenizer.current();
}
}
exports.parse = new ChannelParser().parse;
-310
View File
@@ -1,310 +0,0 @@
var Structr = require('structr'),
Parser = require('./parser'),
utils = require('./utils');
var Request = Structr({
/**
*/
'__construct': function(listener, batch)
{
//data necessary for the request
this.data = batch.data;
//inner data which is invisible to the request, but contains data which needs to get passed along
this.inner = batch.inner;
//the end callback
this.callback = batch.callback;
this._used = {};
this._queue = [];
//yes, and I know what you're thinking: why the hell are you copying data?
//Well, we work backwards, and we don't know if parameters might override data passed to the current URI - we need to be prepared for that. SO
//as we're working our way back up, data will be set, and the original data will be mapped the way it should be for the given URI
this._add(listener, Structr.copy(this.data, true), batch.paths);
if(batch._next)
{
this.add(batch._next);
}
this.last = this._queue[0].target;
},
/**
*/
'init': function()
{
return this;
},
/**
*/
'hasNext': function()
{
return !!this._queue.length;
},
/**
*/
'next': function()
{
if(this._queue.length)
{
var thru = this._queue.pop(),
target = thru.target;
this.current = target;
if(target.paths)
{
var route = this.origin.getRoute({ channel: target });
this._addListeners(route.listeners, route.data, target.paths);
return this.next();
}
//heavier...
/*var route = this.expandRoute(this._queue.length-1);
if(!route) return this.next();
target = route.target,
thru = route.thru;
this._queue.pop();*/
this.current = target;
if(target.isMiddleware && this._used[target.id]) return this.next();
//keep tabs of what's used so there's no overlap. this will happen when we get back to the router
//for middleware specified in path -> to -> route
this._used[target.id] = thru;
this._prepare(target, thru.data, thru.hasParams, thru.paths);
return true;
}
return false;
},
/**
* expands all the routes (middleware even). See leche for example
*/
/*'expandThru': function()
{
var i = 0;
while(i < this._queue.length)
{
if(this.expandRoute(i))
{
i++;
}
else
{
i = 0;
}
}
},*/
/**
*/
/*'expandRoute': function(index)
{
var thru = this._queue[index],
target = thru.target;
if(target.paths)
{
this._queue.splice(index, 1);
var route = this.origin.getRoute({ channel: target }), n = this._queue.length;
this._addListeners(route.listeners, route.data, target.paths);
return false;
}
return { target: target, thru: thru };
},*/
/**
*/
'forward': function(channel, callback)
{
return this.origin.dispatch(Parser.parse(channel), this.data, { inner: this.inner, req: this.req }, callback);
},
/**
*/
'thru': function(channel, ops)
{
var self = this;
if(ops) Structr.copy(ops, this, true);
this._queue.push({ target: Parser.parse('-stream ' + channel).channels[0] });
this.next();
/*this.origin.dispatch(Parser.parse('-stream' + channel), this.data, Structr.copy({ inner: this.inner, req: this.req, _next: callback }, ops, true), function(request)
{
request.pipe(self);
})*/
},
/**
*/
'_addListeners': function(listeners, data, paths)
{
if(listeners instanceof Array)
{
for(var i = listeners.length; i--;)
{
this._add(listeners[i], data, paths);
}
return;
}
},
/**
* adds middleware to the END of the call stack
*/
'add': function(callback)
{
this._queue.unshift(this._func(callback));
},
/**
* adds middleware to the beginning of the call stack
*/
'unshift': function(callback)
{
this._queue.push(this._func(callback));
},
/**
*/
'_func': function(callback)
{
return { target: { callback: callback }, data: {} };
},
/**
*/
'_add': function(route, data, paths)
{
var current = route, _queue = this._queue,
hasParams = false;
if(!data) data = {};
while(current)
{
for(var i = paths.length; i--;)
{
var opath = paths[i],
cpath = route.path[i],
param,
value;
if(cpath.param && !opath.param)
{
param = cpath.name;
value = opath.name;
}
else
if(cpath.param && opath.param)
{
param = cpath.name;
value = this.data[opath.name];
}
else
{
continue;
}
hasParams = true;
this.data[param] = data[param] = value;
// if(!this.data[param]) this.data[param] = value;
}
//make sure not to use the same route twice. this will happen especially with middleware specified as /middleware/*
_queue.push({ target: current, data: data, hasParams: hasParams, paths: paths });
current = current.thru;
}
},
/**
*/
'_prepare': function(target, data, hasParams, paths)
{
//call once, then dispose
if(target.meta && target.meta.one)
{
target.dispose();
}
if(hasParams)
{
Structr.copy(data, this.data,true);
}
if(target.path) this.currentChannel = utils.pathToString(target.path, this.data);
this._callback(target, data);
},
/**
*/
'channelPath': function(index)
{
return utils.pathToString(this._queue[index].target.path || [], this.data);
},
/**
*/
'_callback': function(target, data)
{
return target.callback.call(this, this);
}
});
module.exports = Request;
-282
View File
@@ -1,282 +0,0 @@
var Structr = require('structr'),
Parser = require('./parser'),
utils = require('./utils'),
middleware = require('../middleware/meta'),
Request = require('./request'),
Collection = require('./collection');
/**
* Glorious.
*/
var Router = Structr({
/**
* Constructor. What else do you think it is?
*/
'__construct': function(ops)
{
if(!ops) ops = {};
this.RequestClass = ops.RequestClass || Request;
this._collection = new Collection(ops);
this._allowMultiple = !!ops.multi;
},
/**
* listens to the given expression for any change
*/
'on': function(expr, ops, callback)
{
if(!callback)
{
callback = ops;
ops = null;
}
for(var i = expr.channels.length; i--;)
{
var single = utils.channel(expr, i),
existingRoute = this.getRoute(single);
if(existingRoute.listeners.length && !this._allowMultiple && !this._middleware().allowMultiple(single))
{
var epath = existingRoute.listeners[0].path;
//use-case: server crashes, and reboots. Needs to override current registered path (which is trash)
if(existingRoute.listeners[0].meta.overridable)
{
existingRoute.listeners[0].dispose();
}
else
//if both are params, then there's a collission.
if(single.channel.paths[single.channel.paths.length-1].param == epath[epath.length-1].param)
{
throw new Error('Path "'+utils.pathToString(single.channel.paths)+'" already exists');
}
};
this._middleware().setRoute(channel);
}
return this._collection.add(expr, callback);
},
/**
*/
'_middleware': function()
{
return this.controller.metaMiddleware;
},
/**
*/
'hasRoute': function(channel, data)
{
return !!this.getRoute(channel, data).listeners.length;
},
/**
*/
'hasRoutes': function(expr, data)
{
for(var i = expr.channels.length; i--;)
{
if(this.hasRoute(utils.channel(expr, i), data)) return true;
}
return false;
},
/**
*/
'getRoute': function(single, data)
{
var route = this._collection.route(single.channel);
var r = this._middleware().getRoute({
expr: single,
router: this,
route: route,
data: data,
listeners: this._filterRoute(single, route)
});
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//this chunk is experimental. Moreso to test its usefulness over anything. The initial
//thought is enable the ability for routes with *different* metadata to share the same channel. This is great
//If say, you have web-workers which use the same channels, but different cluster IDs, so a master server knows
//what data to send to what web-worker. This COULD be specified in the URI structure, but that feels a bit messy. We'll see if this works first. It's nice and clean.
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if(!this._middleware().allowMultiple(route) && !this._allowMultiple && r.listeners.length)
{
r.listeners = [r.listeners[0]];
}
return r;
},
/**
*/
'dispatch': function(expr, data, ops, callback)
{
//only one for now. may change later on.
for(var i = expr.channels.length; i--;)
{
if(this._dispatch(utils.channel(expr, i), data, ops, callback)) return true;
}
return false;
},
/**
*/
'_dispatch': function(expr, data, ops, callback)
{
if(data instanceof Function)
{
callback = data;
data = undefined;
ops = undefined;
}
if(ops instanceof Function)
{
callback = ops;
ops = undefined;
}
if(!ops) ops = {};
if(!data) data = {};
channel = utils.pathToString(expr.channel.paths);
var inf = this.getRoute(expr, data);
//warnings are good incase this shouldn't happen
if(!inf.listeners.length)
{
if(!ops.ignoreWarning && !expr.meta.passive)
{
console.warn('The %s route "%s" does not exist', expr.type, channel);
}
//some callbacks are passive, meaning the dispatched request is *optional* ~ like a plugin
if(expr.meta.passive && callback)
{
callback(null, 'Route Exists');
}
return false;
}
var newOps = {
//router is set to controller because router() is used in loader. keeps things consistent, and using "this.controller.pull" is vague
router: this.controller,
//where the route lives
origin: this,
//data attached, duh.
data: inf.data,
//inner data
inner: ops.inner || {},
channel: channel,
paths: inf.expr.channel.paths,
//the metadata attached to the expression. Tells all about how it should be handled
meta: expr.meta,
//where is the dispatch coming from? Useful for hooks
from: ops.from || this.controller,
//the listeners to dispatch
listeners: inf.listeners,
//the final callback after everything's done ;)
callback: callback
};
Structr.copy(newOps, ops, true);
this._callListeners(ops);
return true;
},
/**
*/
'_callListeners': function(newOps)
{
for(var i = newOps.listeners.length; i--;)
{
Structr.copy(newOps, new this.RequestClass(newOps.listeners[i], newOps), true).init().next();
}
},
/**
* filters routes based on metadata
*/
'_filterRoute': function(expr, route)
{
if(!route) return [ ];
var listeners = (route.listeners || []).concat();
//Useful if there are groups of listeners with the same channel, but should not communicate
//with each other. E.g: two apps with slaves, sending queues to thyme. Thyme would need to know exactly where the slaves are
for(var name in expr.meta)
{
//the value of the metadata to search
var value = expr.meta[name];
//make sure that it's not *just* defined. This is important
//for metadata such as streams
if(value === 1) continue;
//loop through the listeners and start filtering
for(var i = listeners.length; i--;)
{
var listener = listeners[i];
if(listener.meta.unfilterable) break;
var metaV = listener.meta[name];
//if value == 1, then the tag just needs to exist
if(metaV != value && metaV != '*')
{
listeners.splice(i, 1);
}
}
}
return listeners;
}
});
module.exports = Router;
-115
View File
@@ -1,115 +0,0 @@
/**
* replaces params of the given expression
*/
exports.replaceParams = function(expr, params)
{
var path;
for(var i = expr.channel.paths.length; i--;)
{
path = expr.channel.paths[i];
if(path.param)
{
path.param = false;
path.name = params[path.name];
//no name? IT MUST EXIST. DELETE!
if(!path.name) expr.channel.paths.splice(i, 1);
}
}
return expr;
}
exports.channel = function(expr, index)
{
return { type: expr.type, channel: expr.channels[index], meta: expr.meta || {} };
}
exports.pathToString = function(path, data)
{
var paths = [];
if(!data) data = {};
for(var i = 0, n = path.length; i < n; i++)
{
var pt = path[i],
part;
if(pt.param && data[pt.name])
{
part = data[pt.name];
}
else
{
part = pt.param ? ':' + pt.name : pt.name;
}
paths.push(part);
}
return paths.join('/');
}
//old
/*exports.channelToStr = function(channel, omit)
{
var buffer = [];
if(!omit) omit = [];
if(expr.type && !omit.type) buffer.push(expr.type);
if(!omit.meta)
for(var key in channel.meta)
{
buffer.push('-'+key+'='+expr.meta[key]);
}
var current = expr.channel;
var middleware = [];
while(current)
{
var paths = [];
for(var i = 0, n = current.paths.length; i < n; i++)
{
var path = current.paths[i];
paths.push((path.param ? ':' : '')+ path.name);
}
middleware.unshift(paths.join('/'));
current = current.thru;
}
buffer.push(middleware.join(' -> '));
return buffer.join(' ');
}*/
exports.passThrusToArray = function(channel)
{
var cpt = channel.thru,
thru = [];
while(cpt)
{
thru.push(this._pathToString(cpt.paths));
cpt = cpt.thru;
}
return thru;
}
-200
View File
@@ -1,200 +0,0 @@
var Structr = require('structr'),
routeMiddleware = require('./middleware/route'),
metaMiddleware = require('./middleware/meta'),
Parser = require('./concrete/parser'),
Janitor = require('sk/core/garbage').Janitor,
utils = require('./concrete/utils');
var AbstractController = Structr({
/**
*/
'__construct': function(target)
{
this.metaMiddleware = metaMiddleware(this);
this.routeMiddleware = routeMiddleware(this);
this.metaMiddleware.init();
this.routeMiddleware.init();
this._channels = {};
},
/**
*/
'has': function(type, ops)
{
var expr = this._parse(type, ops);
return this._router(expr).hasRoutes(expr);
},
/**
*/
'getRoute': function(type, ops)
{
var expr = this._parse(type, ops);
return this._router(expr).getRoute(utils.channel(expr, 0));
},
/**
*/
'on': function(target)
{
var ja = new Janitor();
for(var type in target)
{
ja.addDisposable(this.on(type, {}, target[type]));
}
return ja;
},
/**
*/
'second on': function(type, callback)
{
return this.on(type, {}, callback);
},
/**
*/
'third on': function(type, ops, callback)
{
var expr = this._parse(type, ops),
router = this.routeMiddleware.router(expr);
for(var i = expr.channels.length; i--;)
{
var pathStr = utils.pathToString(expr.channels[i].paths);
if(!this._channels[pathStr])
{
this.addChannel(pathStr, Structr.copy(utils.channel(expr, i)));
}
}
return router.on(expr, ops, callback);
},
/**
*/
'channels': function()
{
return this._channels;
},
/**
*/
'addChannel': function(path, singleChannel)
{
for(var prop in singleChannel.meta)
{
singleChannel.meta[prop] = '*';
}
this._channels[path] = singleChannel;
},
/**
* flavor picker for operations. In the string, or in the ops ;)
*/
'_parse': function(type, ops)
{
var expr = typeof type != 'object' ? Parser.parse(type) : Structr.copy(type);
if(ops)
{
if(ops.meta) Structr.copy(ops.meta, expr.meta);
if(ops.type) expr.type = ops.type;
}
return expr;
},
/**
*/
'_router': function(expr)
{
return this.routeMiddleware.router(expr);
},
/**
*/
'_createTypeMethod': function(method)
{
var self = this;
var func = this[ method ] = function(channel, data, ops, callback)
{
if(!ops) ops = {};
ops.type = method;
var expr = this._parse(channel, ops);
return self._router(expr).dispatch(expr, data, ops, callback);
}
var router = self._router( { type: method });
Structr.copy(router, func, true);
}
});
var ConcreteController = AbstractController.extend({
/**
*/
'override __construct': function()
{
this._super();
var self = this;
//make channels data-bindable
this.on({
/**
*/
'pull channels': function()
{
return self.channels();
}
});
},
/**
*/
'override addChannel': function(path, singleChannel)
{
this._super(path, singleChannel);
//keep the same format as the channels so the end-point is handled exactly the same
var toPush = {};
toPush[path] = singleChannel;
this.push('channels', toPush, { ignoreWarning: true });
}
})
module.exports = ConcreteController;
-11
View File
@@ -1,11 +0,0 @@
var Loader = require('./loader');
//or a new, sandboxed router
exports.router = function()
{
return new Loader();
}
//singleton to boot
exports.router().copyTo(module.exports, true);
-76
View File
@@ -1,76 +0,0 @@
var Structr = require('structr'),
Controller = require('./controller');
try
{
require.paths.unshift(__dirname + '/beans');
}catch(e)
{
//break the web.
}
var Loader = Controller.extend({
/**
*/
'override __construct': function()
{
this._super();
this._params = {};
},
/**
*/
'params': function(params)
{
if(typeof params == 'string') return this._params[params];
Structr.copy(params || {}, this._params);
return this;
},
/**
*/
'require': function()
{
for(var i = arguments.length; i--;)
{
this._require(arguments[i]);
}
return this;
},
/**
*/
'_require': function(source)
{
if(source instanceof Array)
{
for(var i = source.length; i--;)
{
this._require(source[i]);
}
}
else
if(typeof src == 'object' && typeof src.bean == 'function')
{
source.plugin(this._controller, source.params || this._params[ source.name ] || {});
}
else
{
return false;
}
return true;
}
});
module.exports = Loader;
@@ -1,138 +0,0 @@
var Structr = require('structr');
module.exports = function()
{
var mw = new exports.Middleware();
mw.init = function()
{
//needs to be useable online = manual
mw.add(require('./rotate'));
}
return mw;
}
exports.Middleware = Structr({
/**
*/
'__construct': function()
{
//middleware specific to metadata
this._toMetadata = {};
//middleware which handles everything
this._universal = {};
},
/**
*/
'add': function(module)
{
var self = this;
if(module.all)
{
module.all.forEach(function(type)
{
if(!self._universal[type]) self._universal[type] = [];
self._universal[type].push(module);
});
}
module.meta.forEach(function(name)
{
self._toMetadata[name] = module;
});
},
/**
*/
'getRoute': function(ops)
{
//parse metadata TO, and FROM
var mw = this._getMW(ops.route ? ops.route.meta : {}, 'getRoute').concat(this._getMW(ops.expr.meta));
return this._eachMW(ops, mw, function(cur, ops)
{
return cur.getRoute(ops);
});
},
/**
*/
'setRoute': function(ops)
{
var mw = this._getMW(ops.meta, 'setRoute');
return this._eachMW(ops, mw, function(cur, ops)
{
return cur.setRoute(ops);
});
},
/**
*/
'allowMultiple': function(expr)
{
var mw = this._getMW(expr.meta);
for(var i = mw.length; i--;)
{
if(mw[i].allowMultiple) return true;
}
return false;
},
/**
*/
'_getMW': function(meta, uni)
{
var mw = (this._universal[uni] || []).concat();
for(var name in meta)
{
if(meta[name] == undefined) continue;
var handler = this._toMetadata[name];
if(handler && mw.indexOf(handler) == -1) mw.push(handler);
}
return mw;
},
/**
*/
'_eachMW': function(ops, mw, each)
{
var cops = ops,
newOps;
for(var i = mw.length; i--;)
{
if(newOps = each(mw[i], cops))
{
cops = newOps;
}
}
return cops;
}
});
@@ -1,39 +0,0 @@
exports.rotator = function(target, meta)
{
if(!target) target = {};
target.meta = [meta];
target.allowMultiple = true;
target.getRoute = function(ops)
{
var route = ops.route,
listeners = ops.listeners;
//if rotate is specified, then we need to rotate it (round-robin). There's a catch though...
//because the above *might* have filtered down to metadata values, we need to only rotate what's left, AND
//the rotate index must be stuck with the routes rotate metadata.
//Also, if the router can have multiple, then we cannot do round-robin. FUcKs ShiT Up.
if(!ops.router._allowMultiple && route && route.meta && route.meta[meta] != undefined && listeners.length)
{
route.meta[meta] = (++route.meta[meta]) % listeners.length;
//only ONE listener now..
ops.listeners = [listeners[route.meta[meta]]];
}
}
target.setRoute = function(ops)
{
}
}
exports.rotator(exports, 'rotate');
@@ -1,8 +0,0 @@
exports.meta = ['store'];
exports.getRoute = function(){};
exports.setRoute = function(ops)
{
};

Some files were not shown because too many files have changed in this diff Show More