Skip to content

Commit

Permalink
[modular] initial
Browse files Browse the repository at this point in the history
  • Loading branch information
indutny committed May 13, 2011
1 parent b485353 commit 84140a3
Show file tree
Hide file tree
Showing 18 changed files with 503 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1 @@
*.swp
3 changes: 3 additions & 0 deletions templates/modular/content/.gitignore
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
node_modules
*.sock
4 changes: 4 additions & 0 deletions templates/modular/content/.npmignore
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,4 @@
support
test
examples
*.sock
13 changes: 13 additions & 0 deletions templates/modular/content/config.json
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"db": {
"host": "localhost",
"port": 5984,
"auth": {
"username": "admin",
"password": "admin"
}
},
"http": {
"port": 8022
}
}
12 changes: 12 additions & 0 deletions templates/modular/content/config/db.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,12 @@
// This file will create database connection
// And call "fn" when connection is ready
module.exports = function(config, fn) {
var cradle = require('cradle'),
db = new (cradle.Connection)({
host: config.db.host || 'localhost',
port: config.db.port || 5984,
auth: config.db.auth
}).database(config.db.name);

fn(db);
};
106 changes: 106 additions & 0 deletions templates/modular/content/lib/helpers.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,106 @@
var fs = require('fs'),
path = require('path'),
Script = process.binding('evals').Script;

/**
* Check, whether dir is directory or not
*/
var isDirectory = exports.isDirectory = function(dir) {
var stat;
return path.existsSync(dir) && (stat = fs.statSync(dir)) &&
stat.isDirectory();
}

/**
* is obj function?
*/
exports.isFunction = function(obj) {
return typeof obj === 'function';
}

/**
* Copy own properties of b to a
*/
exports.extend = function(a, b) {
if (!b) return a;

for (var i in b) {
if (b.hasOwnProperty(i)) a[i] = b[i];
}

return a;
};

/**
* This function will be used to process files array
*/
exports.reduce = function(dir) {
dir = path.normalize(dir);

return function(accum, elem) {
var filepath = elem.substr(dir.length);

if (!filepath) return accum;

// Process only files
if (!isDirectory(elem)) {
accum.push({
extension: path.extname(filepath),
name: filepath.replace(/\.[^\.]+$/, ''),
filepath: elem
});
}

return accum;
}
};

/**
* Compile script in a new context
* Very common to nodejs module system
*/
exports.require = function(file, context) {
var content = fs.readFileSync(file.filepath);

if (file.extension !== '.js') {
return {exports: content};
}
try {
var fn = Script.runInNewContext('(function(require, __dirname, ' +
'__filename, module, exports) {\n' +
content +
'\n;})', context, file.filepath),
exports = {},
module = {exports: exports},
dirname = path.dirname(file.filepath);

fn(require, dirname, file.filepath, module, exports);
} catch (e) {
console.log('Failed to load: ' + file.filepath, e);
return {};
}
return module;
};

/**
* Helps storing modules in javascript object
* a.b.c = x => {a:{b:{c: x}}}
*/
exports.store = function(storage, key, val) {
var previous;
key.split(/\.|\//g).forEach(function(subkey) {
subkey = subkey.trim();

previous = {
storage: storage,
subkey: subkey
};
storage = storage[subkey] || (storage[subkey] = {});
});

if (!previous) return;
previous.storage[previous.subkey] = val;

return previous.storage;
};

131 changes: 131 additions & 0 deletions templates/modular/content/lib/loader.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,131 @@
var fs = require('fs'),
path = require('path'),
sys = require('sys'),
watch = require('watch'),
helpers = require('./helpers');


/**
* Loader - loads all js files from a directory root,
* compiles them and puts them into objects variable
*/
var loader = module.exports = function(dir, context, callback) {
if (!helpers.isDirectory(dir)) return;

// Add trailing slash
dir = dir.replace(/\/+$/, '') + '/';

// Context must be defined
context = context || {};

var objects = {},
weighted = [],
args = Array.prototype.slice.call(arguments, 1);

addInvokeMethods(context, weighted);

watch.walk(dir, function(err, files_obj) {
if (err) {
console.log(err);
return;
}

var files = [];

for (var file in files_obj) {
if (!files_obj.hasOwnProperty(file)) continue;

files.push(file);
}

// Replace kind of wildcard '?' with objects itself,
// So objects can be passed to context on demand
for (var i in context) if (context[i] === '?') context[i] = objects;

files.reduce(helpers.reduce(dir), []).forEach(function(file) {
var result = helpers.require(file, context);

// If module is disabled - do not load it
if (!result.exports || result.exports.enabled === false) return;

// Add to modules object
var object = result.exports;

// module.folder will points to object in "modules" global variable
// which holds this module
//
// so if we have module with path: /my_app/some_module/abc.js
// module.folder will be equal to modules.my_app.some_module
// inside this module's context
result.folder = helpers.store(objects, file.name, object);

// Add to weighted collection
weighted.push({
weight: parseInt(object.weight) || 0,
name: file.name,
value: object
});
});

// Sort objects in collection by weight
weighted.sort(function(a, b) {
return a.weight > b.weight;
});

// All objects are in place
// Invoke init
process.nextTick(function() {
context.invoke('init');
});

callback && callback(null, context.invoke.emitter);
});
}

/**
* Adds invoke methods to execution context
*
* Provides cool syntax
* invoke('init', arg1, arg2, arg3).array
*/
function addInvokeMethods(context, weighted) {
var len;

context.invoke = function(callbackName) {
var args = Array.prototype.slice.call(arguments, 1);

// Calculate length only once
len = len || weighted.length;

var array = [], object = {};

for (var i = 0; i < len; i++) {
var elem = weighted[i],
callback = elem.value[callbackName];

if (!elem.value.hasOwnProperty(callbackName) ||
!helpers.isFunction(callback)) continue;

try {
var result = callback.apply(elem, args);
array.push(result);
helpers.store(object, elem.name, result);
} catch (e) {
sys.puts(e);
}
}

// Invoke EventEmitter (for debug purposes)
context.invoke.emitter.emit.apply(
context.invoke.emitter,
arguments
);

return {
object: object,
array: array
};
};

context.invoke.emitter = new process.EventEmitter;
}
38 changes: 38 additions & 0 deletions templates/modular/content/modules/express/server.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Express.js: HTTP Server
*
* Adds .middleware(express) and .routes(app) callbacks
*/

/**
* .init() callback
*/
exports.init = function() {
var express = require('express'),
middleware = invoke('middleware', express).array;

middleware.unshift(express.errorHandler());

var server = express.createServer.apply(express, middleware);

// Decode body and cookies
server.use(express.bodyParser());
server.use(express.cookieParser());

// Configure views
server.set('views', rootdir + '/views');
server.set('view engine', 'ejs');

// Apply routes
invoke('routes', server);

server.listen(config.http.port, function() {
console.log('Server has started listening on port ' + config.http.port);
invoke('http-listening');
});
}

/**
* Module's weight
*/
exports.weight = -1E6;
14 changes: 14 additions & 0 deletions templates/modular/content/modules/express/static.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Express.js : Static provider
*
* Servers static content
*/

/**
* Export static content middleware
*/
exports.middleware = function(express) {
return express.static(rootdir + '/static/');
}

exports.weight = 6;
39 changes: 39 additions & 0 deletions templates/modular/content/modules/utils.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Adds middleware, that adds methods:
* # json
*/

var Buffer = require('buffer').Buffer;

/**
* Exports middleware
*/
exports.middleware = function adds_json_method(express) {
return function(req, res, next) {
var jsonp = req.query && req.query.callback;

res.json = function(data, code) {
data = jsonp ? jsonp + '(' + JSON.stringify(data) + ')' :
JSON.stringify(data);

var headers = {
'Content-Type': jsonp ? 'text/javascript' : 'application/json',
'Content-Length': Buffer.byteLength(data)
};

if (res.header('Set-Cookie')) {
headers['Set-Cookie'] = res.header('Set-Cookie');
}

res.writeHead(code || 200, headers);
res.end(data);
};

next();
}
}

/**
* Try to be first
*/
exports.weight = -1E9;
18 changes: 18 additions & 0 deletions templates/modular/content/modules/your_site/pages.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* This should be your site's pages module
*/

exports.routes = function(app) {
function index_page(req, res) {
res.render('pages/index', {
locals: {
title: 'My node.modular site',
req: req
}
});
}

app.get('/', index_page);
app.get('/index', index_page);

};
Loading

0 comments on commit 84140a3

Please sign in to comment.